Преимущество рассмотренного выше способа состоит, в частности, в том, что если программа, получающая доступ к файлу, завершается аварийно из-за какой-нибудь ошибки ввода-вывода, генерирующей исключение, файл все равно закрывается в блоке finally. И если с аварийным завершением простых программ, как в большинстве примеров в этой книге, из-за неожиданно возникающей исключительной ситуации еще можно как-то мириться, то в крупных программах подобная ситуация вряд ли вообще допустима. Именно ее и позволяет исключить блок finally.
Иногда оказывается проще заключить в оболочку те части программы, в которых открывается файл, чтобы получить доступ к нему из единственного блока try, не разделяя его на два блока, а для закрытия файла использовать отдельный блок finally. В качестве примера ниже приведена переделанная версия рассмотренной выше программы ShowFile. /* В этой версии программы отображения текстового файла код, открывающий файл и получающий к нему доступ, заключается в единственный блок try. А закрывается файл в блоке finally. */ import java.io.*; class ShowFile { public static void main(String args[]) { int i; FilelnputStream fin = null; // Прежде всего следует убедиться, что файл был указан, if (args.length != 1) { System.out.println("Usage: ShowFile filename"); return; } // В следующем коде открывается файл, из которого читаются // символы до тех пор, пока не встретится знак EOF, а затем // файл закрывается в блоке finally, try { fin = new FilelnputStream(args[0]); do { i = fin.read ; if(i != -1) System.out.print((char) i); } while(i != -1); } catch(FileNotFoundException exc) { System.out.println("File Not Found."); } catch(IOException exc) { System.out.println("An I/O Error Occurred"); } finally { // Файл закрывается в любом случае, try { if (fin != null) fin.closeO; } catch(IOException exc) { System.out.println("Error Closing File"); } } } }
Обратите внимание на то, что переменная fin инициализируется пустым значением null. А в блоке finally файл закрывается только в том случае, если значение переменной fin не является пустым. Такой способ оказывается вполне работоспособным, поскольку переменная fin не будет содержать пустое значение лишь в том случае, если файл был успешно открыт. Следовательно, метод close не будет вызываться, если во время открытия файла возникнет исключение.
В приведенном выше примере блок try/catch можно сделать более компактным. Ведь исключение FileNotFoundException является подклассом исключения IOException, и поэтому его не нужно перехватывать отдельно. В качестве примера ниже приведен блок оператора catch, которым можно воспользоваться для перехвата обоих этих исключений, не прибегая к перехвату исключения FileNotFoundException в отдельности. В данном случае выводится стандартное сообщение о возникшем исключении с описанием характера ошибки. } catch(IOException exc) { System.out.println("I/O Error: " + exc); } finally { ... В рассматриваемом здесь способе любая ошибка, в том числе и ошибка открытия файла, будет обработана единственным оператором catch. Благодаря своей компактности именно такой способ применяется в большинстве примеров ввода-вывода, представленных в этой книге. Следует, однако, иметь в виду, что он может оказаться не вполне пригодным в тех случаях, когда требуется отдельно обрабатывать ошибку открытия файла, например, вследствие того, что пользователь введет имя файла с опечаткой. В подобных случаях рекомендуется выдать сначала приглашение правильно ввести имя файла, а затем перейти к блоку try для доступа к файлу. ### Вывод в файл Для того чтобы открыть файл для вывода, следует создать объект типа FileOutputStream. Ниже приведены два наиболее часто употребляемых конструктора этого класса.
FileOutputStream(String имя