Ловля исключений в Java - PullRequest
       21

Ловля исключений в Java

4 голосов
/ 11 февраля 2009

Есть определенные предопределенные исключения в Java, которые, если их выдают, сообщают, что произошло что-то серьезное, и вам лучше улучшить код, чем перехватывать их в блоке catch (если я правильно понял). Но все же я нахожу много программ, в которых у меня есть следующее:

} catch (IOException e) {
     ...
} catch (FileNotFoundException e) {
     ....
}

и я подумал, что IOException и FileNotFoundException - это именно такие исключения, которые мы не должны перехватывать в блоке catch. Почему люди делают это? Лучше их так ловить? Компилятор Java в любом случае предупреждает о любой проблеме такого рода.

Спасибо.

Ответы [ 7 ]

14 голосов
/ 11 февраля 2009

Нет, нет ничего плохого в том, чтобы поймать IOException и FileNotFoundException - если вы действительно обрабатываете эти исключения. Это важный момент - вы действительно можете действовать перед лицом этого исключения? Иногда вы можете - очень часто, например, на верхнем уровне сервера, когда только потому, что один запрос не выполнен, следующий не может продолжаться. Реже в клиентских приложениях, хотя это очень сильно зависит от ситуации. Не удается прочитать файл, когда вы пытаетесь выполнить пакетный импорт? Хорошо, прервите операцию, но не обязательно завершайте весь процесс ...

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

5 голосов
/ 11 февраля 2009

Порядок, который вы показываете с IOException, пойманным до FileNotFoundException, неверен. Поскольку FileNotFoundException расширяет IOException, когда выбрасывается FileNotFoundException, будет использован первый обработчик, а второй обработчик будет мертвым кодом.

Я не пробовал, но я немного удивлен, если это скомпилируется. Я надеюсь, что инструмент статического анализа, такой как FindBugs, уловит эту ошибку.

Насколько должен поймать FileNotFoundException, зависит от абонента. Однако я скажу, что FileNotFoundException часто можно восстановить осмысленным способом - запрашивая другой файл, пробуя использовать запасное местоположение - вместо простого регистрации ошибки или прерывания процесса.

4 голосов
/ 11 февраля 2009

В Java существует два типа исключений: проверенные и непроверенные.

Проверенные исключения должны обрабатываться в блоке catch. Невыполнение этого приведет к ошибке компилятора. IOException является примером проверенного исключения и должно быть обработано. То, что вы на самом деле здесь делаете, зависит от рассматриваемого приложения, но для того, чтобы компилятор был доволен, нужно обработать исключение.

Не проверенные исключения не нужно перехватывать. Все классы, которые выходят из RuntimeException, не проверяются. Хорошим примером этого является исключение NullPointerException или ArrayIndexOutOfBoundsException. Компилятор не заставляет вас перехватывать эти исключения, но они все равно могут возникать при запуске вашей программы, вызывая ее сбой.

Для записи, IOException может быть выброшено для чего-то столь же простого, как попытка открыть файл, который не существует. Хорошей идеей будет обработать что-то подобное и восстановиться изящно (диалоговое окно для пользователя, говорящее, что файл не существует, и сделать так, чтобы диалоговое окно открытого файла появилось или что-то в этом роде) вместо того, чтобы позволить программе аварийно завершить работу.

3 голосов
/ 11 февраля 2009

Я думал, что IOException и FileNotFoundException - именно такие исключения

Нет, на самом деле это "другие" типы исключений, которые выходят за рамки ваших навыков программирования. Независимо от того, насколько хорошо вы программируете, компилятор и библиотеки заставляют вас «осознавать», что что-то может произойти.

Подумайте об этом сценарии:

Вы создаете приложение, которое сохраняет данные во временную папку.

Все в порядке, вы проверили, что папка существует, а если нет, вы создаете ее самостоятельно.

А затем вы записываете 2 МБ в эту временную папку.

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

Вы ничего не можете сделать, чтобы предотвратить это программно, в некоторых системах такая операция может происходить (в unix пользователь root может выполнить команду rm -rf / tmp, и вы ничего не можете с этим поделать. В Windows я думаю, что система не позволит другому процессу удалить файл, который используется)

Заставив вас проверять подобные исключения в коде, дизайнеры платформы подумали, что, по крайней мере, вы об этом знаете.

Джон прав иногда вы ничего не можете с этим поделать, возможно, войдите в систему до того, как программа умрет, что считается «обработкой исключения» (плохая обработка да, но обработка по крайней мере)

try { 
   .... 
} catch( IOException ioe ) { 
    logger.severe( 
        String.format("Got ioe while writting file %s. Data was acquired using id = %d, the message is: %s", 
             fileName, 
             idWhereDataCame,
             ioe.getMessage()) );
  throw ioe;
}

Еще одна вещь, которую вы можете сделать, это «связать» исключение в соответствии с абстракцией.

Вероятно, ваше приложение имеет графический интерфейс, показывающий, что IOException для пользователя ничего не значит или может быть уязвимость безопасности . Модифицированное сообщение может быть отправлено.

