С чего начинаются исключения
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Иерархия исключений в Java: начало со Throwable
Все исключения в Java начинаются с класса Throwable — это корень иерархии всех ошибок и исключений. Это фундаментальное знание, которое критично для написания правильного обработчика ошибок.
Иерархия Throwable
Throwable (java.lang.Throwable)
├── Error (java.lang.Error)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ ├── LinkageError
│ ├── VirtualMachineError
│ └── ...
│
└── Exception (java.lang.Exception)
├── Checked Exceptions (должны быть обработаны)
│ ├── IOException
│ ├── SQLException
│ ├── InterruptedException
│ └── ...
│
└── RuntimeException (Unchecked Exceptions)
├── NullPointerException
├── IllegalArgumentException
├── IndexOutOfBoundsException
├── ArithmeticException
└── ...
Что такое Throwable
// Throwable — базовый класс для всего что можно "выбросить"
public class Throwable implements Serializable {
// Сообщение об ошибке
private String message;
// Причина (что вызвало эту ошибку)
private Throwable cause;
// Stack trace для отладки
private StackTraceElement[] stackTrace;
// Подавленные исключения (try-with-resources)
private List<Throwable> suppressedExceptions;
// Основные методы
public String getMessage() { return message; }
public Throwable getCause() { return cause; }
public StackTraceElement[] getStackTrace() { return stackTrace; }
public void printStackTrace() { /* печать в консоль */ }
public String toString() { /* Throwable класс + сообщение */ }
}
Error vs Exception
Error — это проблемы с JVM, которые обычно нельзя обработать:
public class JavaVirtualMachineErrors {
// OutOfMemoryError — когда кончилась память
public void memoryError() {
List<byte[]> hugeList = new ArrayList<>();
while (true) {
hugeList.add(new byte[1024 * 1024]); // 1MB
// java.lang.OutOfMemoryError: Java heap space
}
}
// StackOverflowError — рекурсия без базового случая
public void recursionError(int n) {
System.out.println(n);
recursionError(n + 1); // Бесконечная рекурсия
// java.lang.StackOverflowError
}
// LinkageError — проблемы при загрузке классов
public void linkageError() {
// Возникает когда класс не может быть загружен
// обычно это проблема конфигурации, не кода
}
}
// Не ловим Error! Это означает проблему с JVM
// ❌ try {
// recursionError(0);
// } catch (Error e) { // ПЛОХО
// e.printStackTrace();
// }
Exception — ошибки приложения, которые можно обработать:
public class ExceptionHandling {
// Checked Exception — ДОЛЖНА быть обработана
public void readFile(String filename) throws IOException {
// Либо бросаем дальше (throws), либо обрабатываем (try-catch)
FileReader reader = new FileReader(filename);
reader.close();
}
// Unchecked Exception (RuntimeException)
public void runtimeError(String[] args) {
String value = args[0]; // Может быть ArrayIndexOutOfBoundsException
Integer number = Integer.parseInt(value); // Может быть NumberFormatException
}
}
Checked vs Unchecked Exceptions
public class ExceptionTypes {
// CHECKED (должны быть обработаны компилятором)
public void checkedExceptionExample() {
try {
Thread.sleep(1000); // throws InterruptedException
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
InputStream is = new FileInputStream("file.txt");
// throws IOException
} catch (IOException e) {
System.err.println("File not found: " + e.getMessage());
}
}
// UNCHECKED (не требуют обработки, но хорошо бы обработать)
public void uncheckedExceptionExample() {
String value = null;
// Может быть NullPointerException
int length = value.length();
// Может быть ClassCastException
Object obj = new Integer(123);
String str = (String) obj;
// Может быть IllegalArgumentException
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
}
}
Правильная обработка исключений
@Service
public class BankTransferService {
@Autowired
private AccountRepository accountRepository;
@Autowired
private AuditLogService auditLog;
public void transferMoney(Long fromId, Long toId,
BigDecimal amount) throws InsufficientFundsException {
// Обработка Checked Exception
try {
// Операция которая может выбросить IOException
validateTransferWithExternalService(fromId, toId, amount);
} catch (IOException e) {
// IOException — это ошибка сети, нужно обработать
auditLog.error("External service unavailable", e);
throw new ServiceUnavailableException(
"Cannot validate transfer", e);
}
// Обработка Unchecked Exception
try {
Account from = accountRepository.findById(fromId)
.orElseThrow(() -> new AccountNotFoundException(
"From account not found"));
Account to = accountRepository.findById(toId)
.orElseThrow(() -> new AccountNotFoundException(
"To account not found"));
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException(
"Amount must be positive");
}
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException(
from.getId(), amount);
}
// Выполняем передачу
from.withdraw(amount);
to.deposit(amount);
accountRepository.saveAll(List.of(from, to));
} catch (DataAccessException e) {
// Ошибка БД — логируем и переводим в более ясную форму
auditLog.error("Database error during transfer", e);
throw new TransferFailedException(
"Failed to complete transfer", e);
}
}
private void validateTransferWithExternalService(
Long fromId, Long toId, BigDecimal amount)
throws IOException {
// Может выбросить IOException (Checked)
}
}
Custom Exceptions
// Проверяемое исключение (Checked)
public class InsufficientFundsException extends Exception {
private Long accountId;
private BigDecimal requiredAmount;
public InsufficientFundsException(Long accountId,
BigDecimal requiredAmount) {
super(String.format(
"Account %d has insufficient funds. Required: %s",
accountId, requiredAmount));
this.accountId = accountId;
this.requiredAmount = requiredAmount;
}
public Long getAccountId() { return accountId; }
public BigDecimal getRequiredAmount() { return requiredAmount; }
}
// Непроверяемое исключение (Unchecked)
public class AccountNotFoundException extends RuntimeException {
private Long accountId;
public AccountNotFoundException(String message) {
super(message);
}
public AccountNotFoundException(String message, Throwable cause) {
super(message, cause);
// Всегда сохраняй причину для лучшей отладки
}
}
// Специальное исключение для сервиса
public class TransferFailedException extends RuntimeException {
public TransferFailedException(String message, Throwable cause) {
super(message, cause);
}
}
Stack Trace и Debugging
public class ExceptionAnalysis {
public static void analyzeException(Throwable e) {
// Основная информация
System.out.println("Exception: " + e.getClass().getSimpleName());
System.out.println("Message: " + e.getMessage());
// Stack trace
StackTraceElement[] stackTrace = e.getStackTrace();
for (StackTraceElement element : stackTrace) {
System.out.println(" at " + element);
// Filename.java:123
}
// Цепочка исключений (cause)
Throwable cause = e.getCause();
if (cause != null) {
System.out.println("Caused by: " + cause.getClass().getSimpleName());
System.out.println(cause.getMessage());
}
}
public static void main(String[] args) {
try {
throw new NullPointerException("Value is null");
} catch (Exception e) {
analyzeException(e);
}
}
}
// Вывод:
// Exception: NullPointerException
// Message: Value is null
// at ExceptionAnalysis.main(ExceptionAnalysis.java:45)
Лучшие практики
-
Никогда не лови Exception или Throwable просто так
// ❌ ПЛОХО try { riskyOperation(); } catch (Exception e) { // Ловишь всё // Может скрыть серьёзные ошибки } // ✅ ХОРОШО try { riskyOperation(); } catch (SpecificException e) { // Ловишь конкретное handleSpecificError(e); } -
Всегда сохраняй cause (причину)
try { databaseOperation(); } catch (SQLException e) { throw new DataAccessException("Failed to access DB", e); // Передаём e } -
Логируй полную информацию об ошибке
logger.error("Operation failed", exception); // Включает stack trace -
Не выбрасывай generic Exception
// ❌ ПЛОХО throw new Exception("Something went wrong"); // ✅ ХОРОШО throw new InvalidOperationException("Cannot process negative amount");
Иерархия в Spring
Spring вводит свои иерархии исключений:
DataAccessException (Unchecked)
├── InvalidDataAccessResourceUsageException
├── PermissionDeniedDataAccessException
├── DataRetrievalFailureException
└── ...
HttpClientErrorException (Unchecked)
├── HttpClientErrorException.BadRequest (400)
├── HttpClientErrorException.Unauthorized (401)
├── HttpClientErrorException.NotFound (404)
└── ...
Ключевой момент: Все исключения в Java происходят от Throwable, но различаются тем, как и когда их нужно обрабатывать. Error — это обычно конец программы, Exception — это то, что приложение может попытаться исправить.