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
答案 0 :(得分:10)
对B.x
的引用会发出以下字节码:
getstatic #3 <Field int B.x>
Java虚拟机说明anewarray,checkcast,getfield, getstatic ,instanceof,invokedynamic,invokeinterface,invokespecial, invokestatic,invokevirtual,ldc,ldc_w,multianewarray,new, putfield和putstatic将符号引用添加到运行时 恒定的池。执行任何这些指令都需要 解析其符号引用。
因此JVM应该解析符号引用B.x
。字段分辨率为specified like this:
将未解析的符号引用从D解析为a中的字段 class或interface C,字段给出的对C的符号引用 必须首先解决参考(第5.4.3.1节)。
...
解析字段引用时,字段解析首先尝试 在C及其超类中查找引用字段 :
如果C声明了一个具有由...指定的名称和描述符的字段 字段引用,字段查找成功。声明的字段是 字段查找的结果。
否则,字段查找将递归地应用于direct 指定类或接口C的超接口。
否则,如果C具有超类S,则应用字段查找 递归到S.
否则,字段查找失败。
换句话说,JVM会将B.x
解析为A.x
。这就是为什么只需要加载A
类的原因。
答案 1 :(得分:7)
因为B.x
实际上是A.x
所以只需要加载A
类。
答案 2 :(得分:4)
类的初始化包括执行其
static
初始值设定项和类中声明的静态字段(类变量)的初始值设定项。[...]
对
static
字段(§8.3.1.1)的引用仅导致实际声明它的类或接口的初始化,即使它可能通过子类的名称,子接口引用,或实现接口的类。
所以尽管 - 与上面某些答案中的声明相反 - 类B
确实必须加载,以确定B.x
在{{}中声明1}},类A
不是初始化(即,它的B
初始化程序实际上并未运行),直到您执行更具体的static
。
答案 3 :(得分:2)
Class B
扩展A
,当您拨打public static variable x
B.x
如果您期望Inside B.
,那么您必须创建该类的Object。执行所有静态代码块。或者将该静态代码块移动到类A
: - )
当JVM加载类时,它会对所有静态块进行分组,并按照它们声明的顺序执行它们。
编辑(Source): 简短的回答是静态不是用Java继承的。相反,在类中声明的静态成员(受“访问”限制)在派生类的名称空间中直接可见,除非它们被派生类中的声明“隐藏”。
因此,如果Static属于Class,那么为什么它会逐渐减少 派生类?它不应该只停留在它所在的类 定义了吗?
答案 4 :(得分:2)
在直接访问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.从文章&#34;并且不要忘记,当JVM加载类时,将执行此代码。 JVM将所有这些块组合成一个静态块然后执行。以下是我要提及的几点:&#34;