[英]Does the classloader load a class file if the corresponding class is never used?
為了使我的問題更清楚,請考慮以下用例:
假設有一個 package 允許在給定平台上進行一組操作,例如 class 可以在 Windows 上編輯注冊表。這個 package 在其他平台上不存在,因為在其他操作系統上沒有等效的操作。
為了簡單起見,考慮
窗戶/注冊表.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中沒有顯式執行function或Main.class
,是否保證Registry.class
中的代碼不執行?
我能夠在多個桌面平台和不同的 java 版本上測試這個例子,但我想知道這種行為是否被記錄並且可以依賴它,如果它因此在其他平台上也是預期的行為,比如 android 或嵌入式JRE 的版本。
如果沒有這樣的保證,因為(可能是自定義的)類加載器可能決定加載類路徑中的所有.class
文件,我是否可以假設如果onWindows
為 false 並且我刪除Registry.class
,代碼將在沒有java.lang.NoClassDefFoundError
的情況下工作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
這些行為(惰性類加載器和刪除Registry.class
)是否已定義?
問題是,如果Main.class中沒有顯式執行function或
Main.class
,是否保證Registry.class
中的代碼不執行?
這並沒有直接觸及 class loading ,而是觸及 class initialization ,這是來自 class 的任何代碼被執行的第一個點。 具體來說,static個初始化塊和static個成員的初始化器在這個階段被執行。 有問題的 class 此時必須已經加載和驗證,但它可能在任意時間之前加載過。
根據JLS 12.4.1 ,
class 或接口 T 將在第一次出現以下任何一項之前立即初始化:
T 是一個 class 並且創建了一個 T 的實例。
調用 T 聲明的
static
方法。分配了 T 聲明的
static
字段。使用了 T 聲明的
static
字段,該字段不是常量變量
因此,如果您從未實例化 class 或訪問訪問其任何 static 方法或字段(讀取 static 字段是“常量變量”除外),則永遠不會執行 class 中的任何代碼。
但是 class 沒有被初始化並不意味着不會嘗試加載它。 JLS 不禁止實現前瞻性地加載類。 事實上, JLS 12.2.1中明確表示:
class 加載器可以緩存類和接口的二進制表示,根據預期用途預取它們,或一起加載一組相關類。
因此,不,依賴 class main.Main
代表的應用程序在沒有java.lang.NoClassDefFoundError
或其他加載錯誤時運行是不安全的 class windows.Registry
無法加載,無論是否可以實際預期要使用的。 但是,在適當的情況下,您可以相信 class 沒有被初始化,因此它的代碼沒有被執行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.