Почему Java не позволяет выбросить проверенное исключение из статического блока инициализации?

Почему Java не позволяет выбросить проверенное исключение из статического блока инициализации? Какова была причина этого проектного решения?

7 ответов


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

поскольку вы не можете обрабатывать ошибки, указанные проверенным исключением, было решено запретить выбрасывание статических блоков проверенных исключений.

статический блок не должен бросить проверил исключения, но по-прежнему позволяет создавать непроверенные/runtime-исключения. Но по вышеуказанным причинам вы также не сможете справиться с ними.

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


вы можете обойти проблему, поймав любое проверенное исключение и перестроив его как непроверенное исключение. Этот непроверенный класс исключений хорошо работает как оболочка:java.lang.ExceptionInInitializerError.

пример кода:

protected static class _YieldCurveConfigHelperSingleton {

    public static YieldCurveConfigHelper _staticInstance;

    static {
        try {
            _staticInstance = new YieldCurveConfigHelper();
        }
        catch (IOException | SAXException | JAXBException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
}

Это должно было бы выглядеть так (это не действительный код Java)

// Not a valid Java Code
static throws SomeCheckedException {
  throw new SomeCheckedException();
}

но как бы объявление где ты его поймал? Проверенные исключения требуют перехвата. Представьте себе некоторые примеры, которые могут инициализировать класс (или не могут, потому что он уже инициализирован), и просто для того, чтобы привлечь внимание к сложности, которую он представит, я помещаю примеры в другой статический инициализатор:

static {
  try {
     ClassA a = new ClassA();
     Class<ClassB> clazz = Class.forName(ClassB.class);
     String something = ClassC.SOME_STATIC_FIELD;
  } catch (Exception oops) {
     // anybody knows which type might occur?
  }
}

и еще одна неприятная вещь -

interface MyInterface {
  final static ClassA a = new ClassA();
}

представьте, что ClassA имел статический инициализатор, бросающий проверенное исключение: в этом случае MyInterface (который является интерфейсом со "скрытым" статическим инициализатором) должен был бы бросить исключение или обработать его - обработка исключений на интерфейсе? Лучше оставить все как есть.


посмотри Спецификации Языка Java: утверждается, что это ошибка времени компиляции, если статический инициализатор не способен завершить резко С проверенным исключением.


почему Java не позволяет выбросить проверенное исключение из статического блока инициализации?

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

технически также возможно разрешить непроверенному исключению распространяться из статического блока инициализатора1. Но это действительно плохо идея сделать это сознательно! Проблема в том, что JVM сам ловит непроверенное исключение и обертывает его и переосмысливает как ExceptionInInitializerError.

NB: это Error не регулярное исключение. Вы не должны пытаться оправиться от этого.

в большинстве случаев, исключение не может быть перехвачено:

public class Test {
    static {
        int i = 1;
        if (i == 1) {
            throw new RuntimeException("Bang!");
        }
    }

    public static void main(String[] args) {
        try {
            // stuff
        } catch (Throwable ex) {
            // This won't be executed.
            System.out.println("Caught " + ex);
        }
    }
}

$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
    at Test.<clinit>(Test.java:5)

вы нигде не можете разместить try ... catch в выше, чтобы поймать ExceptionInInitializerError2.

в некоторых случаях вы можете поймать он. Например, если вы запустили инициализацию класса, вызвав Class.forName(...) вы можете заключить вызов в try и поймать либо ExceptionInInitializerError и в последующем NoClassDefFoundError.

однако, если вы попытаетесь восстановить С ExceptionInInitializerError вы можете столкнуться с блокпостом. Проблема в том, что перед тем, как выбросить ошибку, JVM отмечает класс, который вызвал проблему, как "failed". Вы просто не сможете его использовать. Кроме того, любые другие классы, которые зависят от при попытке инициализации класс failed также переходит в состояние failed. Единственный путь вперед-выгрузить все неудачные классы. Это может быть выполнимым для динамически загружаемого кода3, но в целом это не так.

1 - это ошибка компиляции, если статический блок безоговорочно бросает исключение непроверенное.

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

3-Если вы хотите восстановить неудачные классы, вам нужно будет избавиться от загрузчика классов, который их загрузил.


какова была причина этого проектного решения?

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

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


OK, так что вы должны делать, если ваш код "нуждается" в исключениях в статическом инициализаторе. В основном, есть два альтернативы:

  1. Если (полной!) восстановление из исключения в блоке возможно, затем сделайте это.

  2. в противном случае реструктурируйте код, чтобы инициализация не происходила в статическом блоке инициализации (или в инициализаторах статических переменных).


поскольку ни один код, который вы пишете, не может вызвать статический блок инициализации, не полезно бросать checked exceptions. Если бы это было возможно, что бы делал jvm, когда генерируются проверенные исключения? Runtimeexceptions распространяются вверх.


Я также могу скомпилировать выбрасывание проверенного исключения....

static {
    try {
        throw new IOException();
    } catch (Exception e) {
         // Do Something
    }
}