【发布时间】:2014-05-27 16:36:33
【问题描述】:
必须是 private + static + final 才能作为编译时常量内联吗?
什么可以作为运行时常量内联?
还有呢:
private final ...
static final ...
final ...
【问题讨论】:
标签: java access-modifiers
必须是 private + static + final 才能作为编译时常量内联吗?
什么可以作为运行时常量内联?
还有呢:
private final ...
static final ...
final ...
【问题讨论】:
标签: java access-modifiers
JLS 4.12.4 指定什么是“常量变量”。基本上要求是必须是final,类型是原始类型或者String,值必须是常量表达式(15.28)。
常量变量的值将被字节码编译器内联。
JLS 规则不区分 static 与否,也不区分不同的访问修饰符。
但是public static final可以通过反射来改变,那么它是compile-time final吗?
是的。事实上,对常量变量的反射性更改没有任何影响:
“即便如此,仍有许多复杂情况。如果在字段声明中将 final 字段初始化为常量表达式(第 15.28 节),则可能不会观察到对 final 字段的更改,因为使用它final 字段在编译时被常量表达式的值替换。" - JLS 17.5.3
什么可以作为运行时常量内联?
有趣的问题。我没有看到 JLS 中直接提到过这个问题。但是,显然可以对此进行反思(鉴于 JLS 17.5.3 所说的)。
允许内联的一种情况(如果 JIT 编译器可以处理它)是一个局部变量,它要么是最终的,要么是有效的最终变量,并且具有 JIT 编译器可以推导出的初始值设定项始终相同。总体约束是运行时内联不能改变程序的可观察行为。 (这里我们不需要考虑反射,因为局部变量不能通过反射访问或改变。)
【讨论】:
访问修饰符对内联没有影响。
重要的是该值来自一个常量表达式 (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28) 并且它是合适的类型(原始或字符串)。
虽然可以通过多种方式更改最终赋值(最简单的方法是使用新值重新编译包含类),但如果遵守上述限制,它们仍然被视为常量。所以即使是 public final static 也可以(并且经常是)编译时内联。
【讨论】:
根据您的需要,常量可以是私有的或公共的,但您应该使用 static + final。
所以...
private static final
或者……
public static final
【讨论】:
任何标记为 static final 的字段都不能在运行时更改*,即使使用反射也是如此,并且将被内联。
*需要注意的是,我相信您可以通过 unsafe 操作来做到这一点。
【讨论】: