Если NoClassDefFoundError вызван ClassNotFoundException, почему Java ожидает, что вы поймаете оба комбайна?

Когда я запускаю этот код, приложение выходит из ClassNotFoundException:

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

Когда я пытаюсь скомпилировать этот код, компилятор жалуется, что исключение ClassNotFoundException недоступно, потому что оно не выбрано из try-предложения инструкции try-catch.

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

Когда я запускаю этот код, единственное, что можно поймать, это 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());
}

Следующий код скомпилирует и поймает ошибку (и только ошибку), но это неуклюже:

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

И все же, когда я пишу следующее, я могу уйти без предложения catch для причины ошибки:

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

Пример 3 привел бы к заключению, что throwable был NoClassDefFoundError. Пример 1 привел меня к выводу, что throwable является ClassNotFoundException. И все же, пример 2 показывает, что java даже не позволяет мне писать код, чтобы правильно поймать ClassNotFoundException.

Просто, когда я собирался сделать вывод о том, что проблема заключается в ошибке, вызванной ошибкой, я запустил код, показанный в предыдущем примере, который показывает, что это не правило.

Может кто-нибудь объяснить, что здесь происходит?

PS: это трассировка стека:

 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

Ответы

Ответ 1

Итак, вы неправильно понимаете свою трассировку стека.

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

Ваш код генерирует NoClassDefFoundError. основная причина - ClassNotFoundException. Помните, что cause является свойством класса Throwable и что при печати stacktraces Java отображает информацию как по прямому исключению, так и по его основной причине. Сложнее сказать, почему метод define терпит неудачу внутри, но одно можно сказать наверняка - вы не можете использовать ключевое слово package в имени пакета.

Ответ 2

NoClassDefFoundError возникает, когда класс .class для класса найден, но класс не может быть создан из этого .class.

Существует несколько различных сценариев, которые обычно встречаются, плюс несколько более неясных.

  • Файл .class содержит имя (и пакет), которое не соответствует имени/пакету файла класса
  • Не удалось найти класс, необходимый для проверки и инициализации класса.
  • Произошла ошибка во время инициализации класса

В большинстве этих сценариев есть еще одна ошибка или исключение, которое происходит раньше, улавливается загрузчиком классов, и появляется новая ошибка.

Непонятно, какой именно сценарий происходит в предыдущей трассировке исключения, но я угадываю, что такое несоответствие имени.

Ответ 3

NoClassDefFoundError на самом деле является подклассом Error, и они не должны быть пойманы. Подробнее см. docs of error. Важное примечание ниже:

Ошибка - это подкласс Throwable, который указывает на серьезные проблемы что разумное приложение не должно пытаться поймать. Большинство таких ошибки являются ненормальными условиями.

Не требуется метод объявлять в своей брошюре пункт any подклассы ошибки, которые могут быть выброшены во время выполнения но не пойманы, поскольку эти ошибки являются ненормальными условиями, которые никогда не должно происходить.

По этой причине, я думаю, вы должны более внимательно посмотреть на свой код, чтобы увидеть, что вы делаете неправильно.

Ответ 4

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

Замените его:

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

Попробуйте e.printStackTrace(), и вы увидите аналогичный вывод.

Ответ 5

Как уже указывалось, Java не позволит вам обрабатывать ошибки.

Во всяком случае, я не совсем уверен, в чем ваши причины пытаются обойти эти исключения (и ошибки), но это те случаи, о которых программистам действительно не нужно беспокоиться (в большинстве случаев). Для меня это симптом, что в вашем коде/проекте есть проблема. Если система выбрасывает ClassNotFoundException, и это позволяет вам ее поймать, как бы вы справились? Или вы скорее рассмотрите фактическую проблему, что определенный класс, требуемый вашим приложением, не находится в пути к классам?

Кроме того, вы можете проверить разницу между NoClassDefFoundError и ClassNotFoundException, чтобы лучше решить вашу проблему.