Що таке пригнічений виняток?


76

У коментарі (користувачем soc ) до відповіді на запитання про оптимізацію хвостових викликів згадувалося, що Java 7 має нову функцію, яка називається "пригнічені винятки", через "додавання ARM" (підтримка процесорів ARM?).

Що таке "придушений виняток" у цьому контексті? В інших контекстах "придушений виняток" буде винятком, який було спіймано, а потім проігноровано (рідко хороша ідея); це явно щось інше.


Я не бачу жодної згадки про це в описі "Удосконалення мови програмування Java" download.oracle.com/javase/7/docs/technotes/guides/language/…
Raedwald,

18
ARM означає автоматичне управління ресурсами, наприклад, infoq.com/news/2010/08/arm-blocks
daniel kullmann

5
У цьому контексті ARM - це стара назва спроби з ресурсами. Вони перестали використовувати ARM і почали використовувати спробу з ресурсами десь до того, як Java 7 надійшла. І @danielkullmann має рацію в тому, що означає ARM
Бред Купіт,

Відповіді:


55

Я вважаю, що коментатор, на який посилається, - це виняток, який напівігнорується, коли він потрапляє в неявний finallyблок блоку try-with-resources , в контексті існуючого винятку, що викидається з tryблоку:

Виняток може бути виведено з блоку коду, пов'язаного з інструкцією try-with-resources. У прикладі writeToFileZipFileContents може бути викинуто виняток із блоку try, а до оператора try-with-resources - до двох винятків, коли він намагається закрити об'єкти ZipFile та BufferedWriter. Якщо виключення викидається з блоку try і одне або кілька винятків викидаються з оператора try-with-resources, тоді ці винятки, викинуті з оператора try-with-resources, пригнічуються, і виняток, який створює блок, є тим що викидається методом writeToFileZipFileContents. Ви можете отримати ці приглушені винятки, викликавши метод Throwable.getSuppressed з винятку, викликаного блоком try.

