最后以某种方式导致静态行为

Vla*_*rko 1 java reflection static final

我有这样的代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个输出将是

new 
new
Run Code Online (Sandbox Code Playgroud)

但如果我将变量初始化更改为:

private final String some = "old";
Run Code Online (Sandbox Code Playgroud)

输出将是

new
old
Run Code Online (Sandbox Code Playgroud)

看起来像内联初始化会导致最终非静态字段的静态行为

我找不到这种行为的任何码头参考,可能有一些合理的解释.

顺便说一句,init方式的这种方式会导致类似于构造函数初始化的行为:

{
    some = "old";
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*rey 5

javac不恒定的内联.当你有一个代码,如

class A {
    final String text = "Hello";

    public static void main(String... args) {
        System.out.println(new A().text);
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我觉得有趣的是它仍然创建A并使用它来检查null,.getClass()所以它的优化只到目前为止.

BTW您可以在不使用带有包装方法的构造函数/初始化块的情况下解决此问题.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

或者在编译时无法确定的任何表达式.