При написании кода для исключений одной из часто возникающих проблем является вопрос: "Будут ли исключения правильно очищены после своего возникновения?" В большинстве случаев это безопасно, но в конструкторах это становится серьёзной проблемой. Конструкторы помещают объекты в начальное безопасное состояние, однако они могут выполнять некоторые действия, такие как открытие файла. Эти действия будут корректно очищены только тогда, когда пользователи завершат использование объекта и вызовут специальный метод очистки. Если исключение будет выброшено внутри конструктора ("thrown"), эти действия по очистке также могут не произойти корректно. Все это означает, что при написании конструкторов мы должны быть особенно внимательны.Исходя из того, что недавно мы узнали про finally
, возможно, вы считаете его подходящим решением. Однако всё не так просто, поскольку finally
всегда выполняет код очистки, даже если мы не хотим этого делать до выполнения метода очистки. Поэтому, если действительно используете finally
для очистки, вам потребуется установить какой-то вид метки при нормальном завершении конструктора. И если эта метка установлена, ничего не должно выполняться внутри блока finally
. Поскольку этот подход не идеален (требует объединения кода одного места с другим), обычно не следует пытаться использовать такой способ очистки в finally
, если это не абсолютно необходимо.Класс InputFile {
приватный BufferedReader in;
InputFile(строка fname) бросает Исключение {
попробовать {
in =
новый BufferedReader(
новый FileReader(fname));
// Другой код, который может вызвать исключения
} поймать FileNotFoundException e {
System.out.println("Не удалось открыть " + fname);
// Не было открыто, поэтому не закрывай его
бросить e;
} поймать Исключение e {
// Все остальные исключения должны закрыть его
попробовать {
in.close();
} поймать IOException e2 {
System.out.println("in.close() неудачна");
}
бросить e;
} наконец {
// Не закрывай его здесь!!!
}
}
строка getLine() {
строка s;
попробовать {
s = in.readLine();
} поймать IOException e {
System.out.println("readLine() неудачна");
s = "неудачно";
}
вернуть s;
}
вункция cleanup() {
попробовать {
in.close();
} поймать IOException e2 {
System.out.println("in.close() неудачна");
}
}
}
//: Cleanup.java
// Учет исключений в конструкторах
import java.io.*;
``````java
public class Очистка {
public static void основной(String[] аргументы) {
try {
InputFile в =
new InputFile("Очистка.java");
String s;
int i = 1;
while (!((s = в.getLinie()).equals(null)))
System.out.println("" + i++ + ": " + s);
в.очистка();
} catch (Exception е) {
System.out.println(
"Поймано в основном, е.printStackTrace()");
е.printStackTrace();
}
}
}
///:~
Пример использует Java 1.1 IO классы.
Конструктор InputFile
принимает один параметр типа String
, который представляет имя файла, который мы хотим открыть. Внутри блока try
он создает объект FileReader
с этим именем файла. Для FileReader
нет смысла существования без преобразования его в объект BufferedReader
, который может действительно взаимодействовать с ним. Обратите внимание, что одним из преимуществ InputFile
является то, что он объединяет эти две операции одновременно.
Перевод:
public class Очистка {
public static void main(String[] args) {
try {
InputFile in =
new InputFile("Очистка.java");
String s;
int i = 1;
while (!((s = in.getLinie()).equals(null)))
System.out.println("" + i++ + ": " + s);
in.close();
} catch (Exception e) {
System.out.println(
"Caught in main, e.printStackTrace()");
e.printStackTrace();
}
}
}
///:~
Пример использует Java 1.1 IO классы.
Конструктор InputFile
принимает один параметр типа String
, который представляет имя файла, который мы хотим открыть. Внутри блока try
он создает объект FileReader
с этим именем файла. Для FileReader
нет смысла существования без преобразования его в объект BufferedReader
, который может действительно взаимодействовать с ним. Обратите внимание, что одним из преимуществ InputFile
является то, что он объединяет эти две операции одновременно.Если конструктор FileReader
не удается, будет выброшено исключение FileNotFoundException
. Это исключение должно быть отдельно поймано, так как это специальное исключение, которое указывает на ситуацию, когда файл ещё не был успешно открыт. Любые другие пойманные исключения должны закрывать файл, поскольку файл уже был открыт до того, как программа достигла этих пойманных блоков. Метод close()
также может выбросить исключение, которое будет поймано даже если оно находится внутри другого блока catch
. Выполнение локальных действий после этого не позволяет нам считать, что объект был создан корректно, поэтому исключение должно быть повторно выброшено.В этом примере не используется вышеупомянутый метод флагов, и блок finally
явно не является правильным местом для закрытия файла, так как это может привести к закрытию файла каждый раз при завершении конструктора. Поскольку мы хотим, чтобы файл оставался открытым во время активности объекта InputFile
, использование такого подхода было бы некорректным. Метод getLine()
возвращает строку, содержащую содержимое следующей строки файла. Он вызывает метод readLine()
, который может генерировать исключение, но это исключение будет поймано, что позволяет методу getLine()
больше не генерировать никаких исключений. Одним из особых вопросов при работе с исключениями является решение о полном контроле над исключением на этом уровне, частичном контроле и передаче того же (или другого) исключения или просто простой передаче его. В подходящий момент простая передача исключения может значительно упростить нашу работу с кодом. Метод getLine()
будет выглядеть следующим образом:
public String getLine() throws IOException {
return in.readLine();
}
Однако, конечно же, вызывающий код теперь должен контролировать возникающие IOException
.
После завершения работы с объектом InputFile
следует вызвать метод cleanup()
, чтобы освободить системные ресурсы, такие как файловые ссылки, занятые BufferedReader
и/или FileReader
— примечание ⑥. Очистка должна выполняться только после окончательного использования объекта InputFile
. Возможно, вы захотите включить такую логику в метод finalize()
, но, как было указано в главе 4, это гарантируется не всегда (даже если известно, что он будет вызван, время его выполнения остаётся неопределённым). Это является недостатком Java — автоматическое удаление всех ресурсов, кроме памяти, не происходит, поэтому клиентским программистам следует знать, что они должны обеспечивать правильное выполнение очистки через метод finalize()
.⑥: В C++, "деструктор" помогает нам контролировать эту ситуацию.
В Cleanup.java
мы создаем объект InputFile
, используем его для открытия того же исходного файла, который использовался для создания программы. Мы читаем содержимое этого файла построчно и добавляем соответствующие номера строк. Все исключения будут перехватываться в методе main()
— хотя можно выбрать более надёжный подход.
Этот пример также демонстрирует, почему концепция исключений была представлена именно здесь в книге. Исключения очень хорошо интегрированы в работу с Java благодаря тому, что компилятор требует обязательной обработки исключений. Только после понимания того, как работать с исключениями, можно глубже изучить возможности компилятора.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )