为了使我的问题更清楚,请考虑以下用例:
假设有一个包允许在给定平台上进行一组操作,例如在 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.");
}
}
Run Code Online (Sandbox Code Playgroud)
以及我将有条件使用注册表的类: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);
}
}
Run Code Online (Sandbox Code Playgroud)
问题是,如果 中 没有显式执行任何函数或类Main.class,是否可以保证 中 的代码不会Registry.class被执行?
我能够在多个桌面平台和不同的 java 版本上测试此示例,但我想知道此行为是否已记录并且可以依赖它,如果它因此也是其他平台(例如 android 或嵌入式)上的预期行为JRE 的版本。
如果没有这样的保证,因为(可能是自定义的)类加载器可能决定加载.class类路径中的所有文件,我是否可以假设代码将在没有java.lang.NoClassDefFoundErrorif为 false 并从类路径中onWindows删除的情况下工作?Registry.class
到目前为止我观察到的行为是
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
Run Code Online (Sandbox Code Playgroud)
这些行为(惰性类加载器和删除Registry.class)是否已定义?
问题是,如果 中 没有显式执行任何函数或类
Main.class,是否可以保证 中 的代码不会Registry.class被执行?
这并不直接涉及类加载,而是涉及类初始化,这是执行相关类中任何代码的第一个点。具体来说,静态初始化块和静态成员的初始化器在此阶段执行。此时,所讨论的类必须已被加载并验证,但它可能已提前加载任意时间。
根据JLS 12.4.1,
类或接口 T 将在第一次出现以下任一情况之前立即初始化:
T 是一个类,并且创建了 T 的实例。
static调用 T 声明的方法。
staticT 声明的字段被赋值。使用了 T 声明的字段
static并且该字段不是常量变量
因此,如果您从未实例化该类或访问其任何静态方法或字段(除了读取作为“常量变量”的静态字段),则该类中的任何代码都不会被执行。
但是,类未初始化并不意味着不会尝试加载它。JLS 并不禁止实现前瞻性地加载类。事实上,JLS 12.2.1具体说:
类加载器可以缓存类和接口的二进制表示,根据预期的使用情况预取它们,或者一起加载一组相关的类。
main.Main因此,不,当类无法加载时,依赖类代表的应用程序运行而不会出现java.lang.NoClassDefFoundError或其他加载错误是不安全的windows.Registry,无论它是否可以预期实际使用。但是,在适当的情况下,您可以依赖未初始化的类,因此不会执行其中的任何代码。
| 归档时间: |
|
| 查看次数: |
348 次 |
| 最近记录: |