【问题标题】:java static initialization with inheritance带有继承的java静态初始化
【发布时间】:2012-11-08 15:16:25
【问题描述】:
public class Main {

    public static void main(String[] args) {
        System.out.println(B.x);
    }

}
class A {
    public static String x = "x";
}
class B extends A {
    static {
        System.out.print("Inside B.");
    }
}

问题:为什么输出会是:x。但不是:Inside B.x

【问题讨论】:

    标签: java inheritance static static-initialization


    【解决方案1】:

    B.x 的引用发出以下字节码:

    getstatic       #3   <Field int B.x>
    

    根据Java Virtual Machine Spec

    Java 虚拟机指令 anewarray、checkcast、getfield、 getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、 invokestatic,invokevirtual,ldc,ldc_w,multianewarray,新, putfield 和 putstatic 对运行时进行符号引用 常量池。执行这些指令中的任何一个都需要 其符号引用的分辨率

    因此 JVM 应该解析 B.x 的符号引用。字段分辨率为specified like this

    将 D 中未解析的符号引用解析为 类或接口 C,由字段给出的对 C 的符号引用 必须首先解析引用 (§5.4.3.1)。

    ...

    解析字段引用时,字段解析首先尝试 在 C 及其超类中查找引用的字段:

    如果 C 声明一个字段,其名称和描述符由 字段引用,字段查找成功。声明的字段是 字段查找的结果。

    否则,字段查找将递归地应用于直接 指定类或接口 C 的超接口。

    否则,如果 C 有超类 S,则应用字段查找 递归到 S。

    否则,字段查找失败。

    换句话说,JVM 会将B.x 解析为A.x。这就是为什么只需要加载A 类的原因。

    【讨论】:

    • 好的,但是它的行为是在哪里描述的,我的意思是在规范中?
    • 这不是真的。例如,可以在B中添加静态字段x,即使不重新编译Main也会打印出来。
    • @ShyJ:我不喜欢你的第一个答案,但这绝对是现在最好的答案。 +1
    【解决方案2】:

    因为B.x 实际上是A.x,所以只需要加载A 类。

    【讨论】:

      【解决方案3】:

      §12.4 "Initialization of Classes and Interfaces" of The Java Language Specification, Java SE 7 Edition 指定:

      类的

      初始化包括执行其static 初始化程序和类中声明的静态字段(类变量)的初始化程序。

      […]

      static 字段 (§8.3.1.1) 的引用只会导致实际声明它的类或接口的初始化,即使它可能通过子类、子接口或实现一个接口。

      因此,尽管 - 与上述某些答案中的声明相反 - 类 B 确实必须加载,以便确定 B.xA 中声明,类 @ 987654328@ 未初始化(即,它的 static 初始化程序实际上并未运行),直到您对 B 执行更具体的操作。

      【讨论】:

        【解决方案4】:

        它实际上不需要加载B,直到它直接访问B 的静态成员。注意这段代码:

        public class TestMain {
            public static void main(String[] args) {
                System.out.println(B.x);
                System.out.println(B.y);
            }
        
            static class A {
                public static String x = "x";
            }
        
            static class B extends A {
                public static String y = "y";
                static {
                    System.out.print("Inside B.");
                }
            }
        }
        

        将输出:

        x
        Inside B.y
        

        因为在访问B 中的某些内容之前,它不需要加载B

        Here's a good link on the subject. 来自文章,“别忘了,这段代码会在 JVM 加载类时执行。JVM 将所有这些块组合成一个静态块然后执行。这里有几点我想提一下:“

        【讨论】:

        • 所以这看起来像是编译器优化,但编译器可以不同。它在某处有描述吗?
        • @user1839039:我为你添加了一个链接
        【解决方案5】:

        Class B 扩展了 A,它有一个 public static variable x,当您调用 B.x 时您正在访问它

        如果您期望Inside B. 输出,您必须创建该类的对象。执行所有静态代码块。或将该静态代码块移动到类A :-)

        当 JVM 加载类时,它会将所有静态块分组并按照它们声明的顺序执行它们。

        编辑 (Source): 简短的回答是静态在 Java 中不是继承的。相反,类中声明的静态成员(受“访问”限制)在派生类的命名空间中直接可见,除非它们被派生类中的声明“隐藏”。

        所以如果静态只属于类,为什么它会向下渗透到 派生类?它不应该只留在它所在的班级吗 定义了吗?

        【讨论】:

        • 是的,但是为什么不执行静态块。
        • 您不必创建该类的对象来执行静态块。请参阅下面的示例
        • @durron597 我只是告诉了一种实现它的方法,问题是为什么示例没有在派生类中执行静态代码块:-)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 1970-01-01
        • 1970-01-01
        • 2023-03-26
        • 2012-02-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多