java - final 以某种方式导致静态行为

标签 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);
    }
}

输出结果为

new 
new

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

private final String some = "old";

输出将是

new
old

似乎内联初始化导致最终非静态字段的类静态行为

我找不到任何关于此行为的停靠引用,可能对此有一些合乎逻辑的解释。

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

{
    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 并使用 .getClass() 检查它是否为 null,因此它的优化仅到此为止。

顺便说一句,您可以在不使用带有包装方法的构造函数/初始化 block 的情况下解决这个问题。

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;
    }
}

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

关于java - final 以某种方式导致静态行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45673629/

相关文章:

java - 迭代参数数组 - Java 反射

java - JMH 基准中的现场访问成本和不断折叠

c# - 有没有办法通过反射或其他方式获取实体 ID 字段的名称?

java - 从非静态 Java (ActionListener) 获取静态

java - Reflections - 按照@Order注解的顺序获取包类

java - 如何在java中重命名文件夹

java - 使用反射方法设置EditText光标颜色

scala - 如何在运行时访问案例类字段上定义的注释

Android 使用现有 View 对象扩充 View

c++ - 如何在C++类中调用静态库函数?