Weird NoClassDefFoundError Java Class

Wednesday: jdk1.7

javax.servlet-api-3.0.1.jar needed for this test.

Repeat the steps:

  • javac Test1.java -cp javax.servlet-api-3.0.1.jar build Test1.java using javax.servlet-api-3.0.1.jar

  • javac Test2.java -cp javax.servlet-api-3.0.1.jar build Test2.java using javax.servlet-api-3.0.1.jar

  • javac Test3.java build Test3.java

  • java -classpath .:javax.servlet-api-3.0.1.jar Test3 run Test3 with a dependency. The following is the conclusion. It is nice here. hello world1 hello world2

  • But when this java Test3 command is java Test3 , an Exception is java Test3 . The result is at the end of this post. It is strange that hello world1 can be printed, but instead of printing hello world2, an exception is thrown.

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"); } } 

conclusion of the last step. Test2.hello ("world2") throws an exception:

 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 

I am very confused by the Exception. Because I did not use the code in the ServletOutputStream class. And the difference in Test1 and Test2 is only limited by try .

This question is not a duplicate question as it is noted. Because the JVM should not throw an exception when a try block is involved.

+5
source share
2 answers

After comparing the output javap -v -c Test? I get an answer.
javac create a StackMapTable for the try/catch block of Test2.java .

If the StackMapTable is found while loading the class, the JVM will perform a bytecode check and check the reference class. That is why it throws java.lang.NoClassDefFoundError .

fooobar.com/questions/148125 / ... describe more information about StackMapTable .

Old wrong answer

I will compile Test1.java and decompile it, the source code will become:

 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); } } 

Since the returned object resp.getOutputStream() is javax.servlet.ServletOutputStream . The JVM still needs to check this class with a subclass of java.io.OutputStream before it is completed at run time, so it tries to load ServletOutputStream.class . But the JVM cannot find it and throw a ClassNotFoundException .

+2
source

It makes great sense to me. Test3 refers to Test2 and Test1, and both require javax.servlet-api-3.0.1.jar in the classpath at runtime.

0
source

All Articles