Исключения в Java и их обработка. Try-catch-finally

Сегодня поговорим об исключениях в языке java и обработке ошибок, которые обязательно будут случаться с во время работы. Исключения (Exception)  бывают двух видов: checked (проверяемые) и unchecked (не проверяемые).

По примеру того, как мы работали со строками и системой ввода-вывода, в языке java есть классы, которые отвечают за ошибки. Архитектура классов такая:

исключения java архитектура классов

Есть 2 главных класса, которые унаследованы от Throwable: Error, Exception. Желтым цветом обозначены классы ошибок, которые нужно обрабатывать: Exception, IOException, ClassNotFoundException, CloneNotSupportedException, EOFExeption, FileNotFoundException. Названия классов говорит само за себя.

Если Вы не напишете обработчик ошибок, компилятор будет ругаться и запустить программу будет нельзя. Мы уже сталкивались с этой проблемой когда работали с файлами и системой ввода-вывода. Тогда мы просто писали метод и добавляли: throws IOException. Это объявление, что наш метод может выбросить такой тип исключения. Но, чтобы обработать такое исключение (когда оно вдруг произойдет) нужно использовать конструкцию try{}catch(){}finally{}. В блоке try пишется код, который может выбросить исключение, catch — что делать, когда ошибка произошла; finally- код, который будет выполнен обязательно. Пример:

Код    
  1. package com.exception;
  2.  
  3. import java.io.IOException;
  4.  
  5. public class ExampleOfException {
  6.    
  7.     public static void myMethodWhichThrowsException() throws IOException{
  8.         System.out.println("My operations");
  9.     }
  10.  
  11.     public static void main(String[] args) {
  12.         try {
  13.             ExampleOfException.myMethodWhichThrowsException();
  14.         } catch (IOException e) {
  15.             e.printStackTrace();
  16.         } finally {
  17.             System.out.println("Finally code");
  18.         }
  19.  
  20.     }
  21.  
  22. }

 

Результат выполнения:

My operations
Finally code

Если ошибки не будет, блок finally все равно будет выполнен, а catch  нет. Часто на собеседованиях спрашивают: может ли быть ситуация, когда finally не выполнится? Ответ: может. Например, в классе System есть метод exit, который может прерывать ход программы. Его использование может прервать блок finally:

Код    
  1. package com.exception;
  2.  
  3. import java.io.IOException;
  4.  
  5. public class ExampleOfException {
  6.    
  7.     public static void myMethodWhichThrowsException() throws IOException{//ключевое слово throws говорит о том, что метод может породить исключение
  8.                 //чтобы метод, который будет вызывать его знал, что нужно позаботиться об этом исключении
  9.         System.out.println("My operations");
  10.     }
  11.  
  12.     public static void main(String[] args) {
  13.         try {
  14.             ExampleOfException.myMethodWhichThrowsException();
  15.         } catch (IOException e) {
  16.             e.printStackTrace();
  17.         } finally {
  18.             System.exit(0);
  19.             System.out.println("Finally code");
  20.         }
  21.  
  22.     }
  23.  
  24. }

 

Результат выполнения: My operations

Кстати говоря, писать блок fianally не обязательно. Его как правило, используют, когда нужно выполнить некоторые действий независимо произошла ошибка или нет. Например, закрытие потока ввода или соединения с базой данных и т.д.

Чем выше иерархия класса, тем более общую ошибку он «ловит». Класс Exception может отлавливать любые checked исключения, тогда как FileNotFoundException может увидит ошибку, когда файл не найден. Но, когда у Вас много исключительных ситуаций в одном коде, тогда лучше использовать больше конкретики нежели универсальности. Сейчас покажу на примере: 