try { 
   .... 
} catch( IOException ioe ) { 
   throw new EndUserException("The operation you've requeste could not be completed, please contact your administrator" , ioe );
}    

И исключение EndUserException может быть захвачено где-то в графическом интерфейсе и представлено пользователю в диалоговом сообщении (вместо того, чтобы просто пропустить приложение в его глазах без дополнительной информации). Конечно, вы ничего не могли сделать, чтобы восстановить это IOException, но, по крайней мере, вы умрете со стилем: P

Наконец, клиентский код может использовать разные реализации, и не все исключения будут иметь смысл.

Например, подумайте еще раз о первом сценарии. Та же самая «операция» может иметь три вида «плагинов» для сохранения данных.

a) Write the data  to a file.
b) Or, write to a db
c) Or write to a remote server. 

Интерфейс не должен выбрасывать:

java.io.IOException
java.sql.SQLException 

ни

java.net.UnknownHostException

Но вместо этого что-то вроде

my.application.DataNotSavedException

и различные реализации будут обрабатывать исключение на правильном уровне, и преобразуют его в соответствующую абстракцию:

Код клиента:

 DataSaver saver = DataServer.getSaverFor("someKeyIdString");

 try {
     saver.save( myData );
 } catch( DataNotSavedException dnse ) {

     // Oh well... .
     ShowEndUserError("Data could not be saved due to : " dnse.getMessage() );
 }

Код реализации:

 class ServerSaver implements DataSaver { 
 ....
     public void save( Data data ) throws DataNotSavedException {
         // Connect the remore server.
         try { 
             Socket socket = new Socket( this.remoteServer, this.remotePort );
             OuputStream out = socket.getOut.... 

             ....
             ....
         } catch ( UnknownHostException uhe ) { 
            // Oops....
            throw new DataNotSavedException( uhe );
         }
    }
 }

FileSaver и DatabaseSaver будут делать что-то похожее.

Все это Проверенные исключения , потому что компилятор заставляет вас проверять их.

Когда использовать один или другой (отмечен / не отмечен): здесь

Есть два других вида: здесь

И, наконец, гораздо более простое объяснение среды выполнения: здесь

3 голосов
/ 11 февраля 2009

Как сказал Джон , во многих случаях можно ловить эти исключения. Исключения, которые вы не должны ловить, - это такие вещи, как NullPointerException и ArrayIndexOutOfBoundsException, они указывают на ошибки в вашем коде.

Java имеет два типа исключений: проверенные и непроверенные исключения (те, которые наследуются от RuntimeException).

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

С другой стороны, непроверенные исключения лучше всего использовать для вещей, которых можно избежать, изменив код. Исключение NullPointerException всегда можно избежать, если код проверяет возможность нулевой ссылки. Аналогично, если вы будете осторожны с вашими индексами, вы никогда не получите ArrayIndexOutOfBoundsException. Компилятор не обязывает вас обрабатывать эти сценарии, поскольку они представляют собой ошибки, которые должны быть исправлены.

3 голосов
/ 11 февраля 2009

Это делается для того, чтобы по-разному обрабатывать различные типы исключений. Как правило, вам нужно сначала перехватить наиболее детальные исключения, если вы поместите более широкие исключения в начале вашего блока catch, вы сначала выполните этот код, а затем нажмете на блок finally.

Джон прав, улов, который перехватывает IOException, перехватит все IOException и любой подтип IOException, и, поскольку FileNotFoundException является типом IOException, он никогда не достигнет 2-го улова.

0 голосов
/ 11 февраля 2009

Чтобы немного отклониться от этой идеи: может быть, в другом домене это яснее

Что вы делаете, если машина перед вами внезапно останавливается.

Стоп!

Итак, мы обработаем исключение.

Итак, вернемся к коду:

Что делать, если нужный вам файл недоступен?

либо

  1. Есть резервная копия. Составлено в виде ресурс, потому что это часть вашего программа. Я не шучу.
  2. IFF Это пользовательский файл: Сообщите пользователю; это их файл.
  3. Прервать программу с сообщением пользователю, потому что ваша система SYSTEM сломаны.

По моему мнению, четвертого варианта не существует.

Наши братья по C # / VC ++ выбирают непроверенные исключения. Многие «эксперты» считают, что проверенные исключения - это плохо: я утверждаю, что жизнь сложна, смирись с этим. Проверенные исключения представляют известные режимы отказа: и должны быть устранены. Ваша диаграмма рыбьих костей прямолинейна для нормальной работы и отклоняется в сторону для сбоев. Проверенные исключения - ожидаемые сбои.

Теперь, когда вы начнете обрабатывать исключения Runtime, это станет интересным. Java-программа может работать в основном нормально с функциями, которые не работают. Под этим я подразумеваю, что они генерируют исключения нулевого указателя, ошибки границ массива, недопустимые аргументы и исчерпывают пространство кучи. Это делает добавочную доставку вполне осуществимой.

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

...