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

5

17

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
Leastwise answered 2/4, 2013 at 1:39 Comment(9)
Something doesn't add up. Presumably, you are writing a custom class loader, please include any overriden methods you may have defined for it.Overthrust
defineClass does not throw ClassNotFoundException. If you're getting that exception it's coming from somewhere else.Twink
@j-smith It is better if u can give a sscce to show the problem. There are quite some possibilities for the unmatched exception. For example, the "final" exception cause the app to exit may be thrown by some outer level, for which it catch your NoClassDefFoundError and rethrow with a ClassNotFoundException. It is better if you can inspect the "cause" of the exception, and the call stack in the exception. It give you idea on where the exception is actually originated.Evolution
@AdrianShum If you catch an Exception and then get the stack trace of the exception, that is a perfectly viable solution.Ogre
@Ogre For debugging, otherwise you're not actually solving anything.Proust
do you really have package as a package name?Upbraid
@zhong.j.yu - Yeah, my guess is that there is something amiss with the naming. And I suspect the above ex trace has been "sanitized", so we can't tell if there are some subtleties in the naming that are mucking things up.Twink
JSmith -- In real life, are the two class names in the exception message identical (other than . <=> /) as you have shown, or are they somehow different?Twink
(Hint: The class you're trying to load -- put it into a .class file and see if you can load it "normally". Presumably the reason for your error will become obvious.)Twink
O
12

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.

Overthrust answered 2/4, 2013 at 3:10 Comment(3)
The fact that the code is generating a NoClassDefFoundError with an underlying cause of ClassNotFoundException does not answer the question. I also confirmed that this unusual behavior persists even with a conventional package name.Leastwise
@JSmith - please post your code that shows a ClassNotFoundException being thrown from a defineClass method being called, yet not allowing an explicit catch block.Overthrust
@JSmith Perception's answer answers the question very well: The "underlying cause" ClassNotFoundException is wrapped by the NoClassDefFoundError and therefore wont be caught by catch(ClassNotFoundException e).Daria
T
7

NoClassDefFoundError occurs when the .class for a class is found, but the class cannot be constructed from that .class.

There are several different scenarios that occur commonly, plus a few more obscure ones.

  • The .class file contains a name (and package) that does not match the class file name/package
  • A class that was needed to verify and initialize the class could not be found
  • An error occurred during class initialization

In most of these scenarios there is another error or exception that occurs earlier, is caught by the class loader, and the new error is signaled.

It's not clear exactly which scenario is occurring in the above exception traceback, but I'm guessing some sort of name mismatch.

Twink answered 2/4, 2013 at 2:41 Comment(0)
C
2

NoClassDefFoundError is actually a subclass of Error and these should not be caught. See the docs of Error for details. The important note below:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur.

For this reason, I think you should take a closer look at your code to see what you are doing wrong.

Cocksure answered 2/4, 2013 at 2:2 Comment(0)
K
0
//and yet this works just fine...
try
{
    throw new Error(new IOException());
}
catch (Error e)
{
    System.out.println("err");
}

Replace it with:

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

Try a e.printStackTrace() and you'll see a similar output.

Kelvinkelwen answered 17/9, 2013 at 14:22 Comment(0)
C
-1

As has been pointed out already, Java won't allow you to handle errors.

Anyway, I'm not really sure what your reasons are for trying to work around these exceptions (and errors) but these are occurrences that programmers don't really need to worry about (most of the time). To me this is a symptom that there is a problem elsewhere in your code/project. If the system throws a ClassNotFoundException, and it allows you to catch it, how would you handle it? Or would you rather address the actual problem that a certain class required by your application is not in the classpath?

Also, you may want to check the difference between NoClassDefFoundError and ClassNotFoundException to better address your problem.

Cluster answered 2/4, 2013 at 2:15 Comment(3)
Yes, you definitely can "handle" errors, from the purely mechanical standpoint of catching them. In general, though, you may not be able to cleanly recover from them.Twink
@JSmith - In general, reasonable applications wouldn't handle errors as well as unchecked exceptions. Rather, they would enforce such conditions/mechanisms as they would prevent such errors or exceptions from being thrown, hence, there is no question of handling them. I would say they should be prevented (by the application developers) before they're thrown.Cornish
Yeah, I shouldn't have said 'Java won't allow' as if it's going to complain about it because try-catch-finally was designed around Throwables and not just Exceptions. It's more of you can but you should not.Cluster

© 2022 - 2024 — McMap. All rights reserved.