以下文本来自jls
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
即使这样,仍然存在许多并发症。如果在字段声明中将final字段初始化为编译时常量表达式(第15.28节),则可能不会观察到对final字段的更改,因为在编译时会用常量表达式的值替换对final字段的使用。
有人可以给我更好的解释。我无法理解 “可能无法观察到对最终字段的更改” 的说法。可以借助示例。
我无法理解可能无法观察到对最终字段的更改
它表明,如果将最终变量声明为编译时间常数,则在执行过程中,程序中进一步使用 反射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
}
}
上面代码的输出是:
20
20
为什么?
答案在于javap -c
命令为公共静态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
}
在第16行(在changeFinal
调用方法之前),将的值cf.x
硬编码为20
。在第33行(changeFinal
调用方法之后)的值cf.x
再次被硬编码为20
。因此,尽管最终变量值的更改x
是通过reflection API
在执行期间成功完成的 ,但是由于x
是编译时常量,因此它显示了其常量值20
。
问题内容: Java语言文档说: 如果将原始类型或字符串定义为常量,并且在编译时知道该值,则编译器会使用其值替换代码中各处的常量名称。这称为编译时常量。 我的理解是,如果我们有一段代码: 然后,编译器会将x代码中每次出现的内容替换为literal 10。 但假设常量在运行时初始化: 与编译时常量相比,性能是否会下降(无论可以忽略不计)? 另一个问题是下面的代码行: 被编译器以与编译时常量相同的方式
问题内容: 最近,我发现匿名类和lambda表达式之间有细微的区别: 通常,lambda与匿名类等效。甚至我的Eclipse IDE都具有重构功能,可以将转换为lambda(变得完全像)并转换为匿名类(变得完全像)。但是lambda给了我一个编译错误,而匿名类却可以完美地编译。错误消息如下所示: 所以问题是:为什么会有这种差异? 问题答案: 这与处理前向引用的JLS#8.3.3有关。特别是,如果使
问题内容: 最终变量和编译时间常数之间有什么区别? 考虑以下代码 这是什么意思?何时以及如何为最终变量分配值?在运行时会发生什么,在编译时会发生什么?为什么要给switch一个编译时间常数?Java还有哪些其他结构需要编译时间常数? 问题答案: 问题在于, 所有语句在编译时 都 必须是最终的 。您的第一个陈述 是最终决定 。对于100%,除以外没有其他值。 然而,这 不能保证 对。如果周围有一个i
问题内容: 我正在尝试将java8 forEach循环内的布尔变量更改为true,这是非最终的。但是我遇到了以下错误:在封闭范围内定义的必需局部变量必须是final或有效的final。 如何解决这个错误? 代码: 这是我在函数中创建的变量。 现在,当我尝试更改它时: 我收到错误消息:封闭范围中定义的必需局部变量必须是final或有效的final。 为什么会出现此错误,以及如何解决? 问题答案: 您
配置选项 要做到最大限度的定制每一个软件包,获取完整的配置选项是必须的。当然,要想更加详细、全面的了解如何自定义安装,还需要查看 README INSTALL FAQ 之类的文档,甚至是软件包的官方手册。需要注意的是,有不少软件包的配置选项分布在多个 configure 脚本中,还有少数并不是通过 configure 脚本进行配置的,查看完整的配置信息就变成一件很吃力的事情了。因此唯一的建议就是:
问题内容: 让我们从一个简单的测试用例开始: 任何人都在乎猜测什么将作为输出打印(在底部显示,以免立即破坏惊喜)。 问题是: 为什么原始和包装的整数表现不同? 为什么反射访问与直接访问返回不同的结果? 最困扰我的人-为什么String表现得像原始的而不是像? 结果(java 1.5): 问题答案: 内联编译时常量(在javac编译时)。参见JLS,尤其是15.28定义了常量表达式,而13.4.9讨