If a NoClassDefFoundError is caused by a ClassNotFoundException, why does Java expect you to catch both throwables?

J Smith picture J Smith · Apr 2, 2013 · Viewed 7.9k times · Source

When I run this code the app exits with a ClassNotFoundException:

//uncaught ClassNotFoundException
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (NoClassDefFoundError e)
{
}

When I attempt to compile this code, the compiler complains that the ClassNotFoundException is not reachable because it is not thrown from within the try-clause of the try-catch statement.

//Won't compile
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (ClassNotFoundException e)
{
}

When I run this code, the only throwable that is caught is a NoClassDefFoundError.

//catches throwable of type java.lang.NoClassDefFoundError,
//with a java.lang.ClassNotFoundException as its cause
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
}
catch (Throwable e)
{
    System.out.println(e.getClass().getName());
    System.out.println(e.getCause().getClass().getName());
}

The following code will compile and catch the error (and only the error), but it's clumsy:

//possible workaround
try
{
    Class<?> clazz = defineClass(null, bytes, 0, bytes.length, null);
    table.put(clazz.getName(), clazz);
    if (1 == 0) throw new ClassNotFoundException(); // we want the code to compile
}
catch (ClassNotFoundException e)
{
    System.out.println("ex");
}
catch (NoClassDefFoundError e)
{
    System.out.println("err");
}

And yet when I write the following, I can get away without a catch clause for the cause of the error:

//and yet this works just fine...
try
{
    throw new Error(new IOException());
}
catch (Error e)
{
    System.out.println("err");
}

Example 3 would lead me to conclude that the throwable was a NoClassDefFoundError. Example 1 would lead me to conclude that the throwable was a ClassNotFoundException. And yet, Example 2 shows that java won't even let me write code to properly catch the ClassNotFoundException.

Just when I was about to conclude that the problem here is the error-caused-by-an-exception, I ran the code shown in the previous example which shows that that is not the rule.

Can someone please explain what's going on here?

PS: this is the stack trace:

 java.lang.NoClassDefFoundError: com/my/pckage/MyClass
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at Main$MyClassLoader.getClasses(Main.java:78)
at Main.main(Main.java:109)
 Caused by: java.lang.ClassNotFoundException: com.my.pckage.MyClass
at java.lang.ClassLoader.findClass(ClassLoader.java:522)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
... 4 more

Answer

Perception picture Perception · Apr 2, 2013

So, you are misunderstanding your stack trace.

java.lang.NoClassDefFoundError: com/my/package/MyClass
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at Main$MyClassLoader.getClasses(Main.java:78)
    at Main.main(Main.java:109)
Caused by: java.lang.ClassNotFoundException: com.my.package.MyClass

Your code is generating a NoClassDefFoundError. The underlying cause is a ClassNotFoundException. Remember that cause is a property of the Throwable class, and that when printing stacktraces, Java will display information both on the direct exception and its underlying cause(s). It's tougher to say why the define method is failing internally, but one thing is for sure - you cannot use the keyword package in a package name.