Что такое исключение
Исключение — объект, описывающий ошибку во время выполнения. Если его не обработать, программа завершится со stack trace.
public class Crash { public static void main(String[] args) { int[] data = new int[3]; System.out.println(data[5]); }}Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3 at Crash.main(Crash.java:4)Иерархия
Throwable├── Error (серьезные сбои JVM: OutOfMemoryError, StackOverflowError)└── Exception ├── IOException (checked) ├── SQLException (checked) └── RuntimeException (unchecked) ├── NullPointerException ├── IllegalArgumentException ├── IllegalStateException ├── ArrayIndexOutOfBoundsException ├── ClassCastException ├── NumberFormatException └── ArithmeticException- Checked — компилятор требует обработать или объявить через
throws. Обычно внешние ошибки: файл, сеть, база. - Unchecked (
RuntimeExceptionи наследники) — обычно ошибки программиста. - Error — ловить не нужно.
try-catch
public class TryCatch { public static void main(String[] args) { try { int result = 10 / 0; System.out.println(result); } catch (ArithmeticException e) { System.out.println("Деление на ноль: " + e.getMessage()); } System.out.println("Программа продолжает работу"); }}Несколько catch
Более конкретные исключения указываются раньше общих:
public class MultiCatch { public static void main(String[] args) { String[] inputs = {"42", "abc", null}; for (String input : inputs) { try { int value = Integer.parseInt(input); System.out.println("Число: " + value); } catch (NumberFormatException e) { System.out.println("Не число: " + input); } catch (RuntimeException e) { System.out.println("Другая ошибка: " + e); } } }}Объединение в одном catch:
try { riskyOperation();} catch (IllegalArgumentException | IllegalStateException e) { System.out.println(e.getMessage());}finally
Блок finally выполняется всегда, независимо от того, было ли исключение:
public class Finally { static int test() { try { return 1; } finally { System.out.println("finally выполнен"); } } public static void main(String[] args) { System.out.println(test()); }}throw и throws
throw создает исключение, throws объявляет, что метод может его выбросить:
import java.io.IOException;public class ThrowDemo { static void checkAge(int age) { if (age < 0) { throw new IllegalArgumentException("Возраст не может быть отрицательным"); } } static void readConfig(String path) throws IOException { if (path.isEmpty()) { throw new IOException("Пустой путь"); } } public static void main(String[] args) { try { checkAge(-5); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); } try { readConfig(""); } catch (IOException e) { System.out.println("IO: " + e.getMessage()); } }}try-with-resources
Ресурсы, реализующие AutoCloseable, закрываются автоматически, даже при ошибке:
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class ReadFile { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { System.out.println("Ошибка чтения: " + e.getMessage()); } }}Собственный ресурс:
public class Resource implements AutoCloseable { private final String name; public Resource(String name) { this.name = name; System.out.println("Открыт " + name); } @Override public void close() { System.out.println("Закрыт " + name); } public static void main(String[] args) { try (Resource a = new Resource("A"); Resource b = new Resource("B")) { System.out.println("Работа"); } }}Вывод: Открыт A, Открыт B, Работа, Закрыт B, Закрыт A. Закрытие идет в обратном порядке.
Собственные исключения
public class InsufficientFundsException extends Exception { private final double shortage; public InsufficientFundsException(String message, double shortage) { super(message); this.shortage = shortage; } public double getShortage() { return shortage; }}public class Wallet { private double balance; public Wallet(double balance) { this.balance = balance; } public void pay(double amount) throws InsufficientFundsException { if (amount > balance) { throw new InsufficientFundsException("Не хватает средств", amount - balance); } balance -= amount; } public static void main(String[] args) { Wallet wallet = new Wallet(100); try { wallet.pay(150); } catch (InsufficientFundsException e) { System.out.println(e.getMessage() + ": " + e.getShortage()); } }}Для ошибок программиста наследуйтесь от RuntimeException.
Цепочки исключений
Сохраняйте исходную причину:
public class ServiceException extends RuntimeException { public ServiceException(String message, Throwable cause) { super(message, cause); }}public class Chain { static void load() { try { Integer.parseInt("xyz"); } catch (NumberFormatException e) { throw new ServiceException("Не удалось загрузить настройки", e); } } public static void main(String[] args) { try { load(); } catch (ServiceException e) { System.out.println(e.getMessage()); System.out.println("Причина: " + e.getCause()); } }}Хорошие практики
- Ловите конкретные исключения, а не
Exceptionи тем болееThrowable. - Не оставляйте пустой
catch: ошибка пропадет бесследно. - Не используйте исключения для обычного управления потоком.
- Проверяйте аргументы в начале метода и бросайте
IllegalArgumentException. - Сообщение исключения должно содержать данные, помогающие найти проблему.
- Логируйте исключение целиком, а не только
getMessage().
NullPointerException
Самая частая ошибка. Способы защиты:
import java.util.Objects;import java.util.Optional;public class NullSafety { static String describe(String text) { Objects.requireNonNull(text, "text не должен быть null"); return text.trim(); } static Optional<String> find(String key) { if ("a".equals(key)) { return Optional.of("значение"); } return Optional.empty(); } public static void main(String[] args) { System.out.println(find("a").orElse("нет")); System.out.println(find("b").orElse("нет")); System.out.println(find("a").map(String::toUpperCase).orElse("")); }}Приемы:
- сравнивайте константу с переменной:
"yes".equals(answer); - возвращайте пустую коллекцию вместо
null; - используйте
Optionalв возвращаемых значениях, но не в полях и параметрах.
