Chs*_*y76 52 java reflection final
让我们从一个简单的测试用例开始:
import java.lang.reflect.Field;
public class Test {
private final int primitiveInt = 42;
private final Integer wrappedInt = 42;
private final String stringValue = "42";
public int getPrimitiveInt() { return this.primitiveInt; }
public int getWrappedInt() { return this.wrappedInt; }
public String getStringValue() { return this.stringValue; }
public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {
Field field = Test.class.getDeclaredField(name);
field.setAccessible(true);
field.set(this, value);
System.out.println("reflection: " + name + " = " + field.get(this));
}
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
Test test = new Test();
test.changeField("primitiveInt", 84);
System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());
test.changeField("wrappedInt", 84);
System.out.println("direct: wrappedInt = " + test.getWrappedInt());
test.changeField("stringValue", "84");
System.out.println("direct: stringValue = " + test.getStringValue());
}
}
Run Code Online (Sandbox Code Playgroud)
任何人都在乎猜测将作为输出打印什么(显示在底部,以免立即破坏惊喜).
问题是:
int而不像Integer?结果(java 1.5):
reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
Run Code Online (Sandbox Code Playgroud)
Tom*_*ine 21
编译时常量是内联的(在javac编译时).参见JLS,特别是15.28定义了一个常量表达式,13.4.9讨论了二进制兼容性或最终字段和常量.
如果将字段设为非final或分配非编译时间常量,则不会内联该值.例如:
private final String stringValue = null!= null?"":"42";
Gan*_*fIX 10
在我看来,情况更糟:一位同事指出了以下有趣的事情:
@Test public void testInteger() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field value = Integer.class.getDeclaredField("value");
value.setAccessible(true);
Integer manipulatedInt = Integer.valueOf(7);
value.setInt(manipulatedInt, 666);
Integer testInt = Integer.valueOf(7);
System.out.println(testInt.toString());
}
Run Code Online (Sandbox Code Playgroud)
通过这样做,您可以更改正在运行的整个JVM的行为.(当然,您只能更改-127到127之间的值的值)
Reflection的set(..)方法适用于FieldAccessors.
对于int它得到的UnsafeQualifiedIntegerFieldAccessorImpl,其超类定义的readOnly属性为True,只有当字段都 static和final
所以首先要回答未提出的问题 - 这就是为什么它final会毫无例外地改变的原因.
所有子类都UnsafeQualifiedFieldAccessor使用sun.misc.Unsafe该类来获取值.这些方法都有native,但它们的名字是getVolatileInt(..)和getInt(..)(getVolatileObject(..)和getObject(..)分别).上述访问者使用"易失性"版本.如果我们添加非易失性版本会发生以下情况:
System.out.println("reflection: non-volatile primitiveInt = "
unsafe.getInt(test, (long) unsafe.fieldOffset(getField("primitiveInt"))));
Run Code Online (Sandbox Code Playgroud)
(其中unsafe由反射实例化-这是不允许的其他方式)(我打电话getObject为Integer和String)
这给出了一些有趣的结果:
reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: non-volatile primitiveInt = 84
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: non-volatile wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
reflection: non-volatile stringValue = 84
Run Code Online (Sandbox Code Playgroud)
在这一点上,我回忆起javaspecialists.eu上的一篇文章,讨论相关问题.它引用了JSR-133:
如果在字段声明中将final字段初始化为编译时常量,则可能无法观察到对final字段的更改,因为在编译时将该final字段的使用替换为编译时常量.
第9章讨论了在这个问题中观察到的细节.
事实证明,这种行为并不是意料之外的,因为final字段的修改应该只在对象初始化之后才会发生.
| 归档时间: |
|
| 查看次数: |
10920 次 |
| 最近记录: |