我想设计一个类来逐步展示不变性。
以下是一个简单的课程
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修改字段的修饰符。那么代码如何仍能改变最终字段?
答案 0 :(得分:10)
这是预期的行为 - 这只是你不应该做的事情。
来自Field.set
的文档:
如果底层字段是final,则该方法抛出IllegalAccessException,除非此Field对象的setAccessible(true)成功并且该字段是非静态的。以这种方式设置最终字段仅在反序列化或重建具有空白最终字段的类的实例期间才有意义,然后才能使程序的其他部分访问它们。在任何其他上下文中使用可能会产生不可预测的影响,包括程序的其他部分继续使用此字段的原始值的情况。
现在,如果你想要证明这种违反各种安全性的行为,你需要问问自己为什么你有一些你不信任的代码能够在有足够权限的情况下运行field.setAccessible(true)
。基本上,安全管理员会给予保护 - 但是如果你在没有安全管理器的情况下运行会阻止你这样做,你可以自己射击。
答案 1 :(得分:2)
您可以使用SecurityManager保护setAccessible。您可以在命令行中指定它,也可以动态创建它,就像它们here一样。
但即使你保护自己免受反射,你的代码仍然不会受到像ASM这样的字节码修改库的保护。
所以你不应该依赖你的领域是最终的。
答案 2 :(得分:1)
不,你可以这样做。如果您想限制某些操作,我建议您阅读The Security Manager和Java SE 7 Security Documentation。