在JVM中加载类时

时间:2014-10-23 10:08:55

标签: java classloader

我有两个例子:

示例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从未被引用过。

2 个答案:

答案 0 :(得分:4)

JavaVM规范允许这两者。在Chapter 5. Loading, Linking, and Initializing我们有:

  

例如,Java虚拟机实现可以选择在使用它时分别解析类或接口中的每个符号引用(“延迟”或“延迟”解析),或者在类是被核实(“渴望”或“静态”解决)。

我的猜测是,Sun / Oracle选择对初始(“主”)类执行“静态”解析,因为很可能很快就会调用主类中的方法。

答案 1 :(得分:2)

您的班级引用方法D中的班级public void omg(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 { }

此处,在编译并运行E.class后删除java B不会产生错误。现在在代码中插入反射方法查找:

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.lang.NoClassDefFoundError: E会生成java B。因此,手动触发的方法查找会重现原始代码示例中的行为,尽管类D不是此处的main类。


请注意,您可以通过从方法public中删除foo修饰符来解决问题。原因是Class.getMethod仅考虑public方法并跳过所有其他方法。

这也适用于原始代码示例:从方法public中删除omg会使NoClassDefFoundError消失。