簡體   English   中英

如果從未使用過相應的 class,類加載器是否加載 class 文件?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM