← Назад к вопросам

Опиши иерархию исключений

1.0 Junior🔥 171 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Иерархия исключений в Java

Исключения в Java образуют иерархию классов, начинающуюся с Throwable. Правильное понимание этой иерархии критично для правильной обработки ошибок и написания robust кода.

Корень иерархии: Throwable

Все ошибки и исключения наследуются от Throwable:

public class Throwable implements Serializable {
    // Основные методы
    public String getMessage();
    public void printStackTrace();
    public StackTraceElement[] getStackTrace();
    public Throwable getCause();
    public synchronized Throwable initCause(Throwable cause);
    public String toString();
}

// Иерархия:
// Throwable
//  ├── Error (не ловим!)
//  │   ├── OutOfMemoryError
//  │   ├── StackOverflowError
//  │   ├── VirtualMachineError
//  │   └── LinkageError
//  └── Exception (ловим)
//      ├── Checked Exception
//      │   ├── IOException
//      │   ├── SQLException
//      │   ├── ParseException
//      │   └── ...
//      └── RuntimeException (Unchecked)
//          ├── NullPointerException
//          ├── ArrayIndexOutOfBoundsException
//          ├── IllegalArgumentException
//          ├── ArithmeticException
//          └── ...

1. Error — Критические ошибки JVM

Error — проблемы, которые приложение НЕ может обработать. Это проблемы JVM, а не приложения.

// OutOfMemoryError — нет памяти
public class MemoryTest {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]); // 1MB
            // java.lang.OutOfMemoryError: Java heap space
        }
    }
}

// StackOverflowError — переполнение стека
public class RecursionTest {
    public void recursive() {
        recursive(); // Бесконечная рекурсия
        // java.lang.StackOverflowError
    }
}

// VirtualMachineError — ошибка JVM
// LinkageError — ошибка загрузки класса

// ❌ НЕ ловим Error!
try {
    // что-то
} catch (Error e) { // Плохая практика
    // Это скроет критичные ошибки
}

// ✅ Только для очень специфичных случаев
try {
    // что-то
} catch (OutOfMemoryError e) {
    // Может быть graceful shutdown
    System.err.println("Out of memory, shutting down");
    System.exit(1);
}

2. Exception — Обрабатываемые исключения

2.1 Checked Exception

Checked Exception — компилятор ТРЕБУЕТ обработать или пробросить. Наследуют Exception, но не RuntimeException.

// IOException — checked exception
public class FileReader {
    // Вариант 1: Обработать
    public String readFile(String path) {
        try {
            Files.readString(Paths.get(path));
        } catch (IOException e) {
            System.err.println("Cannot read file: " + e.getMessage());
            return "";
        }
    }
    
    // Вариант 2: Пробросить
    public String readFile(String path) throws IOException {
        return Files.readString(Paths.get(path)); // Компилятор доволен
    }
}

// SQLException — checked exception
public class DatabaseService {
    public User getUserById(Long id) throws SQLException {
        Connection conn = getConnection();
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE id = " + id);
        // IOException, SQLException ДОЛЖНЫ быть обработаны или пробросены
    }
}

// ParseException, ClassNotFoundException, FileNotFoundException
public class ConfigLoader {
    public void loadConfig(String path) throws IOException, ParseException {
        String json = Files.readString(Paths.get(path)); // IOException
        JSONObject obj = new JSONObject(json); // ParseException
    }
}

// Custom checked exception
public class PaymentFailedException extends Exception {
    public PaymentFailedException(String message) {
        super(message);
    }
    
    public PaymentFailedException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class PaymentService {
    public void processPayment(Order order) throws PaymentFailedException {
        try {
            // Вызов платежного API
            if (paymentAPI.charge(order.getAmount()) == null) {
                throw new PaymentFailedException(
                    "Payment gateway returned null response"
                );
            }
        } catch (HttpException e) {
            throw new PaymentFailedException(
                "Payment service unavailable", e
            );
        }
    }
}

Когда использовать Checked Exception:

