Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие Error невозможно отловить
В Java существует различие между Exception и Error. Error'ы — это серьезные проблемы, которые обычно невозможно обработать. Рассмотрю, какие ошибки невозможно или нельзя ловить.
1. Error vs Exception иерархия
java.lang.Throwable (база для всех ошибок)
├── java.lang.Exception (обрабатываемые исключения)
│ ├── IOException
│ ├── SQLException
│ ├── RuntimeException (необработанные исключения)
│ │ ├── NullPointerException
│ │ ├── ArrayIndexOutOfBoundsException
│ │ └── ArithmeticException
│ └── (другие проверяемые исключения)
└── java.lang.Error (необработанные критичные ошибки)
├── OutOfMemoryError
├── StackOverflowError
├── VirtualMachineError
├── ThreadDeath
└── (другие критичные ошибки)
2. Error'ы, которые нельзя обработать
OutOfMemoryError (Нехватка памяти)
// Эта ошибка НИКОГДА не должна обрабатываться
// Потому что нет памяти даже для обработки исключения!
public class OutOfMemoryExample {
public static void main(String[] args) {
try {
// Пытаемся создать огромный массив
int[] huge = new int[Integer.MAX_VALUE];
} catch (OutOfMemoryError e) {
// ❌ НИКОГДА так не делай!
// Нет памяти для обработки, попытка вывода может завершить JVM
System.out.println("Out of memory!");
}
}
}
// Правильный подход — предотвращение
public class MemorySafeExample {
public static void main(String[] args) {
// 1. Используй -Xmx для ограничения памяти
// java -Xmx512m MyApp
// 2. Обрабатывай исключения, которые могут привести к OutOfMemory
try {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1_000_000_000; i++) {
list.add("item " + i);
// Может бросить OutOfMemoryError
}
} catch (Exception e) {
// Обрабатываем предосторожностью
logger.error("Error during processing", e);
}
// 3. Мониторь использование памяти
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
if (usedMemory > runtime.maxMemory() * 0.9) {
logger.warn("Memory usage is above 90%");
// Очисти кеши, освободи ресурсы
}
}
}
StackOverflowError (Переполнение стека)
// Бесконечная рекурсия вызовет StackOverflowError
public class StackOverflowExample {
// ❌ НЕПРАВИЛЬНО: бесконечная рекурсия
public void recursiveMethodBad() {
recursiveMethodBad(); // Вызывает сама себя
}
// ❌ Попытка обработать StackOverflowError бесполезна
public static void main(String[] args) {
try {
new StackOverflowExample().recursiveMethodBad();
} catch (StackOverflowError e) {
// Стек переполнен, эта ловушка может не сработать!
System.out.println("Stack overflow!");
}
}
}
// ✅ ПРАВИЛЬНО: рекурсия с базовым случаем
public class StackOverflowFixed {
public int factorial(int n) {
if (n <= 1) {
return 1; // Базовый случай — выход из рекурсии
}
return n * factorial(n - 1); // Рекурсивный вызов
}
// Альтернатива: итерация вместо рекурсии
public int factorialIterative(int n) {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
}
VirtualMachineError (Ошибки JVM)
// Включает InternalError, OutOfMemoryError, StackOverflowError
// Это критичные ошибки, которые означают, что JVM не работает правильно
public class VirtualMachineErrorExample {
public static void main(String[] args) {
try {
// Внутренняя ошибка JVM
// Может быть вызвана ошибкой в самой JVM
} catch (VirtualMachineError e) {
// ❌ Не обрабатывай — это означает, что JVM сломана
// Лучше перезагрузить приложение/сервер
System.exit(1);
}
}
}
ThreadDeath (Завершение потока)
// Deprecated, но все еще существует
public class ThreadDeathExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// ❌ НИКОГДА не используй stop() — он вызывает ThreadDeath
// thread.stop(); // Deprecated!
// ✅ ПРАВИЛЬНО: используй interrupt()
// thread.interrupt();
}
}
3. AssertionError (Ошибки утверждений)
// AssertionError выбрасывается при нарушении assert
public class AssertionErrorExample {
public static void main(String[] args) {
int age = -5;
// Ловушка для ошибок утверждений
try {
assert age >= 0 : "Age cannot be negative";
} catch (AssertionError e) {
// Технически можно поймать, но это плохая практика
System.out.println("Assertion failed: " + e.getMessage());
}
}
}
// ✅ ЛУЧШЕ: предотвращение
public class AssertionErrorFixed {
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
}
4. Какие ошибки МОЖНО обработать
// ✅ RuntimeException — можно обработать
public class RuntimeExceptionExample {
public static void main(String[] args) {
try {
int result = 10 / 0; // ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
}
try {
String str = null;
str.length(); // NullPointerException
} catch (NullPointerException e) {
System.out.println("Null pointer!");
}
try {
int[] arr = {1, 2, 3};
int value = arr[10]; // ArrayIndexOutOfBoundsException
} catch (IndexOutOfBoundsException e) {
System.out.println("Array index out of bounds");
}
}
}
// ✅ Checked Exception — ОБЯЗАТЕЛЬНО обработать
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
// IOException — checked exception, обязательно обработать
FileReader reader = new FileReader("file.txt");
int ch = reader.read();
reader.close();
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO error: " + e.getMessage());
}
}
}
5. UncaughtExceptionHandler (Глобальная обработка)
// Для ошибок, которые не были обработаны
public class GlobalExceptionHandler {
public static void main(String[] args) {
// Установить глобальный обработчик для потоков
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
logger.error("Uncaught exception in thread: " + thread.getName(), throwable);
// Если это ошибка, а не исключение
if (throwable instanceof Error) {
logger.error("CRITICAL ERROR - shutting down", throwable);
// Graceful shutdown
System.exit(1);
}
});
// Для отдельного потока
Thread thread = new Thread(() -> {
throw new RuntimeException("Error in thread");
});
thread.setUncaughtExceptionHandler((t, e) -> {
logger.error("Thread exception", e);
});
thread.start();
}
}
6. Spring Boot глобальная обработка
// Global Exception Handler в Spring
@RestControllerAdvice
public class GlobalExceptionHandler {
// Обработка проверяемых исключений
@ExceptionHandler(FileNotFoundException.class)
public ResponseEntity<ErrorResponse> handleFileNotFound(FileNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("FILE_NOT_FOUND", e.getMessage()));
}
// Обработка runtime исключений
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("INVALID_ARGUMENT", e.getMessage()));
}
// НЕ ОБРАБАТЫВАЙ Error'ы здесь!
// OutOfMemoryError, StackOverflowError не попадут сюда
// и обработаны не будут
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
logger.error("Unexpected error", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("INTERNAL_ERROR", "Internal server error"));
}
}
Чеклист: что можно/нельзя обработать
| Тип | Пример | Можно ловить? | Нужно ловить? |
|---|---|---|---|
| Checked Exception | IOException, SQLException | ✅ Да | ✅ Да (обязательно) |
| RuntimeException | NPE, ArithmeticException | ✅ Да | 🔶 Лучше предотвращать |
| OutOfMemoryError | ❌ Нет (или бесполезно) | ❌ Нет | ❌ Нет (перезагрузить) |
| StackOverflowError | ❌ Нет | ❌ Нет | ❌ Нет (исправить рекурсию) |
| VirtualMachineError | ❌ Нет | ❌ Нет | ❌ Нет (перезагрузить JVM) |
| ThreadDeath | ❌ Deprecated | ❌ Нет | ❌ Нет (использовать interrupt) |
Лучшие практики
- Не ловай Error'ы — это означает, что JVM сломана
- Предотвращай ошибки — проверяй null, размеры массивов
- Ловай только нужные исключения — не catch (Exception e)
- Логируй все ошибки — для отладки
- Gracefully shutdown при Error'ах — лучше перезагрузить
- Мониторь OutOfMemoryError — используй инструменты мониторинга
- Исправляй StackOverflowError — переписи рекурсию на итерацию
Итог: Error'ы (OutOfMemory, StackOverflow, VirtualMachineError) невозможно и нельзя обрабатывать. Их лучше предотвращать и gracefully перезагружать приложение.