1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-thinking-in-java-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
9.6 用finally清除.md 12 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 11.03.2025 09:15 d56454c

9.6 Используйте finally для очистки

Независимо от того, происходит ли исключение в блоке try, часто требуется выполнить определённый набор действий. Это особенно важно для некоторых операций, но обычно не требуется при освобождении памяти (так как сборщик мусора автоматически занимается этим). Для достижения этой цели можно использовать конструкцию finally в конце всех блоков управления исключениями (примечание ④). Вот как выглядит полный блок управления исключениями:

try {
  # Защищённый участок:
  # Возможность "бросить" исключения A, B или C
} catch(A a1) {
  # Обработчик A
} catch(B b1) {
  # Обработчик B
} catch(C c1) {
  # Обработчик C
} finally {
  # Код, который всегда выполняется
}

Примечание ④: В C++ управление исключениями не предоставляет ключевое слово finally, так как он зависит от конструкторов для достижения эффекта очистки.

Для демонстрации использования finally рассмотрите следующую программу:

//: FinallyWorks.java
// Блок finally всегда выполняется

public class FinallyWorks {
  static int count = 0;
  public static void main(String[] args) {
    while(true) {
      try {
        // Последний инкремент равен нулю в первый раз:
        if(count++ == 0)
          throw new Exception();
        System.out.println("Исключение не выброшено");
      } catch(Exception e) {
        System.out.println("Выброшено исключение");
      } finally {
        System.out.println("в блоке finally");
        if(count == 2) break; // выход из "while"
      }
    }
  }
} ///:~
```С помощью этой программы мы можем также понять, как работать с исключениями Java (аналогично исключениям C++) в случае, когда они не позволяют вернуться к месту возникновения исключения. Размещение своего блока `try` внутри цикла позволяет установить условие, которое должно быть выполнено перед продолжением программы. Также можно добавить статический счетчик или другой механизм, чтобы позволить циклу пробовать несколько различных методов до отказа. Таким образом, программа может стать более "жёсткой".

Выходные данные:

Выброшено исключение в блоке finally Исключение не выброшено в блоке finally


Блок `finally` будет выполнен независимо от того, было ли выброшено исключение или нет.

### 9.6.1 Что делать с помощью `finally`

В языках, где нет механизма "сборки мусора" и "автоматического вызова деструкторов" (примечание ⑤), использование `finally` становится особенно важным, поскольку программист может гарантировать правильное освобождение памяти — независимо от того, что произошло внутри блока `try`. Однако Java предоставляет механизм сборки мусора, поэтому освобождение памяти почти никогда не является проблемой. Кроме того, она не имеет деструкторов для вызова. Так почему же тогда используется `finally` в Java?Примечание ⑤: Языки, где нет механизма "сборки мусора" и "автоматического вызова деструкторов".⑤: Деструктор (Destructors) является противоположностью конструктора (Constructors). Это специальная функция, которая вызывается, когда объект больше не нужен. Мы точно знаем, где и когда будет вызван деструктор. C++ предоставляет автоматическую систему вызова деструкторов, но версия Object Pascal в Delphi 1 и 2 этого не делает (в этом языке значение и использование деструктора изменились).Если требуется выполнить что-то еще помимо освобождения памяти, `finally` необходим. Например, иногда нам нужно открыть файл или установить сетевое соединение, или нарисовать что-то на экране, даже установить внешний переключатель и так далее. В следующем примере показано:

```java
//: OnOffSwitch.java
// Почему использовать finally?

class Switch {
  boolean state = false;
  boolean read() { return state; }
  void on() { state = true; }
  void off() { state = false; }
}

public class OnOffSwitch {
  static Switch sw = new Switch();
  public static void main(String[] args) {
    try {
      sw.on();
      // Код, который может выбрасывать исключения...
      sw.off();
    } catch(NullPointerException e) {
      System.out.println("NullPointerException");
      sw.off();
    } catch(IllegalArgumentException e) {
      System.out.println("IllegalArgumentException");
      sw.off();
    }
  }
} ///:~

