Правильное использование исключений в Java
Доброго времени суток, уважаемый Хабр.
Я хотел бы рассказать, как правильно нужно использовать исключения в Java. Частично этот материал рассматривается на просторах интернета, а также рассматривается немного в книге J.Bloch Effective Java. Речь пойдет о использовании проверенных и непроверенных (checked/unchecked) исключениях. Статья будет полезна новичкам, т.к. вначале не всегда ясно, как правильно нужно пользоваться исключениями.
Иерархия исключений в Java представлена следующим образом: родительский класс для всех Throwable. От него унаследовано 2 класса: Exception и Error. От класса Exception унаследован еще RuntimeException.
Error – критические ошибки, который могут возникнуть в системе (например, StackOverflowError ). Как правило обрабатывает их система. Если они возникают, то приложение закрывается, так как при данной ситуации работа не может быть продолжена.
Exception – это проверенные исключения. Это значит, что если метод бросает исключение, которое унаследовано от Exception (напр. IOException), то этот метод должен быть обязательно заключен в блок try-catch. Сам метод, который бросает исключение, должен в сигнатуре содержать конструкцию throws. Проверенные (checked) исключения означают, что исключение можно было предвидеть и, соответственно, оно должно быть обработано, работа приложения должна быть продолжена. Пример такого исключения — это попытка создать новый файл, который уже существует (IOException). В данному случае, работа приложения должна быть продолжена и пользователь должен получить уведомление, по какой причине файл не может быть создан.
Например:
try < File.createTempFile("prefix", ""); >catch (IOException e) < // Handle IOException >/** * Creates an empty file in the default temporary-file directory * any exceptions will be ignored. This is typically used in finally blocks. * @param prefix * @param suffix * @throws IOException - If a file could not be created */ public static File createTempFile(String prefix, String suffix) throws IOException
В данном примере можно увидеть, что метод createTempFile может выбрасывать IOException, когда файл не может быть создан. И это исключение должно быть обработано соответственно. Если попытаться вызвать этот метод вне блока try-catch, то компилятор выдаст ошибку и будет предложено 2 варианта исправления: окружить метод блоком try-catch или метод, внутри которого вызывается File.createTempFile, должен выбрасывать исключение IOException (чтобы передать его на верхний уровень для обработки).
RuntimeException – это непроверенные исключения. Они возникают во время выполнения приложения. К таким исключениям относится, например, NullPointerException. Они не требуют обязательного заключения в блок try-catch. Когда RuntimeException возникает, это свидетельствует о ошибке, допущенной программистом (неинициализированный объект, выход за пределы массива и т.д.). Поэтому данное исключение не нужно обрабатывать, а нужно исправлять ошибку в коде, чтобы исключение вновь не возникало.
Ниже приведен пример, как правильно работать с RuntimeException:
/** * Calculates rectangle square * @param aRect Rectangle * @throws NullPointerException if Rectangle is null */ public int calculateSquare(Rectange rect) < if (rect == null) < throw new NullPointerException(“Rectangle can't be null”); >// calculate rectangle square int rectWidth = rect .getWidth(); int rectHeight = rect .getHeight(); int square rectWidth * rectHeight(); return square; > . Rectangle rect = new Rectangle(); int square = calculateSquare(rect); ….
В данном примере метод принимает объект класса Rectangle. В описании метода содержится строка @throws, которая описывает исключение, которое может быть выброшено и при каких условиях. Однако, сигнатура метода не содержит конструкции throws. Это значит, что при вызове метода его не нужно оборачивать блоком try-catch. А программист должен не допустить передачи в метод неинициализированного объекта.
Надеюсь, такие небольшие разъяснения могут немного разобраться, когда же нужно обрабатывать исключения, а когда нужно просто исправлять ошибки в коде.
Для чего используют исключения в java
правильно ли понимаю, что когда я работаю с проектом, в котором есть несколько потоков исполнения, может быть вот такая ситуация. Один из этих потоков запускается и завершается успешно, а затем выбрасывает исключение внутри блока try-catch. Оставшиеся потоки исполнения продолжают свою работу, но никакой код в блоке finally не выполняется. Тогда блок finally при обработке исключений не будет выполнен?
я читаю про исключения на 1м и в принципе понимаю, но не очень. ps: зачем только я начал с java core. pss: если вы это читаете, и я до сих пор на первом, то либо я прохожу другой курс, либо читаю книгу по джаве, параллельно проходя этот курс, либо решил взять перерыв на неопределенный срок времени. никогда не сдамся)
Есть подозрение, что так будет правильнее.
обращу внимание на некоторую неточность. цитата «Создание исключения При исполнении программы исключение генерируется JVM или вручную, с помощью оператора throw» в java исключения это тоже объекты поэтому создается исключение так же как объект new Exception. а бросается в программе с помощью оператора throw. обычно эти операции объединяют в одну throw new Exception(«aaa»);
если что я пишу это с 3 уровня. Под конец лекций я читал статью про бафридер, после нашел там ссылку на потоки вводов, а потом чтобы понять что там говориться ввел гугл про исключение и нашел эту статью, спасибо автору, это статья очень помогла. PS если ты читаешь этот комментарий и видишь что у меня нет прогресса(то есть если я все еще на 3 уровне или чуточку больше), то скажи мне, что я нуб и не дошел до 40 лвла
Исключения Java
Защита от дурака — стандартная конструкция для любого приложения. Рассмотрим, как организовать её в Java.
Одно из стандартных требований ТЗ на разработку ПО – отсутствие ошибок и конфликтов, препятствующих нормальной работе. Путей реализации два – ограничить функциональность и возможности пользователя или создать код, который будет учитывать возможные неприятности.
В java исключением называется любая ошибка, которая возникает в ходе выполнения программы. Это может быть несоответствие типов данных, деление на ноль, обрыв связи с сервером и многое другое. Операции по их поиску и предотвращению называются обработкой исключений.
Иерархия
Прежде чем мы перейдём к практике, давайте познакомимся с видами исключений Джава и их иерархией. В основе всего лежит класс Throwable. Все возможные конфликты кода с машиной и пользователем описаны здесь. Для удобства обработки и чтения класс Throwable имеет подклассы Error и Exception. Error – критические ошибки, которые не обязательно происходят по вине пользователя, обработать их невозможно. Exception – собственно конфликты нашей программы, которые необходимо отлавливать.
Взгляните на упрощённую схему иерархии исключений java:
Как видно, блоки делятся на «два лагеря» по цветам — проверяемые и непроверяемые java исключения. Данная классификация показывает, как их воспринимает компилятор: проверяемые – учитывает, непроверяемые – игнорирует. К первому относится Exception в полном составе, кроме RuntimeException. Все остальные классы исключений – непроверяемые компилятором.
Иерархия классов исключений важна и для правильной организации кода. Допустим, у вас есть несколько блоков обработки. Тогда в начале необходимо указать низшие уровни, а в конце – высшие. В противном случае, будет запущен только первый блок, а остальные – проигнорированы.
Создание обработчика
Для обработки исключений java используются следующие операторы: try, catch, finally, throw, throws. Первые три — стандартная структура вашего блока. По шагам:
- Оператор или часть кода, в которой вам надо отыскать ошибку, помещается в блок try.
- Далее в блоке catch вы указываете, что за исключение надо ловить и как его обрабатывать.
- В блоке finally набор обязательных действий при возникновении ошибки. Обычно это запись данных, закрытие ресурсов и пр. Блок исполняется всегда, вне зависимости от срабатывания catch.
Рассмотрим структуру на примере Джава исключения:
try // код, где мы хотим отследить ошибку
>
catch (тип_исключения объект_исключения) // код обработки
>
finally // что нужно выполнить после завершения блока try
>
Если вы хотите обработать несколько исключений – просто создайте ещё один блок catch.
try // код, где мы хотим отследить ошибку
>
catch (тип_исключения_1 объект_исключения_1) // код обработки
>
catch (тип_исключения_2 объект_исключения_2) // код обработки
>
finally // что нужно выполнить после завершения блока try
>
С помощью оператора throw вы можете создавать исключения:
На практике это выглядит так:
Student stud1;
public void onClick(View view) if(stud1 == null) throw new NullPointerException(«Студента не существует»);
>
>
Включим оператор throw в наш стандартный пример с try-catch:
public void onClick(View view) if (stud1 == null) try throw new NullPointerException(«Студента не существует»);
> catch (NullPointerException e) Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
>
>
>
Как только обработка дойдёт до оператора throw, дальнейшее выполнение кода будет прекращено. Обработчик рассмотрит ближайший блок try-catch на требуемое исключение, потом следующий и так до конца кода. В случае, если вызвать ява исключение неоткуда – обработчик остановит программу.
Оператор throws используется для методов, которые содержат исключения, но их не обрабатывают.
тип имя_метода(список_параметров) throws список_исключений // код метода
>
Несколько исключений в списке необходимо перечислить через запятую. Воспользовавшись такой конструкцией, вы сообщите всем вызывающим методам о необходимости учитывать исключения.
Операторы try можно вкладывать друг в друга. При этом если вложенный обработчик не имеет своего блока catch, он осуществляет его поиск в родительском операторе. Если и там нет – блок обрабатывается системой.
Готовые и новые исключения
Далее приведём список java исключений, которые вам потребуются в работе чаще других:
- ArithmeticException — ошибки вычислений.
- NullPointerException — ссылка на пустое место.
- NegativeArraySizeException — массив отрицательной размерности.
- ArrayStoreException — присвоение элементу массива неправильного типа.
- NumberFormatException — невозможно преобразовать строку в число.
- IllegalArgumentException — неправильный аргумент при вызове метода.
- UnsupportedOperationException — указанной операции не существует.
- TypeNotPresentException — указанного типа не существует.
Все указанные типы java исключений содержатся в классе RuntimeException, а значит, их не надо указывать в блоке throws.
Естественно, система не может содержать всевозможные исключения. Некоторые придётся создавать самостоятельно. Для того, чтобы создать собственное java исключение, вам необходимо унаследовать собственный класс от Exception и переопределить требуемые методы класса Throwable. Или унаследоваться от наиболее близкого по смыслу типа. Рассмотрим на примере программы под android создание java исключения:
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
>
public void testMethod() throws StudentException System.out.println(«Возбуждаем StudentException из метода testMethod()»);
throw new StudentException(); // конструктор по умолчанию
>
public void testMethod2() throws StudentException System.out.println(«Возбуждаем StudentException из метода testMethod2()»);
throw new StudentException(«Создано во втором методе»);
>
public void onClick(View view) try testMethod();
> catch (StudentException e) e.printStackTrace();
System.out.println(«Исключение перехвачено»);
>
try testMethod2();
> catch (StudentException e) e.printStackTrace();
>
>
class StudentException extends Exception StudentException() >
StudentException(String msg) super(msg);
>
>
>
Обработка исключений – основа безопасного и качественного кода. С их помощью вы можете управлять действиями пользователя, ходом выполнения программы или добавить вариативности коду.