>假设我有两个类A和B。
class A{}
import A;
class B{}
然后,如果我在方法if(object instanceof B)
中对某个对象main
执行,它会加载类A
以及它在类B
的导入语句中吗? 类加载器究竟什么时候会在运行时加载这些类?
有两点需要注意:
-
import
语句不会影响类的运行时行为。 不会为 import 语句生成任何代码。 如果您只是在类 B 中导入类 A,则加载类 B 不会导致加载类 A。
类 加载时间和类初始化时间不同。
类的生命周期是它在使用之前被加载、链接和初始化(参见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
测试之前发生。 否则,B
instanceof
测试将不会初始化。
从表面上看,不需要加载类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
。