Код    
  1. package com.exception;
  2.  
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5.  
  6. public class ExampleOfException {
  7.    
  8.     public static void myMethodWhichThrowsException() throws IOException, FileNotFoundException {//метод может выбросить две ошибки
  9.         //и мы об этом предупреждаем
  10.         System.out.println("My operations");
  11.     }
  12.  
  13.     public static void main(String[] args) {
  14.         try {
  15.             ExampleOfException.myMethodWhichThrowsException();
  16.         } catch (IOException e) {//можно написать только один общий класс исключений, который охватывает FileNotFoundException
  17.             //но так мы будем обрабатывать две ошибки одним методом
  18.             e.printStackTrace();
  19.         } finally {
  20.             System.exit(0);
  21.             System.out.println("Finally code");
  22.         }
  23.  
  24.     }
  25.  
  26. }

 

Код    
  1. package com.exception;
  2.  
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5.  
  6. public class ExampleOfException {
  7.    
  8.     public static void myMethodWhichThrowsException() throws IOException, FileNotFoundException {//метод может выбросить две ошибки
  9.         //и мы об этом предупреждаем
  10.         System.out.println("My operations");
  11.     }
  12.  
  13.     public static void main(String[] args) {
  14.         try {
  15.             ExampleOfException.myMethodWhichThrowsException();
  16.         } catch (FileNotFoundException e) {//можно написать только один общий класс исключений, который охватывает FileNotFoundException
  17.             //но так мы будем обрабатывать две ошибки одним методом и не будем их различать
  18.             e.printStackTrace();
  19.         } catch (IOException e) {//2 блока обработки обеспечивают нам больше пространства для обработки ошибок
  20.             //так мы можем различать ошибки
  21.             e.printStackTrace();
  22.         } finally {
  23.             System.exit(0);
  24.             System.out.println("Finally code");
  25.         }
  26.  
  27.     }
  28.  
  29. }

 

Последовательность блоков должна идти от частного к общему случаю.

Вы также можете написать свой класс ошибок. Нужно просто свой класс, который будет отвечать за ошибку унаследовать от Exception или от одного из класса ошибки и переопределить метод из класса Throwable, который родитель всех классов ошибок (именно от него они унаследуют все методы). Можно не реализовывать методы. Само унаследование от Exception позволит Вам использовать класс для исключений:

Код    
  1. package com.exception;
  2.  
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5.  
  6. public class ExampleOfException {
  7.  
  8.     public static void myMethodWhichThrowsException() throws MyOwnException {
  9.         System.out.println("My operations");
  10.     }
  11.  
  12.     public static void main(String[] args) {
  13.         try {
  14.             ExampleOfException.myMethodWhichThrowsException();
  15.         } catch (MyOwnException e) {
  16.             e.printStackTrace();
  17.         }
  18.     }
  19. }
  20.  
  21. class MyOwnException extends Exception {
  22.  
  23.     public MyOwnException() {
  24.         System.out.println("My exception");
  25.     }
  26. }

 

Это мы рассмотрели обрабатываемые ошибки. Но, бывают исключения, которые не обрабатываются: те которые унаследованы от RuntimeException. Такие ошибки говорят о том, что нужно подправить код, который Вы написали. Иначе, когда они возникнут, программа не сможет корректно продолжить работу. Чаще всего Вы будете получать:

  • NullPointerException — означает, что объект не проинициализирован. Вы где то забыли добавить слово new. У меня часто такое с массивами.
  • ArrayIndexOfBoundException — выход за пределы границ. Часто при работе с массивами, неправильно были учтены границы массива и его индексы.
  • ClassCastException — попытка прикастить неприводимые типы.

На схеме выше нет еще одного типа ошибок: Error. Это ошибки, которые будут Вам встречаться наиболее редко и связаны они с ошибками системы: VirtualMachineError, OutOfMemoryError. Их не обрабатывают.

Чуть не забыл о ключевом слове throw — которое указывает, что метод может выбрасывать такого рода исключения:

Код    
  1. package com.exception;
  2.  
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5.  
  6. public class ExampleOfException {
  7.    
  8.     private static Object obj = null;
  9.  
  10.     public static void myMethodWhichThrowsException(){
  11.         System.out.println("My operations");
  12.         if (obj==null)
  13.         throw new NullPointerException();
  14.     }
  15.  
  16.     public static void main(String[] args) {
  17.         ExampleOfException.myMethodWhichThrowsException();
  18.     }
  19. }

исключительные ситуации

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

Добавить комментарий