在java虚拟机中——第7章类型的生存期——初始化的代码片段如下。
class NewParent {
static int hoursOfSleep = (int) (Math.random() * 3.0);
static {
System.out.println("NewParent was initialized.");
}
}
class NewbornBaby extends NewParent {
static int hoursOfCrying = 6 + (int) (Math.random() * 2.0);
static {
System.out.println("NewbornBaby was initialized.");
}
}
class Example2 {
// Invoking main() is an active use of Example2
public static void main(String[] args) {
// Using hoursOfSleep is an active use of NewParent,
// but a passive use of NewbornBaby
int hours = NewbornBaby.hoursOfSleep;
System.out.println(hours);
}
static {
System.out.println("Example2 was initialized.");
}
}
在上面的例子中,执行example2的main()只会初始化example2和newparent。新生婴儿未初始化,不需要加载。 Example2
参考文献 NewbornBaby
,我认为应该是“jvm加载” NewbornBaby
一开始它发现 NewbornBaby
没有 hoursOfSleep
字段,然后继续加载 NewbornBaby
的超类 NewParent
". 那么,为什么java虚拟机内部说不需要加载newbornbaby呢?
之后 javac Example2.java
,我跑 java -verbose:class Example2
,下面是输出的一部分。
[Loaded Example2 from file:/Users/jason/trivial/]
[Loaded sun.launcher.LauncherHelper$FXHelper from /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class$MethodArray from /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
Example2 was initialized.
[Loaded NewParent from file:/Users/jason/trivial/]
[Loaded NewbornBaby from file:/Users/jason/trivial/]
[Loaded java.lang.Math$RandomNumberGeneratorHolder from /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.util.Random from /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
NewParent was initialized.
1
它表明jvm确实加载了 NewbornBaby
.
2条答案
按热度按时间a7qyws3x1#
在本例中,正如您在书中所引用的
NewbornBaby
一开始它发现NewbornBaby
没有hoursOfSleep
字段,然后继续加载NewbornBaby
的超类NewParent
“:jvm尝试加载NewbornBaby
但是这个类是NewParent
所以要加载它需要加载它的超类的所有类方法,所以要加载newbornBaby
(一)必须加载NewParent
(二)作为装载的一部分newbornBaby
.在恢复负荷时
NewParent
,看起来它是先加载的,但请记住,这是加载newbornBaby
(已启动的进程)xj3cbfub2#
您遇到了类加载和初始化的常见混淆。
您链接的这篇文章介绍了由一些定义良好的操作触发的初始化:
§12.4.1. 初始化发生时
类或接口类型t将在第一次出现以下任一情况之前立即初始化:
T
是的类和示例T
已创建。一
static
方法声明者T
已调用。一
static
字段声明者T
已分配。一
static
字段声明者T
并且字段不是常量变量(§4.12.4).您的代码正在访问
static
课堂上的场NewParent
这将触发该类的初始化。你访问它的方式与此无关。因此,当您运行代码而不记录时,它会打印出来所以呢
NewbornBaby
尚未初始化,因为未执行任何指定的触发器操作。然而,类加载是完全不同的事情。它的时间是故意不指定的,只是它必须在初始化之前发生。jvm可能会急切地加载所有引用的类,甚至在应用程序启动之前,或者延迟加载,直到验证器或应用程序需要它为止。
在这一点上,理解编译器将检查
static
字段存在并将在类中找到它NewParent
,它仍将使用源代码中使用的类型生成字节码。所以,加载指定的类NewbornBaby
在运行时是不可避免的(文章在这方面是错误的),即使它不会得到初始化(这篇文章似乎与加载混淆)。与jls相比, §13.1. 二进制的形式:
给定一个表示类中字段访问的合法表达式
C
,引用名为f
它不是常量变量,并且在(可能不同的)类或接口中声明D
,我们定义字段引用的限定类型如下:...
如果引用的格式为typename
.f
,其中typename表示类或接口,则typename表示的类或接口是引用的限定类型。...
引用
f
必须编译成符号引用才能擦除(§4.6)引用的限定类型,加上字段的简单名称,f
.换句话说,这个表达式
NewbornBaby.hoursOfSleep
将使用NewbornBaby
作为限定类型,运行时必须像编译器那样在父类型中再次找到实际字段。如果有不同版本的NewbornBaby
在运行时具有该名称和类型的匹配字段时,将改用该字段。没有办法加载类
NewbornBaby
在运行时,找出哪个场景适用。此外,当类加载被记录时,它是不规范的。似乎,它不是在加载被触发时发生的,而是在加载完成时发生的。这已经包括了一些验证步骤,包括加载和检查超类是否存在以及是否兼容(即不是
interface
,不是final
等)。所以当验证器遇到访问类
NewbornBaby
,它触发该类的加载,从而触发NewParent
. 但是NewParent
首先完成并首先报告,因为它的完成是完成的加载所必需的NewbornBaby
之后会记录下来。但是,如前所述,这是特定于实现的。只精确指定了初始化。