(Це посилання на розділ під назвою "Пригнічені винятки" на пов'язаній сторінці.)


2
Відповідний виклик API: download.oracle.com/javase/7/docs/api/java/lang/…
Raedwald,

@Raedwald: В основному так, я думаю.
Джон Скіт,

6
@JonSkeet @Raedwald: Я вважаю, що ця відповідь не враховує, що придушені винятки існували до Java 7 (я вже не кажу про ігноровані винятки): якщо finallyблок викидає виняток, коли tryблок також видає виняток, оригінальний виняток із tryблок втрачено або "придушено" (див. http://accu.org/index.php/journals/236 для отримання додаткової інформації). Java 7 щойно додала зручний метод для зберігання винятку з finallyблоку, оскільки finallyнеявно генерується методом try-with-resources.
JBert

5
Але один момент, на який слід звернути увагу, полягає в тому, що якщо ми явно передбачаємо нарешті блок у операторі try-with-resource і викидаємо з нього виняток, він має перевагу над винятками, викинутими з блоку try або try-with-resource.
Aniket Thakur

63

Щоб пояснити цитату у відповіді Джона, методом може бути створено лише одне виняток (за кожне виконання), але у випадку a try-with-resourcesможливе викидання декількох винятків. Наприклад, один може бути кинутий у блок, а інший може бути викинутий із неявного, finallyнаданого try-with-resources.

Компілятор повинен визначити, кого з них "справді" кинути. Він вирішує кинути виняток, викликаний явним кодом (код у tryблоці), а не виклик неявного коду ( finallyблоку). Тому виключення, викинуті в неявному блоці, придушуються (ігноруються). Це відбувається лише у випадку кількох винятків.


1
Таким чином, пригнічені винятками є наслідком нової функції , що означає , що ми більше не потрібно написати громіздкий try... finallyМорози при відкритті і close()Інги файлів: stackoverflow.com/questions/3305405 / ...
Raedwald

@JohnB Але у нас є варіант конструктора Exception (anotherExcetion e) rit?
Канагавелу Сугумар

3
@KanagaveluSugumar Exception(Exception cause)Конструктор використовується, щоб обернути викликаючий виняток в інший, який може бути більш описовим. Однак у цьому випадку ми говоримо про два окремі винятки, для яких не існує причинно-наслідкових зв'язків. Виняток A не спричинив виняток B, тому не було б сенсу загортати одне в інше. Крім того, ви припускаєте, що кодер явно видає друге виняток і має доступ до першого. Цього не було б, якби обидва випадки кидали бібліотечні дзвінки.
Джон Б

1
@JohnB Я думаю, що ваше твердження неправильне. "Компілятор повинен визначити, кого з них" справді "кинути. Він вирішує кинути виняток, викликаний явним кодом (код у блоці try)" "Але компілятор вибере лише остаточне виключення, і виняток try буде придушено.
Канагавелу Сугумар

1
@KanagaveluSugumar За документацією:If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed
Джон Б

21

До Java7; У коді є винятки, але їх якось ігнорували.

наприклад)

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace(); **//Only Finally Exception is Caught**
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    try 
    {
        throw new TryException(); **//This is lost**
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

Новий конструктор та два нові методи були додані до класу Throwable в JDK 7. Вони наведені нижче:

Throwable.getSupressed(); // Returns Throwable[]
Throwable.addSupressed(aThrowable);

за допомогою цього нового підходу ми можемо впоратися і з тими пригніченими винятками.

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace();
        for(Throwable t: e.getSuppressed())
        {
            t.printStackTrace();
        }
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    Throwable t = null;
    try 
    {
        throw new TryException();
    }
    catch (Exception e) {
        t = e;
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        if(t != null)fEx.addSuppressed(t);
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

У Java7 спробувати ресурси; виняток у AutoCloseable :: close () додається як вимкнений виняток за замовчуванням разом із винятком try.

Також усвідомлюючи, що це відрізняється від ланцюгових винятків (були введені з JDK 1.4 і мали на меті зробити можливим легко відстеження причинно-наслідкових зв’язків між винятками).


11

Поступаючись коду нижче:

public class MultipleExceptionsExample {

   static class IOManip implements Closeable{
       @Override
       public void close() {
           throw new RuntimeException("from IOManip.close");
       }
   }

   public static void main(String[] args) {
       try(IOManip ioManip = new IOManip()){
           throw new RuntimeException("from try!");
       }catch(Exception e){
           throw new RuntimeException("from catch!");
       }finally{
           throw new RuntimeException("from finally!");
       }
   }
}

З усіх рядків ви отримаєте: java.lang.RuntimeException: from finally!

Видаливши finallyблок, ви отримаєте:java.lang.RuntimeException: from catch!

Видаливши catchблок, ви отримаєте:

Exception in thread "main" java.lang.RuntimeException: from try!
    Suppressed: java.lang.RuntimeException: from IOManip.close

9

Пригнічені винятки - це додаткові винятки, які виникають у операторі try-with-resources ( представленому в Java 7 ) при AutoCloseableзакритті ресурсів. Оскільки під час закриття AutoCloseableресурсів можуть виникати кілька винятків , додаткові винятки приєднуються до основного винятку як пригнічені винятки .

Розглядаючи байт-код фрагмента зразка коду try-with-resources, для розміщення семантики try-with-resources використовуються стандартні обробники винятків JVM .


0

Ви також можете придушити винятки в Java 6 (із невеликою хитрістю),

Я створив утиліту, яка прозоро обробляє придушення винятків у Java 1.6 та Java 1.7. Ви можете знайти реалізацію тут

Все, що вам потрібно, це зателефонувати:

public static <T extends Throwable> T suppress(final T t, final Throwable suppressed) 

придушити виняток, і

public static Throwable [] getSuppressed(final Throwable t) {

отримати приглушені винятки винятків, якщо хтось все ще використовує Java 1.6


0

ARM - Автоматичне управління ресурсами (представлено з Java 7)

Візьмемо дуже простий приклад

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Тепер, якщо readLine()функція викидає виняток, а потім навіть close()функція [у блоці нарешті] викидає виняток, тоді пізніший надається більше пріоритету і повертається до викличної функції. У цьому випадкуException thrown by the readLine() method is ignored/suppressed . Ви можете ланцюжком викликати виняток у вашому винятку і повернути свій виняток з нарешті блокувати.

Оскільки java 7була надана функціональність для отримання пригнічених винятків. Ви можете викликати public final java.lang.Throwable[] getSuppressed()функцію на спійманому об'єкті, який можна кинути, щоб переглянути пригнічені винятки.

Для напр.

static String readFirstLineFromFileWithFinallyBlock(String path)
        throws Exception {
    try (BufferedReader br = new BufferedReader(new FileReader(path));) {
        return br.readLine();
    }
}

Тепер, якщо br.readLine();рядок кидає, Exception1а потім дозволяє сказати Exception2, що кидається під час закриття ресурсу [уявіть, що це відбувається в неявному остаточному блоці, який створює оператор try-with-resource], тоді Exception1 придушує Exception2.

Тут слід відзначити кілька пунктів -

  1. Якщо блок try-with-resource видає виняток, тобто під час створення екземпляра ресурсу, тоді блок try не буде виконуватися, і буде викинутий той самий виняток.
  2. Якщо створення екземпляра ресурсу успішне, спроба блоку викидає виняток, а виняток створюється під час закриття ресурсу, тоді виняток, викинутий під час закриття ресурсу, придушується винятком, викинутим із блоку try.
  3. Якщо ви надаєте явний блок нарешті, а виняток викидається з цього блоку, він буде придушувати всі інші виключення. (Цей явний блок нарешті виконується після закриття ресурсів)

Я скомпілював більшість можливих сценаріїв із фрагментами коду та результатами в наступному дописі.

Пригнічені винятки в Java 7

Сподіваюся, що це допомагає.


1
Однак у цьому випадку те, Exceptionщо походить від блоку try {}, не буде автоматично припинено. Програміст може вирішити це зробити, чого у вас немає. Тільки спроба використання ресурсів, коли це необхідно, автоматично припиняє винятки. І коли він це зробить, виключення з вашого еквівалентного остаточно блоку стане придушеним.
Мартін Андерссон,

1
@MartinAndersson Ви маєте рацію. У мене була певна плутанина, коли я написав відповідь. Сподіваюсь, відредагована відповідь дала кращі уявлення.
Aniket Thakur

-1

Думаю, це пов’язано з "ланцюжком винятків". Це вплине на те, як обробляється виняток цим засобом у міру еволюції трасування стека. З часом винятки, які входять до групи ланцюгових винятків, можуть бути припинені. Подивіться документацію Throwable, щоб отримати докладнішу інформацію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.