簡體   English   中英

在JVM中加載類時

[英]When a class is loaded in JVM

我有兩個例子:

例1:

public class A {

}

public class B {

  public void m(A a) {

  }

}
public class C {

    public static void main(String[] args) {
            B b = new B();
            System.out.println("hello!");
    }

}

編譯所有三個類。 刪除A.class。 運行main。 沒有異常被拋出。

例2:

public class D {

}

public class E {

    public void omg(D d) {

    }

    public static void main(String[] args) {
        E e = new E();
    }


}

編譯類。 刪除D.class。 運行main方法。

Exception in thread "main" java.lang.NoClassDefFoundError: D
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
    at java.lang.Class.getMethod0(Unknown Source)
    at java.lang.Class.getMethod(Unknown Source)
    at sun.launcher.LauncherHelper.getMainMethod(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: D
    at java.net.URLClassLoader$1.run(Unknown Source)

為什么? D從未被引用過。

兩者都是JavaVM規范允許的。 第5章中,加載,鏈接和初始化我們有:

例如,Java虛擬機實現可以選擇在使用它時分別解析類或接口中的每個符號引用(“延遲”或“延遲”解析),或者在驗證類時立即解析它們( “渴望”或“靜態”決議)。

我的猜測是Sun / Oracle選擇對初始(“主”)類執行“靜態”解析,因為很可能很快就會調用主類中的方法。

您的類在方法public void omg(D d)引用了D類。

通常,Sun / Oracle的JVM使用延遲解析,因此只要您不使用該方法就沒關系,但是,您可以從堆棧跟蹤中看到主要方法是通過反射操作Class.getMethod搜索的。這有所不同。

您可以使用以下代碼進行驗證:

public class B {

  public static void main(String[] args) {
    D.main(args);
  }
}
class D {

  public void foo(E e) {}
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}
class E { }

在這里,在編譯和運行java B之后刪除E.class不會產生錯誤。 現在在代碼中插入一個反射方法查找:

public class B {

  public static void main(String[] args) {
    try {
      D.class.getMethod("main", String[].class);
    } catch(NoSuchMethodException ex) {
      ex.printStackTrace();
    }
    D.main(args);
  }
}
class D {

  public void foo(E e) {}
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}
class E { }

現在,在編譯后刪除E類會在運行java B時產生java.lang.NoClassDefFoundError: E 因此,手動觸發的方法查找會再現原始代碼示例中的行為,盡管D類不是此處的main類。


請注意,您可以通過從方法foo刪除public修飾符來解決問題。 原因是Class.getMethod只考慮public方法並跳過所有其他方法。

這也適用於您的原始代碼示例:從方法omg刪除public將使NoClassDefFoundError消失。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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