betacode

Обработка исключений Java - Java Exception Handling

  1. Что такое Exception (Исключение)?
  2. Иерархия исключения
  3. Обработка исключения через try-catch
  4. Блок try-catch-finally
  5. Свернуть один Exception в другой Exception 
  6. RuntimeException т подклассы

1. Что такое Exception (Исключение)?

Для начала, давайте посмотрим иллюстрированный пример ниже:
В данном примере имеется часть кода с ошибкой в результате деления на 0. Деление на 0 вызыввет исключение: ArithmeticException
HelloException.java
package org.o7planning.tutorial.exception;

public class HelloException {

	public static void main(String[] args) {

		System.out.println("Three");

		// Данное деление не имеет проблем.
		int value = 10 / 2;

		System.out.println("Two");

		// Данное деление не имеет проблем.
		value = 10 / 1;

		System.out.println("One");

		// Данное деление имеет проблему, деление на 0.
		// Ошибка происходит здесь.
		value = 10 / 0;

		// И строка кода ниже не будет выполнена.
		System.out.println("Let's go!");

	}

}
Результат запуса примера:
Вы можете увидеть оповещение ошибки на экране Console, оповещение ошибки очень ясное, включает информацию, где произошла информация на какой строке кода.
Three
Two
One
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at org.o7planning.tutorial.exception.HelloException.main(HelloException.java:31)
Посмотрим поток программы через следующую иллюстрацию.
  • Программо запущенно по обыкновению начиная с шага (1),(2) до (5)
  • В шаге (6) происходит проблема при делении на 0.
  • Программа выходит из метода main, и линия кода (7) не выполнена.
Мы исправим код примера выше.
HelloCatchException.java
package org.o7planning.tutorial.exception;

public class HelloCatchException {

	public static void main(String[] args) {

		System.out.println("Three");

		// Данное деление совершенно не имеет проблем
		int value = 10 / 2;

		System.out.println("Two");

		// Данное деление тоже
		value = 10 / 1;

		System.out.println("One");

		try {
			// Данное деление имеет проблему, деление на 0.
			// Ошибка произошла здесь.
			value = 10 / 0;

			// Данная строка не будет выполнена.
			System.out.println("Value =" + value);

		} catch (ArithmeticException e) {
			// Строки кода в catch будут выполнены.
			System.out.println("Error: " + e.getMessage());

			// Строки кода в catch будут выполнены.
			System.out.println("Ignore...");

		}

		// Данная строка кода будет выполнена.
		System.out.println("Let's go!");

	}

}
И результаты запуска примера:
Three
Two
One
Error: / by zero
Ignore...
Let's go!
Мы объясним поток программы иллюстрацией ниже.
  • Шаги (1)-(5) абсолютно нормальны.
  • Исключение происходит в шаге (6), проблема при делении на 0.
  • Сразу же выполняет команду в блоке catch, шаг (7) пропущен.
  • Шаг (8),(9) выполнен.
  • Шаг (10) выполнен.

2. Иерархия исключения

Это модель иерархической карты Exception в java.
  • Самый высокий класс это Throwable
  • Два прямых подклассов это Error и Exception.
В ветке Exception есть подветка RuntimeException это исключение непроверенное в java во время компиляции. Значение проверенный и непроверенный во время компиляции будет изображено в следующих примерах.
Примечание: Ваши кастомизированные классы должны быть наследованы от 2 веток Error или Exception, не наследованы напрямую от Throwable.
Error
Когда динамическое соединение не удается, или в виртуальной машине происходит серьезная проблема, то она выбрасывает Error. Типичным программам Java не стоит ловить ошибку (Error). Помимо этого, это неточно выбрасывают ли когда-нибудь ошибку типичные программы Java.
Пример динамического соединения: Например, когда вы вызываете библиотеку, и у этой библиотеки отсутствует класс, или метод... в том случае, выбрасывается Error.
Exceptions
Большинство программ выбрасывающих и ловящих объекты, являются подклассом Exception. Случай Exception показывает происхождение проблемы, но это не серьезная системная проблема. Большинство написанных вами программы будут выбрасывать и ловить Exception.

Класс Exception имеет много подклассов с определением в пакете Java. Наследники показывают, что может произойти много случаев исключения. Например, NegativeArraySizeException выбрасывается, когда вы пытаетесь создать массив (array) с отрицательным элементом.

