Environment: jdk1.7
javax.servlet-api-3.0.1.jar
is needed for this test.
Reproduce steps:
javac Test1.java -cp javax.servlet-api-3.0.1.jar
build Test1.java with javax.servlet-api-3.0.1.jar
javac Test2.java -cp javax.servlet-api-3.0.1.jar
build Test2.java with 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 dependency. Following is the output. It's OK here.
hello world1
hello world2
But when this command java Test3
run, Exception is thrown. The result at the end of this post. The weird thing is that "hello world1" could be printed out, but instead of printing out "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");
}
}
output of the final step. Test2.hello("world2") throw 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 with the Exception. Because I didn't use any code in the Class ServletOutputStream. And the difference of the Test1 and Test2 is only a try
blocked.
This Question is not a duplicated question as it marked. Because JVM should not throw an Exception when a try
block get involved.
After compare the output of javap -v -c Test?
, I get the answer.
The javac
create StackMapTable attribute for try/catch
block of Test2.java
.
If StackMapTable
is found on class loading, JVM will perform bytecode verification and check referenced class. That's why it throw java.lang.NoClassDefFoundError
.
https://stackoverflow.com/a/25110513 describe more detail about StackMapTable
.
I compile the Test1.java and decompile it, the source code 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 return object of resp.getOutputStream()
is javax.servlet.ServletOutputStream
. JVM still have to verify this class is sub-class of java.io.OutputStream
before case it at runtime, so it try to load ServletOutputStream.class
. But JVM cannot find it and throw ClassNotFoundException
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With