Почему NoClassDefFoundError вызвано сбоем инициализации статического поля?
Вот интересный вопрос на 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();
}
}
}
Ответы
Ответ 1
Ответ на такие вопросы обычно похоронен где-то в спецификациях... (§12.4.2)
Что происходит, когда классы инициализируются:
Шаги 1-4 несколько не связаны с этим вопросом. Шаг 5 - вот что вызывает исключение:
5
. Если объект класса находится в ошибочном состоянии,, то инициализация невозможна. Отпустите блокировку объекта Class и выбросите NoClassDefFoundError.
6-8 продолжить инициализацию, 8 выполняет инициализаторы, и что обычно происходит на шаге 9:
9
. Если выполнение инициализаторов выполняется нормально, то заблокируйте этот объект класса, пометьте его полностью инициализированным, уведомите все ожидающие потоки, отпустите блокировку и выполните эту процедуру как обычно.
Но мы получили ошибку в инициализаторе, поэтому:
10
. Иначе инициализаторы должны завершиться внезапно, выбросив некоторое исключение E. Если класс E не является ошибкой или одним из его подклассов, то создать новый экземпляр класса ExceptionInInitializerError, с E в качестве аргумента и использовать этот объект вместо E на следующем шаге. Но если новый экземпляр ExceptionInInitializerError не может быть создан из-за возникновения OutOfMemoryError, вместо этого вместо него следует использовать объект OutOfMemoryError вместо E на следующем шаге.
Да, мы видим ExceptionInInitializerError
b/c исключения нулевого указателя.
11
. Заблокируйте объект класса, отметьте его ошибочным, уведомите все ожидающие потоки, отпустите блокировку и выполните эту процедуру внезапно с причиной E или ее заменой, как определено на предыдущем шаге. (Из-за недостатка в некоторых ранних реализациях исключение во время инициализации класса игнорировалось, а не вызывало исключение ExceptionInInitializerError, как описано здесь.)
И тогда класс помечен как ошибочный, поэтому мы получаем исключение из шага 5 во второй раз.
Удивительная часть - это третья распечатка, которая показывает, что TestClass.class
в MainClass
фактически содержит ссылку на физический объект Class
.
Вероятно, поскольку TestClass
все еще существует, он просто помечен как ошибочный. Он уже загружен и проверен.
Ответ 2
Да, обычно это почему NoClassDefFoundError
. Это злобно названо, что все. Он должен был быть назван как "исключение с ошибкой класса init" или что-то еще.
Бесполезно вводить вводящее в заблуждение имя, программисты java, которые получили эту ошибку, потратили сотни человеко-лет, пытаясь понять, почему класс не может быть найден.
Всякий раз, когда вы видите это исключение, вы должны проверить журнал вверх и попытаться выяснить основную причину, когда класс не смог инициализировать.
Ответ 3
Когда я получаю доступ к такому статическому полю, будет создан NoClassDefFoundError. кажется, что VM относится к классу не полностью.
Это правильно...
Но когда я обращаюсь к Класу, он все еще доступен
Да.
загрузчик классов не пытался удалить сломанный класс, потому что:
- было бы трудно сделать,
- было бы очень сложно сделать безопасно,
- он оставил бы JVM в состоянии, когда приложение могло легко тратить много времени на повторную загрузку и перезагрузку сломанного кода, а
- спецификации говорят (или, по крайней мере, подразумевают), что это не должно; см. другие ответы для деталей.
Чтобы войти в состояние видимости этого несоответствия, ваше приложение должно поймать ClassDefNotFoundError
(или суперкласс) и попытаться восстановить его. Это хорошо документированный факт, что исключения Error
обычно не подлежат восстановлению; т.е. если вы попытаетесь восстановить, JVM может оказаться в несогласованном состоянии. Вот что произошло здесь... по отношению к классам, которые были загружены/инициализированы.
Ответ 4
он ограничен
8.3.2.2 http://psc.informatik.uni-jena.de/languages/Java/javaspec-3.pdf