Подкласс Exception так же имеет особое значение в языке Java: RuntimeException.
Runtime Exceptions
Класс RuntimeException представляет исключения произшедшие во время запуска программы. Пример исключения при запуске это NullPointerException, происходит когда вы заходите в метод или поле объекта через ссылку null. Для таких испключений обычно проверяют чтобы гарантировать этот объект отличается от null, чем искать исключение.
Так как исключения при запуске очень распространены и пытаться найти или определить их всех это не эффективно. Компилятор Java не проверяет эти исключения в процессе компиляции кода .
Java определяет некоторые классы RuntimeException. Вы можете словить (Catch) эти исключения как и другие обычные исключения. Методы которые могут выбрасывать RuntimeException не требуют объявленмя по его определению. Помимо этого, вы можете создать ваш собственный подкласс RuntimeException.

3. Обработка исключения через try-catch

Напишем exception унаследованный от класса Exception.
AgeException.java
package org.o7planning.tutorial.exception.basic;

public class AgeException extends Exception {

  public AgeException(String message) {
      super(message);
  }

}
TooYoungException.java
package org.o7planning.tutorial.exception.basic;

public class TooYoungException extends AgeException {

 public TooYoungException(String message) {
     super(message);
 }

}
TooOldException.java
package org.o7planning.tutorial.exception.basic;

public class TooOldException extends AgeException {

 public TooOldException(String message) {
     super(message);
 }

}
И класс AgeUtils со статистическим методом для проверки возраста.
AgeUtils.java
package org.o7planning.tutorial.exception.basic;

public class AgeUtils {

	// Этот метод выполняет обязанность проверки возраста.
	// Если возраст меньше 18 он выбросит исключение TooYoungException
	// Если возраст старше 40 он выбросит исключение TooOldException
	public static void checkAge(int age) throws TooYoungException, TooOldException {
		if (age < 18) {
			// Если возраст меньше 18, выбросится исключение
			// Этот метод заканчивается здесь.
			throw new TooYoungException("Age " + age + " too young");
		} else if (age > 40) {
			// Если возраст старше 40, выбросится исключение.
			// Этот метод заканчивается здесь.
			throw new TooOldException("Age " + age + " too old");
		}
		// Если возраст является в пределах 18-40.
		// Этот код будет запущен.
		System.out.println("Age " + age + " OK!");
	}
}
Checked Exception & Unchecked Exception:
  • AgeException это подкласс Exception, TooOldException и TooYoungException это 2 прямые подклассы AgeException, поэтому они являются "Checked Exception"
  • В методе AgeUtils.checkAge(int) выбрасывает это исключение, поэтому на объявлении вашего метода нужно из перечислить через ключевое слово "throws". Или вы можете объявить выбрасывание на более общем уровне
    • throws Exception.
  • Там где используется AgeUtils.checkAge(int) тоже должна быть обработка чтобы словить эти исключения, или продолжить выбрасывание наружу.
"Checked exception" будет проверен с помощью "Java Compiler".
У вас есть 2 варианта обработки:
  1. Выбросить наружу
  2. Словить и выпонить обработку исключения через try-catch.
TryCatchDemo1.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo1 {

	public static void main(String[] args) {

		// Начать рекрутирование.
		System.out.println("Start Recruiting ...");
		// Проверка возраста.
		System.out.println("Check your Age");
		int age = 50;

		try {

			AgeUtils.checkAge(age);

			System.out.println("You pass!");

		} catch (TooYoungException e) {

			// Сделать что-то здесь ..
			System.out.println("You are too young, not pass!");
			System.out.println(e.getMessage());

		} catch (TooOldException e) {
			// Сделать что-то здесь
			System.out.println("You are too old, not pass!");
			System.out.println(e.getMessage());

		}

	}
}
В примере ниже, мы словим исключения через родительские исключения. На высшем уровнем можно словить эти исключения и все наследованные исключения.
TryCatchDemo2.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo2 {

	public static void main(String[] args) {

		// Начать рекрутирование
		System.out.println("Start Recruiting ...");
		// Проверить ваш возвраст.
		System.out.println("Check your Age");
		int age = 15;

		try {
			// Здесь может получиться исключение TooOldException,
			// или TooYoungException
			AgeUtils.checkAge(age);

			System.out.println("You pass!");

		} catch (AgeException e) {
			// Если происходит исключение, вида AgeException.
			// Этот блок catch будет запущен.
			System.out.println("Your age invalid, you not pass");
			System.out.println(e.getMessage());

		}
	}
}
Вы так же можете собрать для обработки разные исключения в один блок catch, если они имеют одинаковый способ обработки в логике вашей программы.
TryCatchDemo3.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo3 {

	public static void main(String[] args) {

		System.out.println("Start Recruiting ...");

		System.out.println("Check your Age");
		int age = 15;

		try {
			// Здесь может произойти исключение TooOldException,
			// или TooYoungException
			AgeUtils.checkAge(age);

			System.out.println("You pass!");

		} catch (TooYoungException | TooOldException e) {
			// Поймать много исключений в 1 блоке Catch.
			System.out.println("Your age invalid, you not pass");
			System.out.println(e.getMessage());

		}
	}

}

