6

我想设计一个类来逐步展示不变性。

以下是一个简单的类

public class ImmutableWithoutMutator {
    private final String firstName;
    private final String lastName;
    private final int age;

    public ImmutableWithoutMutator(final String firstName, final String lastName, final int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    @Override
    public String toString() {
        return String.format(
                "ImmutableWithoutMutator [age=%s, firstName=%s, lastName=%s]",
                age, firstName, lastName);
    }
}

现在我仍然可以通过使用以下代码来使用反射。

import java.lang.reflect.Field;

public class BreakImmutableUsingReflection {
    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        ImmutableWithoutMutator immMutator = new ImmutableWithoutMutator(
                "firstName", "lastName", 2400);
        System.out.println(immMutator);

        // now lets try changing value using reflection
        Field f = ImmutableWithoutMutator.class.getDeclaredField("age");
        f.setAccessible(true);
        f.set(immMutator, 2000);

        System.out.println(immMutator);
    }
}

我的问题是,我没有使用反射 API 修改字段的修饰符。那么代码如何仍然能够改变最终字段?

4

3 回答 3

10

这是预期的行为——这只是你不应该做的事情。

从文档中Field.set

如果基础字段是最终字段,则该方法将引发 IllegalAccessException,除非此 Field 对象的 setAccessible(true) 已成功并且该字段是非静态的。以这种方式设置最终字段仅在反序列化或重建具有空白最终字段的类实例期间才有意义,然后它们才可供程序的其他部分访问。在任何其他上下文中使用可能会产生不可预知的影响,包括程序的其他部分继续使用该字段的原始值的情况。

现在,如果您想辩称这违反了所有类型的安全性,您需要问自己为什么您不相信有足够的权限运行的代码可以正常运行 call field.setAccessible(true)。基本上,保护是由安全经理提供的——但如果你在没有安全经理的情况下跑步,这会阻止你这样做,你可能会在脚下开枪。

于 2013-04-12T05:59:59.633 回答
2

您可以使用SecurityManager防止 setAccessible 。您可以在命令行上指定它或动态创建它,就像他们在这里做的那样。

但是,即使您保护了反射,您的代码仍然不会受到 ASM 等字节码修改库的保护。

所以你不应该依赖你的领域是最终的。

于 2013-04-12T06:27:18.903 回答
1

不,你可以这样做。如果您想限制某些操作,我建议您阅读The Security ManagerJava SE 7 安全文档。

于 2013-04-12T06:07:41.947 回答