如果从未使用过相应的 class,classloader 是否加载 class 文件?
Does the classloader load a class file if the corresponding class is never used?
为了让我的问题更清楚,请考虑以下用例:
假设有一个包允许在给定平台上执行一组操作,例如 class 可以在 Windows 上编辑注册表。
此包在其他平台上不存在,因为在其他操作系统上没有等效操作。
为简单起见,考虑
windows/Registry.java
package windows;
public class Registry {
static Registry instance = null;
static{
System.out.println("print from static block");
}
private Registry() {
System.out.println("Registry instance created!");
}
public static synchronized Registry getInstance() {
if (null == instance) {
instance = new Registry();
}
return instance;
}
public void foo() {
System.out.println("foo called.");
}
}
和 class 我将有条件地使用注册表的地方:main/Main.java
package main;
import windows.Registry;
public class Main {
public static void test1(boolean onWindows) {
if (onWindows) {
Registry instance = Registry.getInstance();
System.out.println("We are on Windows: ");
instance.foo();
} else {
System.out.println("We are somewhere else!");
}
}
public static void main(String[] args) {
System.out.println("Entered main");
boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false;
test1(onWindows);
}
}
问题是,如果Main.class
中没有显式执行函数或class,是否保证不会执行Registry.class
中的代码?
我能够在多个桌面平台和不同的 java 版本上测试这个例子,但我想知道这种行为是否被记录在案并且可以依赖它,如果它因此是预期的行为也在其他平台上,例如 android 或 JRE 的嵌入式版本。
如果没有这样的保证,因为(可能是自定义的)class加载程序可能决定加载 class 路径中的所有 .class
文件,我可以假设如果 onWindows
为假并且我从 classpath?
中删除 Registry.class
,代码将在没有 java.lang.NoClassDefFoundError
的情况下工作
到目前为止我观察到的行为是
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true
Entered main
print from static block
Registry instance created!
We are on Windows:
foo called.
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false
Entered main
We are somewhere else!
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false
Entered main
We are somewhere else!
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true
Entered main
Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry
at main.Main.test1(Main.java:9)
at main.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: windows.Registry
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 2 more
是否定义了这些行为(惰性 class 加载程序和删除 Registry.class
)?
The question is, if no function or class is explicitly executed in Main.class
, is it guaranteed that no code from Registry.class
is executed?
这并没有直接触及 class 加载 ,而是 class 初始化 ,是来自 class 的任何代码被执行的第一个点。具体来说,静态初始化块和静态成员的初始化程序在此阶段执行。有问题的 class 此时必须已经加载和验证,但它可能在任意时间之前加载。
根据 JLS 12.4.1、
A class or interface T will be initialized immediately before the
first occurrence of any one of the following:
T is a class and an instance of T is created.
A static
method declared by T is invoked.
A static
field declared by T is assigned.
A static
field declared by T is used and the field is not a constant variable
因此,如果您从未实例化 class 或访问其任何静态方法或字段(读取作为“常量变量”的静态字段除外),则 [=41 中没有代码=] 将永远被执行。
但是 class 没有被初始化并不意味着不会尝试 加载 它。 JLS 不禁止实现预期地加载 classes。其实JLS 12.2.1具体说:
a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.
因此,不,当class windows.Registry
无法加载,无论是否可以预期实际使用。但是,在适当的情况下,您 可以 依赖于 class 未被初始化,因此没有代码被执行。
为了让我的问题更清楚,请考虑以下用例:
假设有一个包允许在给定平台上执行一组操作,例如 class 可以在 Windows 上编辑注册表。 此包在其他平台上不存在,因为在其他操作系统上没有等效操作。
为简单起见,考虑
windows/Registry.java
package windows;
public class Registry {
static Registry instance = null;
static{
System.out.println("print from static block");
}
private Registry() {
System.out.println("Registry instance created!");
}
public static synchronized Registry getInstance() {
if (null == instance) {
instance = new Registry();
}
return instance;
}
public void foo() {
System.out.println("foo called.");
}
}
和 class 我将有条件地使用注册表的地方:main/Main.java
package main;
import windows.Registry;
public class Main {
public static void test1(boolean onWindows) {
if (onWindows) {
Registry instance = Registry.getInstance();
System.out.println("We are on Windows: ");
instance.foo();
} else {
System.out.println("We are somewhere else!");
}
}
public static void main(String[] args) {
System.out.println("Entered main");
boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false;
test1(onWindows);
}
}
问题是,如果Main.class
中没有显式执行函数或class,是否保证不会执行Registry.class
中的代码?
我能够在多个桌面平台和不同的 java 版本上测试这个例子,但我想知道这种行为是否被记录在案并且可以依赖它,如果它因此是预期的行为也在其他平台上,例如 android 或 JRE 的嵌入式版本。
如果没有这样的保证,因为(可能是自定义的)class加载程序可能决定加载 class 路径中的所有 .class
文件,我可以假设如果 onWindows
为假并且我从 classpath?
Registry.class
,代码将在没有 java.lang.NoClassDefFoundError
的情况下工作
到目前为止我观察到的行为是
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true
Entered main
print from static block
Registry instance created!
We are on Windows:
foo called.
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false
Entered main
We are somewhere else!
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false
Entered main
We are somewhere else!
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true
Entered main
Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry
at main.Main.test1(Main.java:9)
at main.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: windows.Registry
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 2 more
是否定义了这些行为(惰性 class 加载程序和删除 Registry.class
)?
The question is, if no function or class is explicitly executed in
Main.class
, is it guaranteed that no code fromRegistry.class
is executed?
这并没有直接触及 class 加载 ,而是 class 初始化 ,是来自 class 的任何代码被执行的第一个点。具体来说,静态初始化块和静态成员的初始化程序在此阶段执行。有问题的 class 此时必须已经加载和验证,但它可能在任意时间之前加载。
根据 JLS 12.4.1、
A class or interface T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
A
static
method declared by T is invoked.A
static
field declared by T is assigned.A
static
field declared by T is used and the field is not a constant variable
因此,如果您从未实例化 class 或访问其任何静态方法或字段(读取作为“常量变量”的静态字段除外),则 [=41 中没有代码=] 将永远被执行。
但是 class 没有被初始化并不意味着不会尝试 加载 它。 JLS 不禁止实现预期地加载 classes。其实JLS 12.2.1具体说:
a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.
因此,不,当class windows.Registry
无法加载,无论是否可以预期实际使用。但是,在适当的情况下,您 可以 依赖于 class 未被初始化,因此没有代码被执行。