Playing with Java (v9 specifically) I found this situation:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
interface A {
static A staticMethod() {
try {
Method method = A.class.getDeclaredMethods()[0];
return (A) method.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class Test {
public static void main(String[] args) {
A.staticMethod();
}
}
That program flow should cause a StackOverflow error, however, I'm getting a NoClassDefFoundError
.
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
Exception in thread "main"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "main"
According to Javadoc
Class NoClassDefFoundError
Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.
UPDATE: Bug Report Id: 9052375
Executed from the command line and prints the expected error:
The problem was, the exceptions used in catch
.
You can fix NoClassDefFoundError error by checking following: Check the exception stack trace to know exactly which class throw the error and which is the class not found by java.
NoClassDefFoundError, which means the Class Loader file responsible for dynamically loading classes can not find the . class file. So to remove this error, you should set your classpath to the location where your Class Loader is present. Hope it helps!!
Both ClassNotFoundException and NoClassDefFoundError are the errors when JVM or ClassLoader not able to find appropriate class while loading at run-time. ClassNotFoundException is a checked exception and NoClassDefFoundError is an Error which comes under unchecked.
This is not a bug and it also has nothing to do with static methods in interfaces.
The java.lang.instrument ASSERTION FAILED
message is also not relevant and is just an artifact of running the code from IDE. Running the same class from the command line will result in Exception in thread "main"
only.
Lets simplify your example to
public class Test {
public static void main( String[] args ) throws Exception {
recursive();
}
public static void recursive() throws Exception {
try {
Test.class
.getDeclaredMethod( "recursive" )
.invoke( null );
} catch ( InvocationTargetException e ) {
e.printStackTrace();
}
}
}
What is going on:
StackOverflowError
, as expected.StackOverflowError
is wrapped into InvocationTargetException
which is thrown from the deepest nested call to method.invoke()
.InvocationTargetException
is immediately caught and JVM tries to execute printStackTrace()
but in order to do that it needs to load some classes. But remember that at this point the stack is depleted and any non-trivial methods will hit StackOverflowError
again, which is exactly what happens somewhere inside the class loader when it tries to load some class required to print a stack trace. The class loader did found the class, but failed to load and initialize it, and it reports that as NoClassDefFoundError
.The following code will prove that InvocationTargetException
indeed wraps StackOverflowError
:
public class Test {
public static void main( String[] args ) throws Exception {
recursive();
}
public static void recursive() throws Exception {
try {
Test.class
.getDeclaredMethod( "recursive" )
.invoke( null );
} catch ( InvocationTargetException e ) {
System.out.println(e);
System.out.println(e.getTargetException());
}
}
}
And the following code will prove that if classes required to execute printStackTrace()
are already loaded, then the code behaves as expected (prints a stack trace for InvocationTargetException
caused by StackOverflowError
:
public class Test {
public static void main( String[] args ) throws Exception {
new Exception().printStackTrace(); // initialize all required classes
recursive();
}
public static void recursive() throws Exception {
try {
Test.class
.getDeclaredMethod( "recursive" )
.invoke( null );
} catch ( InvocationTargetException e ) {
e.printStackTrace();
}
}
}
The open question is why reflection API handlesStackOverflowError
at all, instead of simply terminating the whole call chain with the error.
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