4. Блок try-catch-finally

Мы уже ознакомились с улавливением ошибки через блок try-catch. Для полной обработки исключений используется try-catch-finally.
try {

   // Сделать что-то здесь.
} catch (Exception1 e) {

   // Сделать что-то здесь.
} catch (Exception2 e) {

   // Сделать что-то здесь.
} finally {

   // Блок finally всегда выполняется.
   // Сделать что-то здесь.
}
TryCatchFinallyDemo.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchFinallyDemo {

	public static void main(String[] args) {

		String text = "001234A2";

		int value = toInteger(text);

		System.out.println("Value= " + value);

	}

	public static int toInteger(String text) {
		try {

			System.out.println("Begin parse text: " + text);

			// Здесь может произойти исключение NumberFormatException.
			int value = Integer.parseInt(text);

			return value;

		} catch (NumberFormatException e) {

			// В случае 'text' не является числом.
			// Данный блок catch будет выполнен.
			System.out.println("Number format exception " + e.getMessage());

			// Когда происходит NumberFormatException, возвращает 0.
			return 0;

		} finally {

			System.out.println("End parse text: " + text);

		}
	}

}
Это поток программы. Блок finally всегда выполняется.

5. Свернуть один Exception в другой Exception 

Нам нужно несколько классов участвующих в данном примере:
  • Person: Симулирует человека участвующего в рекрутировании в компанию с информацией
    • Имя, возраст, пол.
  • GenderException: Исключить пол.
  • ValidateException: Исключение оценки участников.
  • ValidateUtils: Класс имеющий статистический метод оценивающий подходит или кандидат или нет.
    • Стандартом является возраст 18-40
    • и мужской пол.
Person.java
package org.o7planning.tutorial.exception.wrap;

public class Person {

  public static final String MALE = "male";
  public static final String FEMALE = "female";

  private String name;
  private String gender;
  private int age;

  public Person(String name, String gender, int age) {
      this.name = name;
      this.gender = gender;
      this.age = age;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getGender() {
      return gender;
  }

  public void setGender(String gender) {
      this.gender = gender;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }
}
GenderException.java
package org.o7planning.tutorial.exception.wrap;

// Исключение пола.
public class GenderException extends Exception {
 
      public GenderException(String message)  {
    	  super(message);
      }
}
Класс ValidateException обвалакивает другой Exception.
ValidateException.java
package org.o7planning.tutorial.exception.wrap;

public class ValidateException extends Exception {
	
	// Обернуть(wrap) Exception в другой Exception 
	public ValidateException(Exception e)  {
		super(e);
	}

}
ValidateUtils.java
package org.o7planning.tutorial.exception.wrap;

import org.o7planning.tutorial.exception.basic.AgeUtils;

public class ValidateUtils {

	// Метод проверки 1 кандидатуры.
	public static void checkPerson(Person person) throws ValidateException {
		try {

			// Проверка возраста.
			// Действительно это в рамках 18-40
			// Этот метод может выбросить TooOldException,TooYoungException.
			AgeUtils.checkAge(person.getAge());

		} catch (Exception e) {

			//  Если недействительно
			// Упаковать данное исключение с помощью ValidateException, и выбросить (throw).
			throw new ValidateException(e);

		}

		// Если этот человек является Женщиной, значит недействительно.
		if (person.getGender().equals(Person.FEMALE)) {

			GenderException e = new GenderException("Do not accept women");
			throw new ValidateException(e);

		}
	}

}
WrapperExceptionDemo.java
package org.o7planning.tutorial.exception.wrap;

public class WrapperExceptionDemo {

