Java final字段编译时常量表达式

ver*_*tas 5 java final jls

以下文字来自jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3

Even then, there are a number of complications. If a final field is 
initialized to a compile-time constant expression (§15.28) in the field 
declaration, changes to the final field may not be observed, since uses of that 
final field are replaced at compile time with the value of the constant 
expression.
Run Code Online (Sandbox Code Playgroud)

任何人都可以给我更好的解释.我无法理解这一说法changes to the final field may not be observed.可以借助例子.

提前致谢

Vis*_*l K 10

我无法理解声明对最终字段的更改可能没有被观察到

它告诉我们,如果最终变量被声明为编译时常量,那么在程序中使用反射API在最终变量中进行的任何更改都将在执行期间对程序不可见.
例如,考虑下面给出的代码:

import java.lang.reflect.*;
class ChangeFinal 
{
    private final int x = 20;//compile time constant
    public static void change(ChangeFinal cf)
    {
        try
        {
            Class clazz = ChangeFinal.class;
            Field field = clazz.getDeclaredField("x");
            field.setAccessible(true);
            field.set(cf , 190);//changed x to 190 for object cf
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) 
    {
        ChangeFinal cf = new ChangeFinal();
        System.out.println(cf.x);//prints 20
        change(cf);
        System.out.println(cf.x);//prints 20
    }
}
Run Code Online (Sandbox Code Playgroud)

上面代码的输出是:

20
20
Run Code Online (Sandbox Code Playgroud)

为什么?
答案在于javap -cpublic static void main命令提供的输出:

public static void main(java.lang.String[]);
  Code:
   0:   new     #3; //class ChangeFinal
   3:   dup
   4:   invokespecial   #11; //Method "<init>":()V
   7:   astore_1
   8:   getstatic       #12; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  aload_1
   12:  invokevirtual   #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
   15:  pop
   16:  bipush  20
   18:  invokevirtual   #14; //Method java/io/PrintStream.println:(I)V
   21:  aload_1
   22:  invokestatic    #15; //Method change:(LChangeFinal;)V
   25:  getstatic       #12; //Field java/lang/System.out:Ljava/io/PrintStream;
   28:  aload_1
   29:  invokevirtual   #13; //Method java/lang/Object.getClass:()Ljava/lang/Cla
ss;
   32:  pop
   33:  bipush  20
   35:  invokevirtual   #14; //Method java/io/PrintStream.println:(I)V
   38:  return

}
Run Code Online (Sandbox Code Playgroud)

在第16行(在changeFinal调用方法之前),将值cf.x硬编码为20.在第33行(changeFinal调用方法之后),值cf.x再次被硬编码20.因此,虽然最终变量值的变化在执行期间x成功完成 reflection API,但由于x是编译时常量,因此它显示其常量值20.