最终导致静态不知何故
我有这样的代码:最终导致静态不知何故
public class App {
private final String some;
public App(){
some = "old";
}
public static void main(String... args) throws NoSuchFieldException, IllegalAccessException {
App a = new App();
a.magic();
System.out.println(a.some);
}
private void magic() throws NoSuchFieldException, IllegalAccessException {
Field field = this.getClass().getDeclaredField("some");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, "new");
String someDuplicate = (String) field.get(this);
System.out.println(someDuplicate);
}
}
从这个输出将是
new
new
,但如果我会改的变量初始化到这一点:
private final String some = "old";
输出将为
new
old
好像直列初始化导致最终的非静态字段
我could'n发现任何船坞参照这种行为的静态样的行为,可能会出现这种一些合乎逻辑的解释。
顺便说这种方式来初始化场引起的行为就像在构造函数初始化情况:
{
some = "old";
}
的javac
不恒定的内联。当你有一个代码,如
class A {
final String text = "Hello";
public static void main(String... args) {
System.out.println(new A().text);
}
}
的javac
可以内联常数,因为它是在编译时已知。这使得更改底层字段对已被内联的地方没有影响。
通过将值移到构造函数中,它在编译时就不再知道了。
倾销字节代码为main
方法,你可以看到它不会读取场,而是LDC
负荷不断"Hello"
public static varargs main([Ljava/lang/String;)V
L0
LINENUMBER 5 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
NEW A
DUP
INVOKESPECIAL A.<init>()V
INVOKEVIRTUAL java/lang/Object.getClass()Ljava/lang/Class;
POP
LDC "Hello"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 6 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
我觉得有趣的是,它还是创建A
并检查它因为null使用.getClass()
所以它的优化只能走到目前为止。
顺便说一句,你可以在不使用构造函数/初始化块和包装方法的情况下解决这个问题。
class A {
final String text = dynamic("Hello");
// or final String text = String.valueOf("Hello");
public static void main(String... args) {
System.out.println(new A().text);
}
static <T> T dynamic(T t) {
return t;
}
}
或它在编译时无法确定的任何表达式。
我可以为此工作两次upvote吗?即使它看起来如此简单,我从来没有想过它......好戏! – AxelH
@AxelH'javac'可以确定'1 + 2'是'3'。 ;)它也可以确定''Hel“+”lo“'是'”Hello“' –
似乎是关于javac分析代码的能力,在两种情况下(inline init或constructor \ initializer block) final变量,我们以后不能改变它,所以它在所有情况下都可以看起来像字段“LDC”的值,但在这个例子中,我可以看到它只在内联init的情况下才会做这些事情,在其他情况下我可以通过反思“打破规则” –
顺便说一句,通过反射使字段非最终没有做任何事情。 –
@PeterLawrey它使我能够更改最终字段值,如果它被初始化不内联。问题是关于为什么不同的初始化导致不同的行为 –
这就是为什么它是一个评论,而不是一个答案,我可以改变最终字段而不做这个伎俩。 –