Обработка исключений в Java: Try-Catch-Finally, Throw, Throws и пользовательские исключения
Это полное руководство по обработке исключений в Java, включающее try-catch-finally, throw, throws и пользовательские исключения с практическими примерами.
Вкратце
Обработка исключений позволяет контролировать обработку ошибок в Java. try-catch-finally перехватывают исключения, throw/throws объявляют их, а пользовательские исключения обеспечивают специфичную обработку ошибок.
Определение
Обработка исключений — это механизм для работы с ошибками и нештатными ситуациями при выполнении программы. Java использует структурированную иерархию исключений.
Иерархия исключений:
- Throwable: базовый класс для всех ошибок и исключений
- Error: серьёзные системные ошибки (не обрабатываются)
- Exception: обрабатываемые исключения
- Checked Exceptions: проверяются на этапе компиляции
- Unchecked Exceptions: ошибки выполнения (RuntimeException)
Ключевые концепции:
Try-Catch-Finally
- try: блок с потенциально ошибочным кодом
- catch: блок для обработки конкретных исключений
- finally: блок, выполняемый всегда (даже при исключении)
- try-with-resources: автоматическое управление ресурсами
Throw и Throws
- throw: явное выброс исключения
- throws: объявление исключений в сигнатуре метода
- rethrow: передача исключения дальше
Пользовательские исключения
- Собственные классы исключений: специфичная обработка ошибок
- Business Exceptions: ошибки предметной области
- Validation Exceptions: ошибки валидации входных данных
Ключевые моменты
- Обработка исключений: структурированная работа с ошибками в Java
- Try-Catch-Finally: основные блоки обработки
- Checked vs Unchecked Exceptions: ошибки компиляции и выполнения
- Throw vs Throws: выброс и объявление исключений
- Пользовательские исключения: создание собственных классов
- Иерархия исключений: Throwable → Error/Exception → RuntimeException
- Важно для надёжного и отказоустойчивого ПО
Основные компоненты
- Иерархия исключений: Throwable, Error, Exception, RuntimeException
- Try-Catch-Finally: структура обработки ошибок
- Throw/Throws: выброс и объявление исключений
- Пользовательские исключения: специальные классы ошибок
- Try-with-Resources: автоматическое управление ресурсами
- Exception Chaining: связывание исключений
- Best Practices: рекомендации по обработке исключений
- Logging: журналирование ошибок
Практические примеры
1. Базовая обработка исключений
import java.io.*;
import java.util.*;
public class ExceptionGrundlagen {
public static void main(String[] args) {
System.out.println("=== Exception Handling Grundlagen ===");
// Einfache try-catch
einfachesTryCatch();
// Mehrere catch-Blöcke
mehrfachCatch();
// Finally-Block
finallyDemo();
// Try-with-Resources
tryWithResourcesDemo();
// Exception-Auslösung
exceptionAusloesen();
}
private static void einfachesTryCatch() {
System.out.println("\n--- Einfaches Try-Catch ---");
try {
// Potentiell fehlerhafter Code
int ergebnis = 10 / 0; // ArithmeticException
System.out.println("Ergebnis: " + ergebnis);
} catch (ArithmeticException e) {
System.out.println("Fehler: Division durch Null");
System.out.println("Exception-Typ: " + e.getClass().getSimpleName());
System.out.println("Nachricht: " + e.getMessage());
}
System.out.println("Programm läuft weiter");
}
private static void mehrfachCatch() {
System.out.println("\n--- Mehrfache Catch-Blöcke ---");
String[] zahlen = {"10", "5", "abc", "2"};
for (String zahlString : zahlen) {
try {
int zahl = Integer.parseInt(zahlString);
int ergebnis = 100 / zahl;
System.out.println("100 / " + zahl + " = " + ergebnis);
} catch (NumberFormatException e) {
System.out.println("Fehler: '" + zahlString + "' ist keine Zahl");
} catch (ArithmeticException e) {
System.out.println("Fehler: Division durch Null bei " + zahlString);
} catch (Exception e) {
// Allgemeiner Exception-Handler
System.out.println("Unerwarteter Fehler: " + e.getMessage());
}
}
}
private static void finallyDemo() {
System.out.println("\n--- Finally-Block Demo ---");
BufferedReader reader = null;
try {
// Ressource öffnen
reader = new BufferedReader(new FileReader("nichtexistente.txt"));
String zeile = reader.readLine();
System.out.println("Gelesen: " + zeile);
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden: " + e.getMessage());
} catch (IOException e) {
System.out.println("Lesefehler: " + e.getMessage());
} finally {
// Wird immer ausgeführt
System.out.println("Finally-Block wird ausgeführt");
if (reader != null) {
try {
reader.close();
System.out.println("Reader geschlossen");
} catch (IOException e) {
System.out.println("Fehler beim Schließen: " + e.getMessage());
}
}
}
}
private static void tryWithResourcesDemo() {
System.out.println("\n--- Try-with-Resources Demo ---");
// Automatische Ressourcenverwaltung
try (BufferedReader reader = new BufferedReader(new FileReader("beispiel.txt"))) {
String zeile;
int zeilenNummer = 1;
while ((zeile = reader.readLine()) != null) {
System.out.println("Zeile " + zeilenNummer + ": " + zeile);
zeilenNummer++;
if (zeilenNummer > 3) {
throw new IOException("Künstlicher Fehler nach 3 Zeilen");
}
}
} catch (FileNotFoundException e) {
System.out.println("Datei nicht gefunden: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO-Fehler: " + e.getMessage());
} finally {
System.out.println("Try-with-Resources beendet (Reader automatisch geschlossen)");
}
}
private static void exceptionAusloesen() {
System.out.println("\n--- Exception-Auslösung ---");
try {
validiereAlter(15); // Löst Exception aus
} catch (IllegalArgumentException e) {
System.out.println("Validierungsfehler: " + e.getMessage());
}
try {
validiereAlter(25); // Kein Fehler
} catch (IllegalArgumentException e) {
System.out.println("Dies sollte nicht passieren: " + e.getMessage());
}
System.out.println("Alter-Validierung abgeschlossen");
}
private static void validiereAlter(int alter) {
if (alter < 18) {
throw new IllegalArgumentException("Alter muss mindestens 18 sein, aber war: " + alter);
}
System.out.println("Alter " + alter + " ist gültig");
}
}
2. Пользовательские исключения и клауза throws
// Классы пользовательских исключений
class GeschaeftsException extends Exception {
public GeschaeftsException(String nachricht) {
super(nachricht);
}
public GeschaeftsException(String nachricht, Throwable ursache) {
super(nachricht, ursache);
}
}
class ValidierungsException extends RuntimeException {
private String feld;
public ValidierungsException(String feld, String nachricht) {
super(nachricht);
this.feld = feld;
}
public String getFeld() {
return feld;
}
}
class DatenbankException extends Exception {
private int fehlercode;
public DatenbankException(String nachricht, int fehlercode) {
super(nachricht);
this.fehlercode = fehlercode;
}
public int getFehlercode() {
return fehlercode;
}
}
// Классы сервисов с обработкой исключений
class KundenService {
// Метод с клаузой throws
public Kunde findeKunde(int kundenId) throws GeschaeftsException, DatenbankException {
try {
// Имитируем доступ к базе данных
if (kundenId < 1000) {
throw new DatenbankException("Ungültige Kunden-ID: " + kundenId, 404);
}
if (kundenId == 1234) {
throw new DatenbankException("Datenbankverbindung fehlgeschlagen", 500);
}
// Имитируем успешный доступ
return new Kunde(kundenId, "Max Mustermann", "max@example.com");
} catch (SQLException e) {
// Цепочка исключений, сохраняем исходное исключение
throw new DatenbankException("Datenbankfehler bei Kundensuche", 503);
}
}
// Метод с пользовательским исключением
public void kundeAnlegen(Kunde kunde) throws GeschaeftsException {
try {
validiereKunde(kunde);
// Имитируем бизнес-логику
if (kunde.getName().toLowerCase().contains("test")) {
throw new GeschaeftsException("Test-Kunden dürfen nicht angelegt werden");
}
// Клиент будет сохранён в базу данных
System.out.println("Kunde angelegt: " + kunde.getName());
} catch (ValidierungsException e) {
// Пробрасываем исключение с дополнительной информацией
throw new GeschaeftsException("Kundendaten ungültig: " + e.getMessage(), e);
}
}
private void validiereKunde(Kunde kunde) {
if (kunde.getName() == null || kunde.getName().trim().isEmpty()) {
throw new ValidierungsException("name", "Name darf nicht leer sein");
}
if (kunde.getEmail() == null || !kunde.getEmail().contains("@")) {
throw new ValidierungsException("email", "Ungültige E-Mail-Adresse");
}
if (kunde.getName().length() > 100) {
throw new ValidierungsException("name", "Name zu lang (max 100 Zeichen)");
}
}
}
// Модель данных
class Kunde {
private int id;
private String name;
private String email;
public Kunde(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Геттеры
public int getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}
// Главный класс для демонстрации
public class CustomExceptionDemo {
public static void main(String[] args) {
System.out.println("=== Custom Exceptions Demo ===");
KundenService service = new KundenService();
// Демо 1: успешный поиск клиента
try {
Kunde kunde = service.findeKunde(1001);
System.out.println("Kunde gefunden: " + kunde.getName());
} catch (GeschaeftsException | DatenbankException e) {
System.out.println("Fehler bei Kundensuche: " + e.getMessage());
}
// Демо 2: ошибка базы данных
try {
service.findeKunde(999);
} catch (GeschaeftsException e) {
System.out.println("Geschäftsfehler: " + e.getMessage());
} catch (DatenbankException e) {
System.out.println("Datenbankfehler (Code " + e.getFehlercode() + "): " + e.getMessage());
}
// Демо 3: ошибка валидации при создании
try {
Kunde ungueltigerKunde = new Kunde(0, "", "ungueltig@");
service.kundeAnlegen(ungueltigerKunde);
} catch (GeschaeftsException e) {
System.out.println("Fehler beim Kundenanlegen: " + e.getMessage());
// Выводим исходное исключение
if (e.getCause() instanceof ValidierungsException) {
ValidierungsException ve = (ValidierungsException) e.getCause();
System.out.println("Validierungsfehler in Feld '" + ve.getFeld() + "'");
}
}
// Демо 4: нарушение бизнес-правила
try {
Kunde testKunde = new Kunde(0, "Test User", "test@example.com");
service.kundeAnlegen(testKunde);
} catch (GeschaeftsException e) {
System.out.println("Business Rule Fehler: " + e.getMessage());
}
// Демо 5: логирование исключений
exceptionLoggingDemo();
}
private static void exceptionLoggingDemo() {
System.out.println("\n--- Exception Logging Demo ---");
try {
// Имитируем сложную операцию
komplexeOperation();
} catch (Exception e) {
// Структурированное логирование
logException(e);
}
}
private static void komplexeOperation() throws Exception {
try {
// Первая операция
ersteOperation();
// Вторая операция
zweiteOperation();
} catch (IllegalArgumentException e) {
// Пробрасываем исключение с контекстной информацией
throw new Exception("Fehler in komplexer Operation", e);
}
}
private static void ersteOperation() {
if (Math.random() > 0.5) {
throw new IllegalArgumentException("Fehler in erster Operation");
}
System.out.println("Erste Operation erfolgreich");
}
private static void zweiteOperation() {
if (Math.random() > 0.7) {
throw new IllegalArgumentException("Fehler in zweiter Operation");
}
System.out.println("Zweite Operation erfolgreich");
}
private static void logException(Exception e) {
System.out.println("=== Exception Log ===");
System.out.println("Typ: " + e.getClass().getSimpleName());
System.out.println("Nachricht: " + e.getMessage());
System.out.println("Stack Trace:");
// Выводим стек вызовов
for (StackTraceElement element : e.getStackTrace()) {
System.out.println(" at " + element.getClassName() +
"." + element.getMethodName() +
"(" + element.getFileName() +
":" + element.getLineNumber() + ")");
}
// Логируем исходное исключение
if (e.getCause() != null) {
System.out.println("Ursache: " + e.getCause().getClass().getSimpleName() +
" - " + e.getCause().getMessage());
}
}
}
3. Продвинутые паттерны обработки исключений
import java.util.*;
import java.util.function.*;
public class AdvancedExceptionPatterns {
// Result Pattern для Fehlerbehandlung без исключений
static class Result<T> {
private final T value;
private final Exception error;
private final boolean success;
private Result(T value, Exception error, boolean success) {
this.value = value;
this.error = error;
this.success = success;
}
public static <T> Result<T> success(T value) {
return new Result<>(value, null, true);
}
public static <T> Result<T> failure(Exception error) {
return new Result<>(null, error, false);
}
public boolean isSuccess() { return success; }
public boolean isFailure() { return !success; }
public T getValue() {
if (!success) {
throw new IllegalStateException("Нет значения при ошибке");
}
return value;
}
public Exception getError() {
if (success) {
throw new IllegalStateException("Нет ошибки при успехе");
}
return error;
}
public <U> Result<U> map(Function<T, U> mapper) {
if (success) {
try {
return Result.success(mapper.apply(value));
} catch (Exception e) {
return Result.failure(e);
}
} else {
return Result.failure(error);
}
}
public <U> Result<U> flatMap(Function<T, Result<U>> mapper) {
if (success) {
try {
return mapper.apply(value);
} catch (Exception e) {
return Result.failure(e);
}
} else {
return Result.failure(error);
}
}
public T orElse(T defaultValue) {
return success ? value : defaultValue;
}
}
// Обёртки исключений для функциональных интерфейсов
@FunctionalInterface
interface CheckedSupplier<T> {
T get() throws Exception;
}
@FunctionalInterface
interface CheckedRunnable {
void run() throws Exception;
}
@FunctionalInterface
interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
// Утилиты для обработки исключений
static class ExceptionUtils {
public static <T> Optional<T> optionalOf(CheckedSupplier<T> supplier) {
try {
return Optional.ofNullable(supplier.get());
} catch (Exception e) {
return Optional.empty();
}
}
public static <T> Result<T> resultOf(CheckedSupplier<T> supplier) {
try {
return Result.success(supplier.get());
} catch (Exception e) {
return Result.failure(e);
}
}
public static void unchecked(CheckedRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T, R> Function<T, R> uncheckedFunction(CheckedFunction<T, R> function) {
return t -> {
try {
return function.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
public static <T> Supplier<T> uncheckedSupplier(CheckedSupplier<T> supplier) {
return () -> {
try {
return supplier.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
// Механизм повторных попыток
static class RetryUtils {
public static <T> T retry(int maxAttempts, long delayMs, CheckedSupplier<T> supplier)
throws Exception {
Exception lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return supplier.get();
} catch (Exception e) {
lastException = e;
if (attempt == maxAttempts) {
break;
}
System.out.println("Попытка " + attempt + " не удалась, повтор через " + delayMs + "ms");
Thread.sleep(delayMs);
}
}
throw new Exception("Все " + maxAttempts + " попыток не удались", lastException);
}
public static <T> Optional<T> retryOptional(int maxAttempts, long delayMs,
CheckedSupplier<T> supplier) {
try {
return Optional.of(retry(maxAttempts, delayMs, supplier));
} catch (Exception e) {
return Optional.empty();
}
}
}
// Сервисные классы с продвинутыми паттернами
static class DatenbankService {
// С Result Pattern
public Result<String> leseDatenMitResult(String id) {
try {
// Имитация доступа к БД
if (id == null || id.isEmpty()) {
return Result.failure(new IllegalArgumentException("ID не может быть пусто"));
}
if (id.equals("error")) {
return Result.failure(new RuntimeException("Ошибка БД"));
}
String daten = "Дані для " + id;
return Result.success(daten);
} catch (Exception e) {
return Result.failure(e);
}
}
// С Optional
public Optional<String> leseDatenMitOptional(String id) {
return ExceptionUtils.optionalOf(() -> {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("ID не может быть пусто");
}
if (id.equals("notfound")) {
return null;
}
return "Дані для " + id;
});
}
// С повторными попытками
public String leseDatenMitRetry(String id) throws Exception {
return RetryUtils.retry(3, 1000, () -> {
// Имитация неустойчивого соединения
if (Math.random() > 0.7) {
throw new RuntimeException("Ошибка соединения");
}
return "Стабильные данные для " + id;
});
}
}
public static void main(String[] args) {
System.out.println("=== Продвинутые паттерны обработки исключений ===");
DatenbankService service = new DatenbankService();
// Демонстрация Result Pattern
System.out.println("\n--- Демонстрация Result Pattern ---");
Result<String> result1 = service.leseDatenMitResult("123");
System.out.println("Успех: " + result1.isSuccess());
result1.ifPresent(value -> System.out.println("Значение: " + value));
Result<String> result2 = service.leseDatenMitResult("error");
System.out.println("Успех: " + result2.isSuccess());
result2.ifPresentOrElse(
value -> System.out.println("Значение: " + value),
error -> System.out.println("Ошибка: " + error.getMessage())
);
// Цепочка Result
Result<Integer> laenge = result1
.map(String::length)
.map(laeng -> laeng * 2);
System.out.println("Удвоенная длина: " + laenge.orElse(0));
// Демонстрация Optional Pattern
System.out.println("\n--- Демонстрация Optional Pattern ---");
Optional<String> opt1 = service.leseDatenMitOptional("123");
opt1.ifPresent(daten -> System.out.println("Найдено: " + daten));
Optional<String> opt2 = service.leseDatenMitOptional("notfound");
System.out.println("Найдено: " + opt2.isPresent());
// Optional со значением по умолчанию
String ergebnis = opt2.orElse("Значение по умолчанию");
System.out.println("Результат: " + ergebnis);
// Демонстрация повторных попыток
System.out.println("\n--- Демонстрация повторных попыток ---");
try {
String daten = service.leseDatenMitRetry("123");
System.out.println("Успех после повтора: " + daten);
} catch (Exception e) {
System.out.println("Все повторы не удались: " + e.getMessage());
}
// Демонстрация Exception Utils
System.out.println("\n--- Демонстрация Exception Utils ---");
// Unchecked функциональный интерфейс
List<String> zahlen = Arrays.asList("10", "5", "abc", "2");
zahlen.stream()
.map(ExceptionUtils.uncheckedFunction(zahl -> Integer.parseInt(zahl) * 2))
.forEach(ergebnis -> System.out.println("Удвоено: " + ergebnis));
// Логирование исключения с контекстом
System.out.println("\n--- Исключение с контекстом ---");
try {
berechneKomplex(10, 0);
} catch (Exception e) {
logWithContext(e, Map.of(
"operation", "berechneKomplex",
"param1", 10,
"param2", 0,
"timestamp", System.currentTimeMillis()
));
}
}
private static int berechneKomplex(int a, int b) throws Exception {
if (b == 0) {
throw new ArithmeticException("Деление на ноль");
}
return a / b;
}
private static void logWithContext(Exception e, Map<String, Object> context) {
System.out.println("=== Исключение с контекстом ===");
System.out.println("Исключение: " + e.getClass().getSimpleName());
System.out.println("Сообщение: " + e.getMessage());
System.out.println("Контекст:");
context.forEach((key, value) ->
System.out.println(" " + key + ": " + value));
System.out.println("Stack Trace:");
Arrays.stream(e.getStackTrace())
.limit(3)
.forEach(element ->
System.out.println(" at " + element.getClassName() +
"." + element.getMethodName()));
}
// Вспомогательный метод для Result
private static <T> void ifPresent(Result<T> result, Consumer<T> action) {
if (result.isSuccess()) {
action.accept(result.getValue());
}
}
}
Паттерн Result предоставляет функциональный способ работы с ошибками без использования исключений. Вместо выброса исключения метод возвращает объект Result, содержащий либо успешное значение, либо информацию об ошибке. Это позволяет легко комбинировать операции с помощью методов map и flatMap.
CheckedSupplier, CheckedFunction и CheckedRunnable расширяют стандартные функциональные интерфейсы Java, позволяя им выбрасывать проверяемые исключения. Утилиты ExceptionUtils оборачивают эти интерфейсы, преобразуя проверяемые исключения в неправильные для использования в потоках и лямбдах.
Механизм повторных попыток в RetryUtils автоматически повторяет операцию заданное количество раз с интервалом, если выбросается исключение. Это особенно полезно при работе с нестабильными сетевыми соединениями или внешними сервисами.
DatenbankService демонстрирует три различных подхода к обработке ошибок. Метод leseDatenMitResult использует Result Pattern для явной обработки успеха и ошибки. leseDatenMitOptional использует Optional для случаев, где значение может отсутствовать. leseDatenMitRetry комбинирует обработку ошибок с механизмом повторных попыток.
В методе main показано использование всех этих паттернов. Обратите внимание на цепочку Result операций, где мы применяем несколько трансформаций подряд. Также демонстрируется использование CheckedFunction с потоками данных для безопасного преобразования строк в целые числа.
Функция logWithContext показывает, как логировать исключения вместе с контекстной информацией. Это помогает при отладке, так как предоставляет полную информацию об ошибке и состоянии приложения в момент её возникновения.
Иерархия исключений
Throwable
├── Error (Системные ошибки, не обрабатываются)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception (Обрабатываемые исключения)
├── Checked Exceptions (Проверка на этапе компиляции)
│ ├── IOException
│ │ ├── FileNotFoundException
│ │ └── SQLException
│ ├── ClassNotFoundException
│ └── InterruptedException
└── RuntimeException (Unchecked Exceptions)
├── NullPointerException
├── IllegalArgumentException
├── ArithmeticException
├── IndexOutOfBoundsException
└── NumberFormatException
Лучшие практики при работе с исключениями
Что нужно делать
- Ловить конкретные исключения: вместо универсальной
Exception - Освобождать ресурсы: через finally или try-with-resources
- Информативные сообщения: понятные описания ошибок
- Exception Chaining: сохранять исходное исключение
- Логирование: записывать исключения с контекстом
Что не нужно делать
- Пустые блоки catch: игнорировать исключения
- Exception swallowing: подавлять ошибки
- Over-catch: ловить слишком общие исключения
- Возвращать null: использовать Optional или Result вместо этого
- PrintStackTrace: в production коде
Try-with-Resources против Finally
Try-with-Resources (современный подход)
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
// Ресурсы закрываются автоматически
} catch (IOException e) {
// Обработка исключения
}
Блок Finally (традиционный подход)
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
// Работа с reader
} catch (IOException e) {
// Обработка исключения
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// Обработка ошибки при закрытии
}
}
}
Паттерны обработки исключений
Template Method Pattern
public abstract class DatabaseTemplate {
public final Result<T> execute(CheckedSupplier<T> operation) {
try {
Connection conn = getConnection();
try {
return Result.success(operation.get());
} finally {
conn.close();
}
} catch (Exception e) {
return Result.failure(e);
}
}
protected abstract Connection getConnection() throws SQLException;
}
Decorator Pattern
public class RetryDecorator<T> implements Supplier<T> {
private final Supplier<T> supplier;
private final int maxRetries;
public RetryDecorator(Supplier<T> supplier, int maxRetries) {
this.supplier = supplier;
this.maxRetries = maxRetries;
}
@Override
public T get() {
Exception lastException = null;
for (int i = 0; i <= maxRetries; i++) {
try {
return supplier.get();
} catch (Exception e) {
lastException = e;
if (i < maxRetries) {
// Ожидание перед повторной попыткой
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException(ie);
}
}
}
}
throw new RuntimeException("All retries failed", lastException);
}
}
Преимущества и недостатки
Преимущества обработки исключений
- Контроль ошибок: структурированная обработка сбоев
- Ясность кода: разделение нормального и аварийного потоков
- Надёжность: стабильные программы даже при ошибках
- Отладка: лучший анализ ошибок через stack-trace
- Поддерживаемость: централизованная обработка ошибок
Недостатки
- Производительность: обработка исключений имеет накладные расходы
- Сложность: вложенные конструкции try-catch
- Overhead: дополнительный код для обработки ошибок
- Злоупотребление: использование исключений для управления потоком выполнения
Частые вопросы на собеседовании
-
В чём различие между checked и unchecked исключениями? Checked исключения необходимо объявлять и обрабатывать, unchecked нет (RuntimeException).
-
Когда выполняется finally? Всегда, независимо от наличия исключения, даже если в try-блоке есть return.
-
Объясните try-with-resources! Автоматическое освобождение ресурсов для объектов, реализующих AutoCloseable.
-
Что такое Exception Chaining? Передача исключений с сохранением информации об исходной причине ошибки.
Основные источники
- https://docs.oracle.com/javase/tutorial/essential/exceptions/
- https://www.baeldung.com/java-exceptions
- https://effectivejava.com/



