一个关于反射改变final域的问题

一个关于反射改变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不可变的前提下是可以理解的。