jvm 何时会为实例运算符加载导入的类?



>假设我有两个类A和B。

class A{}
import A;
class B{}

然后,如果我在方法if(object instanceof B)中对某个对象main执行,它会加载类A以及它在类B的导入语句中吗? 类加载器究竟什么时候会在运行时加载这些类?

有两点需要注意:

  1. import语句不会影响类的运行时行为。 不会为 import 语句生成任何代码。 如果您只是在类 B 中导入类 A,则加载类 B 不会导致加载类 A。

  2. 加载时间和类初始化时间不同。

类的生命周期是它在使用之前被加载、链接和初始化(参见JLS第12章)。

加载
  • 由类加载器执行,涉及查找类文件、将其读入byte[]并调用ClassLoader::defineClass

  • 链接由 JVM 核心代码执行,包括符号引用的验证、准备和解析。 JLS 12.3 是这样说的:

    此规范允许在链接活动(以及由于递归,加载)发生时实现灵活性,前提是尊重Java编程语言的语义,在初始化之前完全验证和准备类或接口,并且在链接期间检测到的错误在程序中的某个点抛出,其中程序采取了一些可能需要链接到所涉及的类或接口的操作在错误中。

    这意味着我们无法具体说明何时执行不同的任务。

  • 初始化在链接所有相关类后进行。 根据JLS 12.4.1:

    类或接口类型 T 将在以下任一情况首次出现之前立即初始化:

    T
    • 是一个类,创建 T 的实例。
    • 调用 T 声明的静态方法。
    • 分配由 T 声明的静态字段。
    • 使用 T 声明的静态字段,并且该字段不是常量变量 (§4.12.4)。

根据JLS(见上文),我们无法准确说明何时加载和链接B。 我们只能说它发生在B初始化之前......如果已初始化。 (您可以通过打开一些 JVM 日志记录来深入了解类装入的顺序。 但是,顺序可能因 JVM 供应商和版本而异。

根据 JLS(见上文),B的初始化(通常)将在main方法创建B的第一个实例时发生。 假设问题中的object已初始化为B实例,则初始化将在instanceof测试之前发生。 否则,Binstanceof测试将不会初始化。

从表面上看,不需要加载类A。 (这取决于类B是否以及如何实际使用类A,以及代码库的其他部分是否/如何使用A

你可以这样测试它:

主.java:

package test;
import test.sub.B;
public class Main {
public static void main(String args[]) {
System.out.println("Creating object");
final B b = new B();
System.out.println("instanceof check");
if (b instanceof B) {
// Do nothing
}
}
}

答.java:

package test;
public class A {
static {
System.out.println("Class A loaded");
}
}

乙.java:

package test.sub;
import test.A;
public class B {
A a;
static {
System.out.println("Class B loaded");
}
}

输出:

Creating object
Class B loaded
instanceof check

因此,导入和声明(A a;)都不会加载类。

这符合维基百科的声明:The Java Class Loader [...] dynamically loads Java classes [...]. [...] This loading is typically done "on demand", in that it does not occur until the class is called by the program. [...]

将类B中的A a;更改为A a = new A();时,将加载类A

最新更新