Подтвердить что ты не робот

JDK 1.7 Метод Throwable `addSuppressed()`

Ну, я получаю связанные вопросы, я прочитал исходный код JDK 1.7, но я не нашел ответа.

В этом вопросе я хочу полностью игнорировать fillInStackTrace.

По методу JDK 1.4 initCause(). Например, когда вы используете отражение ядра для вызова метода, вы получаете InvocationTargetException с причиной, в которой есть целевое исключение.

Когда я увидел эту функцию, я начал использовать ее также в таком сценарии

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        throw new RuntimeException(e);
    }

Итак, я поймаю исключение, я не готов разбираться с ним здесь, и я реорганизую новое исключение, где у меня есть исходное исключение в качестве причины. В некоторых сценариях не RuntimeException, но мое пользовательское исключение используется, поэтому иногда я также вызываю e.getCause(), чтобы правильно обработать это исключение во внешнем блоке.

Это ситуация в pre JDK 1.7. Почему и когда я должен использовать addSuppressed()? Должен ли я изменить приведенный выше код на

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        RuntimeException re= new RuntimeException(e.getMessage());
        re.addSuppressed(e);
        throw re;
    }

И как бонусный вопрос, почему addSuppressed() не возвращает Throwable как initCause(), чтобы разрешить throw (RuntimeException)new RuntimeException().initCause(e);? Например, почему я не могу сделать?:

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        throw (RuntimeException)new RuntimeException(e.getMessage()).addSuppressed(e);
    }

Я извлек ответ на отдельный пост.

4b9b3361

Ответ 1

В общем, метод Throwable addSuppressed() должен использоваться, когда каким-то образом мы выполняем параллельное выполнение, которое может создавать исключение, которое подавляется. Я нашел 2 примера;

  • Блок try-with-resource (блок try-finally), когда вызывающий код увидит исходное исключение (в блоке try или catch) и исключение, которое произошло в блоке finally.

  • пакетные задания (массовые операции), когда мы должны перейти к следующему элементу независимо от того, выполнена ли операция над текущим элементом или нет

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

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

Начиная с версии 7, платформа поддерживает понятие исключенных исключений (в сочетании с оператором try-with-resources). Любые исключения, которые были подавлены для предоставления исключения, распечатываются под трассировкой стека.

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#printStackTrace%28%29

Сначала нужно прочитать о новой функции try-with-resource. Здесь вы можете прочитать здесь http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/ или здесь Что такое Java 7 try-with -ресурсы эквивалент байт-кода с помощью try-catch-finally?. Короче говоря, вы можете иметь 2 Throwable параллельно в каком-то смысле, как правило, от вас, попробуйте блок и ваш блок finally. Старая семантика try-catch вернет исключение из блока finally whull suppressed исключение из блока try (или реорганизация исключения из блока catch). Новая функция try-with-resource позволяет получить оба исключения. Более того, вы получите исходное исключение, если исключение из блока finally будет подавлено

Обратите внимание, что когда одно исключение вызывает другое исключение, первое исключение обычно улавливается, а затем возникает второе исключение. Другими словами, существует каузальная связь между двумя исключениями. Напротив, бывают ситуации, когда два независимых исключения могут быть выбраны в блоках кода для сестер, в частности в блоке try оператора try-with-resources и компиляторе, сгенерированном окончательным блоком, который закрывает ресурс. В этих ситуациях можно распространять только одно из заброшенных исключений. В предложении try-with-resources, когда есть два таких исключения, исключение, происходящее из блока try, распространяется, и исключение из блока finally добавляется в список исключений, исключенных из блока try. В качестве исключения разворачивает стек, он может накапливать несколько подавленных исключений.

Пример:

    public class TestClass {
static class ResourceA implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceA read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceA close exception");
      }
    };

static class ResourceB implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceB read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceB close exception");
      }
    };

    //a test method
    public static void test() throws Exception{
      try (ResourceA a = new ResourceA();
           //ResourceB b = new ResourceB()
              ) {
        a.read();
        //b.read();
      } catch (Exception e) {
        throw e;
      }
    }

    public static void main(String[] args)  throws Exception {
        test();
    }

}

Вывод будет следующим:

Exception in thread "main" java.lang.Exception: ResourceA read exception
at TestClass$ResourceA.read(TestClass.java:6)
at TestClass.test(TestClass.java:29)
at TestClass.main(TestClass.java:39)
Suppressed: java.lang.Exception: ResourceA close exception
    at TestClass$ResourceA.close(TestClass.java:10)
    at TestClass.test(TestClass.java:31)
    ... 1 more

пакетные задания (массовые операции). Ну, я нашел некоторое использование этого метода вне try-with-resources. Ниже приведен исходный код из java.net.URLClassLoader.close

 public void close() throws IOException {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkPermission(new RuntimePermission("closeClassLoader"));
    }
    List<IOException> errors = ucp.closeLoaders();
    // now close any remaining streams.
    synchronized (closeables) {
        Set<Closeable> keys = closeables.keySet();
        for (Closeable c : keys) {
            try {
                c.close();
            } catch (IOException ioex) {
                errors.add(ioex);
            }
        }
        closeables.clear();
    }
    if (errors.isEmpty()) {
        return;
    }
    IOException firstex = errors.remove(0);
    // Suppress any remaining exceptions
    for (IOException error: errors) {
        **firstex.addSuppressed(error);**
    }
    throw firstex;
}

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

Ответ 2

Подавленные исключения будут сохранены, если код, выполняемый в блоке finally, выдает исключение. Это исключение, которое случилось, что вам, вероятно, все равно. В Java 6 такое исключение в блоке finally станет единственным исключением, которое ваш код вызова увидит, но с новым блоком try-in-resource ваш код вызова увидит исходное исключение, а исключение, которое произошло в виртуальном блоке finally, будет в getSuppressed().

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