JDK 1.7 Throwable `addSuppressed ()` метод - PullRequest
13 голосов
/ 20 января 2012

Ну, я прохожу связанные вопросы, я читаю исходный код 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(), чтобы правильно обработать это исключение во внешнем блоке.

Это ситуация до 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);
    }

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

Ответы [ 2 ]

15 голосов
/ 21 января 2012

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

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

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

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

Поведение по умолчанию в блоке try-finally,где у нас есть 2 исключения, то исходное исключение подавлено , и мы видим только исключение из блока finally.Если мы используем блок 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 параллельно в некотором смысле, обычно от блока try и от блока finally.Старая семантика try-catch будет возвращать исключение из блока finally, тогда как исключено исключение из блока try (или повторно выбрасывать исключение из блока catch).Новая функция try-with-resource позволяет получить оба исключения.Более того, вы получите исходное исключение, где исключение из блока finally будет подавлено

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

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

1 голос
/ 20 января 2012

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

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

...