奇怪的Java类NoClassDefFoundError

时间:2016-08-04 12:31:31

标签: java jvm

环境:jdk1.7

此测试需要

javax.servlet-api-3.0.1.jar

重现步骤:

  1. javac Test1.java -cp javax.servlet-api-3.0.1.jar使用javax.servlet-api-3.0.1.jar构建Test1.java

  2. javac Test2.java -cp javax.servlet-api-3.0.1.jar使用javax.servlet-api-3.0.1.jar构建Test2.java

  3. javac Test3.java构建Test3.java

  4. java -classpath .:javax.servlet-api-3.0.1.jar Test3依赖运行Test3。以下是输出。好在这里。 hello world1 hello world2

  5. 但是当此命令java Test3运行时,抛出异常。这篇文章结尾的结果。奇怪的是"你好世界1"可以打印出来,但不是打印出来" hello world2"抛出异常。

  6. Test1.java

    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.OutputStream;
    public class Test1 {
        public void getRequest(HttpServletResponse resp) throws IOException {
    
            OutputStream os = resp.getOutputStream();
            resp.getOutputStream().close();
        }
    
        public void hello(String world) {
            System.out.println("hello " + world);
        }
    }
    

    Test2.java

    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.OutputStream;
    public class Test2 {
    
        public void getRequest(HttpServletResponse resp) throws IOException {
            OutputStream os = resp.getOutputStream();
            try {
                resp.getOutputStream().close();
            } catch (Exception e) {
            }
        }
    
        public void hello(String world) {
            System.out.println("hello " + world);
        }
    }
    

    Test3.java

    public class Test3 {
        public static void main(String[] args) {
            new Test1().hello("world1");
            new Test2().hello("world2");
        }
    }
    

    最后一步的输出。 Test2.hello(" world2")抛出异常:

    hello world1
    Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletOutputStream
        at cn.test.abc1.Test3.main(Test3.java:9)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
    Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletOutputStream
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 6 more
    

    我对异常非常困惑。因为我没有在Class ServletOutputStream中使用任何代码。并且Test1和Test2的差异只有try被阻止。

    这个问题不是重复的问题,因为它标记了。因为当try块涉及时,JVM不应抛出异常。

2 个答案:

答案 0 :(得分:2)

比较javap -v -c Test?的输出后,我得到答案 javac try/catch块的Test2.java创建microsoft codedui test属性。

如果在类加载时发现StackMapTable,JVM将执行字节码验证并检查引用的类。这就是它抛出java.lang.NoClassDefFoundError的原因。

StackMapTable描述了有关StackMapTable的详细信息。

错误的答案

我编译Test1.java并对其进行反编译,源代码变为:

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletOutputStream;

public class Test1 {
    public void getRequest(HttpServletResponse resp) throws IOException {
        ServletOutputStream os = resp.getOutputStream();
        resp.getOutputStream().close();
    }

    public void hello(String world) {
        System.out.println("hello " + world);
    }
}

由于resp.getOutputStream()的返回对象为javax.servlet.ServletOutputStream。在运行时,JVM仍然必须验证此类是java.io.OutputStream的子类,因此它尝试加载ServletOutputStream.class。但是JVM无法找到它并抛出ClassNotFoundException

答案 1 :(得分:0)

对我来说很有意义。 Test3引用了Test2和Test1,并且在运行时都需要类路径上的javax.servlet-api-3.0.1.jar