catch子句中的ClassNotFound异常

时间:2013-05-18 06:30:06

标签: java exception classloader classnotfound

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

    public void init() {
        //"dummy2" class defined in a different jar
        dummy2 d = new dummy2(); 
        try { d.run(); } 
        //"dummyexception" class defined in a different jar
        catch (dummyexception e) { e.printStackTrace(); }
        catch (Exception e) { e.printStackTrace(); }
    }

    public void init2() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                //"dummy2" class defined in a different jar
                dummy2 d = new dummy2(); 
                try { d.run(); } 
                //"dummyexception" class defined in a different jar
                catch (dummyexception e) { e.printStackTrace(); } 
                catch (Exception e) { e.printStackTrace(); }        
            }
        };
    }
}

现在在运行之前,我从类路径中删除了依赖jar。 对于方法ClassNotFoundException中引用的类“dummyexception”,我得到“init()”,而不是方法init2()中的用法。有趣的是,在方法ClassNotFoundException中引用了“dummy2”类没有“init()”。

3 个答案:

答案 0 :(得分:0)

  

现在在运行之前,我从类路径中删除了依赖jar。我在方法init()中引用的类'dummyexception'得到'ClassNotFoundException',而不是方法init2()中的用法。

只有在执行init方法时才会加载类dummy2。尽管dummyexcetion是一个catch参数,因此JVM将此类作为java方法处理。 dummyexception类的全名放在jvm * .class文件constant_pool中,并在加载dummy之前检查类可用性。

让我们考虑一个例子:

public class Dummy {

    public void init(){
        DummyExternal dummy = new DummyExternal();
        try {
            dummy.run();
        } catch(DummyException e) {
        } catch(Exception e){ }
    }

    public void init2(){
        new Runnable() {            
            @Override
            public void run() {
                DummyExternal dummy = new DummyExternal();
                try {
                    dummy.run();
                } catch(DummyException e) {
                    e.printStackTrace();
                } catch(Exception e){}
            }
        }.run();
    }

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

}



public class DummyException extends RuntimeException {

    private static final long serialVersionUID = 2049347223914508696L;

    static {
        System.out.println("DummyException");
    }

}


public class DummyExternal implements Runnable {

    static {
        System.out.println("DummyExternal");
    }

    @Override
    public void run() {
        System.out.println("DummyExternal");
    }

}

在输出中我们将:

  

-rw-r - r-- 1 taky staff 756B May 19 08:26 Dummy $ 1.class

     

-rw-r - r-- 1 taky staff 956B May 19 08:26 Dummy.class

     

-rw-r - r-- 1 taky staff 545B May 19 08:26 DummyException.class

     

-rw-r - r-- 1 taky staff 569B May 19 08:26 DummyExternal.class

命令java Dummy打印hello world。因此,没有加载DummyExternalDummyException类。

删除DummyExternal类文件:rm DummyExternal.classjava Dummy的结果不会改变。

我们删除DummyException类文件:rm DummyException.class

执行java Dummy后,我们收到了有趣的堆栈跟踪。

Exception in thread "main" java.lang.NoClassDefFoundError: DummyException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
    at java.lang.Class.getMethod0(Class.java:2685)
    at java.lang.Class.getMethod(Class.java:1620)
    at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:494)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:486)
Caused by: java.lang.ClassNotFoundException: DummyException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    ... 6 more

这种行为是因为异常处理机制。如果class是catch参数,则生成异常处理程序,应在异常处理时解析。异常处理机制与方法调用类似,ClassLoader在类加载之前从常量池解析DummyException

答案 1 :(得分:0)

这里推测 - 方法正在按字典顺序验证,并且对于每个方法,异常处理程序在局部变量之前进行验证。

答案 2 :(得分:0)

如果我理解正确,您首先通过提供所有依赖jar文件来编译您的类。这就是你的课程成功编译的原因。然后在运行程序之前删除了所有依赖的jar文件。然后你就出错了。

我对此的看法:

据我所知,您不应该收到任何错误,因为使用依赖 jar的init()init2()并未被任何函数调用。

未调用的函数与错误生成之间的关系是什么?

编译类时, JVM 会创建类文件。在这些类文件中, JVM 通过搜索symbolic string references中的类来创建classpath来调用方法和类。如果找不到引用的类,则在编译时会出现错误,并且您的类将无法编译。如果找到它,那么编译成功,然后 JVM 在类文件中添加symbolic string references。此时没有实际的pointer to methods and classes

现在,当您运行程序时,您可以在类文件中以symbolic string references的形式找到对方法的调用。此时,这些symbolic references会转换为actual pointers to classes and methods。此时,您的class loader将搜索那些依赖类 only if 从某种方法调用引用的方法。如果它在类路径中找不到依赖类,则会生成错误。 注意:如果未调用方法, class loader将不会搜索依赖类。 Symbolic instructions将被忽略,不会生成任何错误。

您的情况会怎样?

我看不到您的init()init2()被任何人调用。这意味着class loader无需找到dependent jarscreate pointer to these methods。这意味着您不应该收到任何错误 Unless 正在调用这些方法。