一个关于反射改变final域的问题
问题描述:
代码如下
import java.lang.reflect.Field;
class A {
private final int a = 1;
public void a() {
System.out.println(a);
}
}
class B {
private final int b;
public B(int b) {
this.b = b;
}
public void b() {
System.out.println(b);
}
}
public class Test {
public static void main(String args[]) throws Exception {
A a = new A();
B b = new B(2);
Field field_a = A.class.getDeclaredField("a");
Field field_b = B.class.getDeclaredField("b");
field_a.setAccessible(true);
field_b.setAccessible(true);
field_a.set(a, 520);
field_b.set(b, 250);
System.out.println(field_a.get(a));
a.a();
System.out.println(field_b.get(b));
b.b();
}
}
输出结果:
520
1
250
250
问题来了:为什么两种不一样的初始化final域的方式会造成两种截然不同的效果??
答
2
原因在于编译期间final类型的数据自动被优化了,即:所有用到该变量的地方都呗替换成了常量。所以 get方法在编译后自动优化成了return 1 ; 而不是 return this.a;
要想不被自动优化,可以把初始化代码改成:private final int a=(null!=null?0:1);
答
我又回来自己解答了,来看javap -c A 和 B得到的结果:
class A {
A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: return
public void a();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
7: return
}
class B {
public B(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field b:I
9: return
public void b();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field b:I
7: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
10: return
}
从 3: iconst_1
可以看出 方法a是直接将1作为需求的值。
而b是重新去获取field b的值 3: aload_0 4: getfield #2 // Field b:I
可见编译器的确做了优化,这个优化在final不可变的前提下是可以理解的。