Почему NoClassDefFoundError вызван статической ошибкой инициализации поля? - PullRequest
33 голосов
/ 15 июня 2011

Вот интересный вопрос Java.

следующая простая Java-программа содержит статическое поле, статически инициализированное методом. На самом деле, я вынуждаю метод, который вычисляет значение intiailize, вызывать исключение NullPointException. Когда я получаю доступ к такому статическому полю, возникает NoClassDefFoundError. Похоже, что VM относится к классу не завершено.

Но когда я получаю доступ к Классу, он все еще доступен;

Кто-нибудь знает почему?

class TestClass {
    public static TestClass instance = init();

    public static TestClass init() {
       String a = null;
       a.charAt(0); //force a null point exception;
       return new TestClass();
    }
}

class MainClass {
    static public void main(String[] args) {
       accessStatic(); // a ExceptionInInitializerError raised cause by NullPointer
       accessStatic(); //now a NoClassDefFoundError occurs;

       // But the class of TestClass is still available; why?
       System.out.println("TestClass.class=" + TestClass.class);
    }

    static void accessStatic() {
        TestClass a;

        try {
            a = TestClass.instance; 
        } catch(Throwable e) {
            e.printStackTrace();
        }
    }   
}

Ответы [ 4 ]

34 голосов
/ 15 июня 2011

Ответ на такие вопросы обычно находится где-то в спецификации ... (§12.4.2)

Что происходит при инициализации классов:

Шаги 1-4 несколько не связаны с этим вопросом. Шаг 5 вот что вызывает исключение:

5. Если объект Class находится в ошибочном состоянии, , тогда инициализация невозможна. Освободите блокировку объекта Class и сгенерируйте NoClassDefFoundError.

6-8 продолжить инициализацию, 8 выполняет инициализаторы, и то, что обычно происходит, находится на шаге 9:

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

Но мы получили ошибку в инициализаторе так:

10. В противном случае инициализаторы должны были завершиться внезапно, выдав некоторое исключение E. Если класс E не является Error или одним из его подклассов, тогда создаст новый экземпляр класса ExceptionInInitializerError, с E как аргумент и используйте этот объект вместо E на следующем шаге. Но если новый экземпляр ExceptionInInitializerError не может быть создан из-за возникновения OutOfMemoryError, вместо этого используйте объект OutOfMemoryError вместо E на следующем шаге.

Да, мы видим ExceptionInInitializerError b / c исключения нулевого указателя.

11. Заблокируйте объект Class, отметьте его как ошибочный, уведомите все ожидающие потоки, снимите блокировку и завершите эту процедуру внезапно по причине E или ее замене, как определено в предыдущем шаге. (Из-за недостатка в некоторых ранних реализациях исключение во время инициализации класса игнорировалось, а не вызывало ExceptionInInitializerError, как описано здесь.)

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


Удивительной частью является третья распечатка, которая показывает, что TestClass.class в MainClass фактически содержит ссылку на физический Class объект.

Возможно, потому что TestClass все еще существует, он просто помечен как ошибочный. Он уже загружен и проверен.

11 голосов
/ 15 июня 2011

Да, именно поэтому обычно NoClassDefFoundError повышается. Это злобно по имени, вот и все. Он должен был быть назван как «исключение исключения класса init» или что-то в этом роде.

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

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

3 голосов
/ 15 июня 2011

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

Это правильно ...

Но когда я получаю доступ к классу, он все еще доступен

Да.

Загрузчик классов не пытался удалить сломанный класс, потому что:

  • это будет трудно сделать,
  • это будет чрезвычайно трудно сделать безопасно ,
  • это оставит JVM в состоянии, когда приложение может легко тратить много времени на повторную загрузку и перезагрузку неработающего кода, и
  • спецификации говорят (или, по крайней мере, подразумевают), что не должны;подробности см. в других ответах.

Чтобы попасть в состояние, в котором эта несогласованность видна, ваше приложение должно перехватить ClassDefNotFoundError (или суперкласс) и попытаться восстановить его.Это хорошо задокументированный факт, что Error исключения, как правило, не подлежат восстановлению;т. е. если вы попытаетесь восстановиться, JVM может оказаться в несовместимом состоянии.Вот что здесь произошло ... в отношении классов, которые загружались / инициализировались.

0 голосов
/ 15 июня 2011
...