Цель здесь — гарантировать, что переключатель выключен при завершении метода main(), поэтому sw.off() помещается в блок try и в конце каждого блока управления исключениями. Но если произойдет исключение, которое не будет захвачено здесь, то sw.off() будет пропущено. Однако с помощью finally мы можем переместить закрывающий код из блока try в одно место:

//: WithFinally.java
// Finally гарантирует очистку

class Switch2 {
  boolean state = false;
  boolean read() { return state; }
  void on() { state = true; }
  void off() { state = false; }
}

public class WithFinally {
  static Switch2 sw = new Switch2();
  public static void main(String[] args) {
    try {
      sw.on();
      // Код, который может выбрасывать исключения...
    } catch(NullPointerException e) {
      System.out.println("NullPointerException");
    } catch(IllegalArgumentException e) {
      System.out.println("IllegalArgumentException");
    } finally {
      sw.off();
    }
  }
} ///:~
```Здесь `sw.off()` перемещён в одно место. Независимо от того, что происходит, это всегда будет выполнено. Даже если исключение не было поймано в текущем блоке `catch`, метод `finally` будет выполнен перед тем, как механизм управления исключениями перейдет на более высокий уровень в поисках обработчика. Пример:

```java
//: AlwaysFinally.java
// finally всегда выполняется

class Ex extends Exception {}

public class AlwaysFinally {
  public static void main(String[] args) {
    System.out.println("Начало первого блока try");
    try {
      System.out.println("Начало второго блока try");
      try {
        throw new Ex();
      } finally {
        System.out.println("finally во втором блоке try");
      }
    } catch (Ex e) {
      System.out.println("Поймано исключение Ex в первом блоке try");
    } finally {
      System.out.println("finally в первом блоке try");
    }
  }
} ///:~

Выход программы демонстрирует конкретное поведение:

Начало первого блока try
Начало второго блока try
finally во втором блоке try
Поймано исключение Ex в первом блоке try
finally в первом блоке try

Если вызываются операторы break и continue, то блок finally также будет выполнен. Обратите внимание, что вместе с метками для break и continue, finally исключает необходимость использования оператора goto.

9.6.2 Недостаток: потеря исключенияОбщие случаи реализации исключений Java выглядят отличной идеей. К сожалению, существует один недостаток. Хотя исключение указывает на наличие критической ситуации в программе, которую нельзя игнорировать, исключение может быть просто "потерянным". Это возможно при использовании специальной конфигурации блока finally:```java

//: LostMessage.java // Как исключение может быть потерянным

class ОченьВажноеИсключение extends Exception { public String toString() { return "Очень важное исключение!"; } }

class БезЗначительноеИсключение extends Exception { public String toString() { return "Без значительное исключение"; } }

public class LostMessage { void f() throws ОченьВажноеИсключение { throw new ОченьВажноеИсключение(); } void dispose() throws БезЗначительноеИсключение { throw new БезЗначительноеИсключение(); } public static void main(String[] args) throws Exception { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); } } } ///:~


Выход программы следующий:

Без значительное исключение at LostMessage.dispose(LostMessage.java:21) at LostMessage.main(LostMessage.java:29)


Учрежденная здесь `ОченьВажноеИсключение` (очень важное исключение) отсутствует, так как она просто заменена на `БезЗначительноеИсключение`, которое было выброшено в finally-блоке.

Это довольно серьезная проблема, поскольку это означает, что один из исключений может полностью пропасть. Как показывает пример выше, эта потеря кажется очень "естественнной" и трудно заметна. В отличие от этого, в C++, если второй исключения возникает до того, как первый исключение будет обработано, это считается серьезной ошибкой программирования. Возможно, будущие версии Java исправят эту проблему (вышеупомянутый результат получен с использованием Java 1.1).

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-thinking-in-java-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-thinking-in-java-zh.git
oschina-mirror
wizardforcel-thinking-in-java-zh
wizardforcel-thinking-in-java-zh
master