  • Predictable, recoverable ошибки
  • IOException (файл может быть переместен)
  • SQLException (БД может быть недоступна)
  • Но НЕ для всех случаев (смотри ниже)

2.2 Unchecked Exception (RuntimeException)

RuntimeException — не требует обработки в throws. Наследуют RuntimeException. Используются для программных ошибок.

// NullPointerException
public class NullPointerExample {
    public void process(User user) {
        String name = user.getName(); // NPE если user == null
        // Нет необходимости в throws NullPointerException
    }
}

// ArrayIndexOutOfBoundsException
public class ArrayExample {
    public void access(int[] arr, int index) {
        int value = arr[index]; // ArrayIndexOutOfBoundsException если index >= arr.length
        // Не нужно в throws
    }
}

// IllegalArgumentException
public class ValidationService {
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException(
                "Age must be between 0 and 150, got: " + age
            );
        }
        this.age = age;
    }
}

// ArithmeticException
public class Calculator {
    public int divide(int a, int b) {
        return a / b; // ArithmeticException при b = 0
    }
}

// ClassCastException
public class TypeConversion {
    public void process(Object obj) {
        String str = (String) obj; // CCE если obj не String
    }
}

// IndexOutOfBoundsException
public class ListAccess {
    public void access(List<String> list, int index) {
        String item = list.get(index); // IndexOutOfBoundsException
    }
}

// Custom unchecked exception
public class BusinessRuleViolation extends RuntimeException {
    public BusinessRuleViolation(String message) {
        super(message);
    }
}

public class OrderService {
    public void createOrder(Order order) {
        if (order.getAmount() < 10) {
            throw new BusinessRuleViolation(
                "Minimum order amount is 10"
            ); // Не нужен throws
        }
        // создание заказа
    }
}

Правильная обработка исключений

// Хорошая иерархия обработки (от специфичного к общему)
public void process() {
    try {
        riskyOperation();
    } catch (SpecificException e) {
        // Самая специфичная обработка
        handleSpecific(e);
    } catch (IOException e) {
        // Более общая
        handleIO(e);
    } catch (Exception e) {
        // Самая общая
        handleGeneral(e);
    } finally {
        // Всегда выполняется
        cleanup();
    }
}

// Правильное логирование
try {
    someMethod();
} catch (IOException e) {
    logger.error("Failed to read file", e); // Пробросить stack trace
    // ❌ Плохо:
    // logger.error(e.toString()); // Потеряется stack trace
    // System.out.println(e); // Не логируется
}

// Try-with-resources
try (InputStream is = new FileInputStream("file.txt")) {
    byte[] data = is.readAllBytes();
    // Автоматически закроется даже при исключении
} catch (IOException e) {
    logger.error("Cannot read file", e);
}

// Явное преобразование исключения
public class RepositoryService {
    public User getUserById(Long id) {
        try {
            return queryDatabase(id);
        } catch (SQLException e) {
            // Преобразуем checked -> unchecked
            throw new DataAccessException(
                "Cannot fetch user: " + id, e
            ); // Пробросить с cause
        }
    }
}

// Цепь исключений (exception chaining)
try {
    // что-то
} catch (IOException e) {
    throw new ApplicationException(
        "Failed to process request",
        e  // Сохраняем исходное исключение как cause
    );
}

Сравнение Checked vs Unchecked

АспектCheckedUnchecked
НаследуетException (не Runtime)RuntimeException
ОбработкаОБЯЗАТЕЛЬНА (throws/try-catch)ОПЦИОНАЛЬНА
КогдаPredictable errorsProgramming errors
ПримерыIOException, SQLExceptionNullPointerException
Декларацияpublic void method() throws IOExceptionне нужна

Best Practices

  1. Не ловка generic Exception:
// ❌ Плохо
try {
    code();
} catch (Exception e) { // Слишком широко
    e.printStackTrace();
}

// ✅ Хорошо
try {
    code();
} catch (SpecificException e) {
    handleSpecific(e);
} catch (IOException e) {
    handleIO(e);
}
  1. Пробрасывай с информацией:
// ❌ Плохо
throw new RuntimeException("Error");

// ✅ Хорошо
throw new RuntimeException(
    "Cannot process order " + orderId + ": invalid amount",
    originalException
);
  1. Используй custom exceptions:
public class InvalidUserException extends RuntimeException {
    private final Long userId;
    
    public InvalidUserException(Long userId) {
        super("User not found: " + userId);
        this.userId = userId;
    }
    
    public Long getUserId() {
        return userId;
    }
}

Правильная иерархия и обработка исключений — основа надежного и поддерживаемого кода.

Опиши иерархию исключений | PrepBro