javainheritance的静态初始化
public class Main { public static void main(String[] args) { System.out.println(Bx); } } class A { public static String x = "x"; } class B extends A { static { System.out.print("Inside B."); } }
问题:为什么输出将是: x
。 但不是: Inside Bx
对Bx
的引用会发出以下字节码:
getstatic #3
根据Java虚拟机规范
Java虚拟机指示anewarray,checkcast,getfield, getstatic ,instanceof,invokedynamic,invokeinterface,invokespecial,invokestatic,invokevirtual,ldc,ldc_w,multianewarray,new,putfield和putstatic对运行时常量池进行符号引用 。 执行任何这些指令都需要解析其符号引用 。
因此JVM应该解析对Bx
的符号引用 。 字段分辨率指定如下 :
要解决从D到类或接口C中的字段的未解析的符号引用,必须首先解析由字段引用给出的对C的符号引用(第5.4.3.1节)。
…
解析字段引用时,字段解析首先尝试在C及其超类中查找引用的字段:
如果C声明具有字段引用指定的名称和描述符的字段,则字段查找成功。 声明的字段是字段查找的结果。
否则,将字段查找递归地应用于指定类或接口C的直接超接口。
否则,如果C具有超类S,则将字段查找递归地应用于S.
否则,字段查找失败。
换句话说,JVM会将Bx
解析为Ax
。 这就是为什么只需要加载A
类的原因。
因为Bx
实际上是Ax
所以只需要加载A
类。
§12.4JavaSE 7 规范Java语言规范的 “类和接口的初始化”指定:
类的初始化包括执行其
static
初始化程序和类中声明的静态字段(类变量)的初始化程序。[…]
对
static
字段的引用(第8.3.1.1节 )仅导致实际声明它的类或接口的初始化,即使它可能通过子类的名称,子接口或实现接口的类来引用。
因此,尽管 – 与上述某些答案中的声明相反 – 必须加载 B
类,为了确定Bx
在A
声明, B
类未初始化 (即,其static
初始化器实际上并未运行),直到您做一些比B
更具体的事情。
它实际上不需要加载B
直到它直接访问B
的静态成员。 注意这段代码:
public class TestMain { public static void main(String[] args) { System.out.println(Bx); System.out.println(By); } 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 By
因为在访问B
某些内容之前不需要加载B
这是关于这个主题的一个很好的链接。 从文章“不要忘记,这个代码将在JVM加载类时执行.JVM将所有这些块组合成一个静态块然后执行。这里有几点我想提一下:”
Class B
扩展A
,它有一个public static variable x
,当你调用Bx
时,它正在访问
如果你期望Inside B.
as out,你必须创建该类的Object。 执行所有静态代码块。 或将该静态代码块移动到A
类:-)
当JVM加载类时,它会对所有静态块进行分组,并按照它们声明的顺序执行它们。
编辑 ( 来源 ):简短的回答是静态不是用Javainheritance的。 相反,在类中声明的静态成员(受“访问”限制)在派生类的名称空间中直接可见,除非它们在派生类中被声明“隐藏”。
因此,如果静态属于类,那么为什么它会逐渐渗透到派生类? 它不应该只停留在定义它的类中吗?