	public static void main(String[] args) {

		// Кандидатура.
		Person person = new Person("Marry", Person.FEMALE, 20);

		try {

			// Исключение может произойти здесь.
			ValidateUtils.checkPerson(person);

		} catch (ValidateException wrap) {

			// Получить действительную причину.
			// Может быть TooYoungException, TooOldException, GenderException.
			Exception cause = (Exception) wrap.getCause();

			if (cause != null) {
				System.out.println("Not pass, cause: " + cause.getMessage());
			} else {
				System.out.println(wrap.getMessage());
			}

		}
	}

}

6. RuntimeException т подклассы

Класс RuntimeException и подклассы являются "Unchecked exception". Он не проверяется компилятором java во время компиляции. В некоторых случаях вы можете написать ваши собственные исключения наследованные от этих веток. Есть некоторые исключения в этой ветке, требующие внимания.

Ниже являются некоторые классы принадлежащие веткам RuntimeException (Конечно не все).
Попробуем обработать некоторые примеры с данными видами исключений:
NullPointerException
Это один из общих исключений, и часто создающий ошибки в программе. Исключение выбрасывается когда вы вызываете метод или заходите в поля объекта null.
NullPointerExceptionDemo.java
package org.o7planning.tutorial.exception.runtime;

public class NullPointerExceptionDemo {

	// Например это метод, который может вернуть строку null.
	public static String getString() {
		if (1 == 2) {
			return "1==2 !!";
		}
		return null;
	}

	public static void main(String[] args) {

		// Это переменная с ссылкой отличающаяся от null.
		String text1 = "Hello exception";

		// Вызвать метод для получения длины строки.
		int length = text1.length();

		System.out.println("Length text1 = " + length);

		// Это переменная с ссылкой null.
		String text2 = getString();

		// Вызвать метод для получения длины строки.
		// NullPointerException произойдет здесь.
		// Это исключение, появляющееся во время запуска (runtime).
		// (ВидRuntimeException).
		// Компилятор (compiler) Java не обязателен
		// вам нужно поймать (catch) его во время компиляции (compile-time).
		length = text2.length(); // ==> exception!

		System.out.println("Finish!");
	}

}
Results of running the example:
Length text1 = 15
Exception in thread "main" java.lang.NullPointerException
    at org.o7planning.tutorial.exception.runtime.NullPointerExceptionDemo.main(NullPointerExceptionDemo.java:51)
В действительности, как и обработка других исключений, вы можете использовать try-catch чтобы уловить это исключение для обработки. Но это механический способ, обычно стоит проверить, чтобы удостовериться объект отличается от null перед тем, как его использовать.

Вы можете изменить код как внизу, чтобы избежать NullPointerException:
// Метод getString() возвращает значение null.
// Это объект с ссылкой null.
String text2 = getString();

// Проверка, чтобы удостовериться text2 отличается от null.
// Вместо использования try-catch.
if (text2 != null) {
  length = text2.length();
}
ArrayIndexOfBoundException
Это исключение выбрасывается когда вы пытаетесь зайти в элемент с недействительным индексом в массиве. Например массив имеет 10 элементов, а вы заходите в элемент с индексом 20.
ArrayIndexOfBoundsExceptionDemo.java
package org.o7planning.tutorial.exception.runtime;

public class ArrayIndexOfBoundsExceptionDemo {

	public static void main(String[] args) {

		String[] strs = new String[] { "One", "Two", "Three" };
		// Доступ в элемент индекса 0.
		String str1 = strs[0];

		System.out.println("String at 0 = " + str1);

		// Доступ в элемент индекса 5
		// ArrayIndexOfBoundsException происходит здесь.
		String str2 = strs[5];

		System.out.println("String at 5 = " + str2);

	}

}
Чтобы избежать ArrayIndexOfBoundsException вам стоит проверить массив вместо того, чтобы использовить try-catch.
if (strs.length > 5) {
   String str2 = strs[5];
   System.out.println("String at 5 = " + str2);
} else {
   System.out.println("No elements with index 5");
}

Java Basic

Show More