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

Как обрабатывать ошибки в Java?

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

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

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

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

Обработка ошибок в Java

Обработка ошибок - один из самых критичных аспектов разработки надежного программного обеспечения. Java предоставляет мощный механизм через исключения.

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

Throwable
├── Error (не обрабатываем обычно)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError
└── Exception
    ├── Checked Exception (обязательно обрабатывать)
    │   ├── IOException
    │   ├── SQLException
    │   └── FileNotFoundException
    └── Unchecked Exception (необязательно обрабатывать)
        ├── RuntimeException
        │   ├── NullPointerException
        │   ├── IllegalArgumentException
        │   └── IndexOutOfBoundsException
        └── ...

1. Try-Catch блоки

Базовый способ обработки исключений:

try {
    // Код, который может выбросить исключение
    int result = 10 / 0;
} catch (ArithmeticException e) {
    // Обработка исключения
    System.err.println("Ошибка деления: " + e.getMessage());
} finally {
    // Код, который выполнится в любом случае
    System.out.println("Блок finally всегда выполняется");
}

2. Множественные catch блоки

try {
    processFile("data.txt");
} catch (FileNotFoundException e) {
    System.err.println("Файл не найден: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Ошибка ввода-вывода: " + e.getMessage());
} catch (Exception e) {
    System.err.println("Неожиданная ошибка: " + e.getMessage());
    e.printStackTrace();
}

3. Multi-catch (Java 7+)

Обработка нескольких типов исключений одним блоком:

try {
    // Код
    readData();
} catch (IOException | SQLException e) {
    // Обработка нескольких типов
    logger.error("Database or I/O error", e);
} catch (Exception e) {
    logger.error("Unexpected error", e);
}

4. Try-with-resources (Java 7+)

Автоматическое закрытие ресурсов:

// ❌ Плохо - нужно вручную закрывать
FileReader reader = new FileReader("file.txt");
try {
    int data = reader.read();
} finally {
    reader.close(); // Может выбросить исключение!
}

// ✅ Хорошо - автоматическое управление
try (FileReader reader = new FileReader("file.txt")) {
    int data = reader.read();
    System.out.println(data);
} catch (IOException e) {
    System.err.println("IO Error: " + e.getMessage());
}

5. Выброс исключений

public void validateAge(int age) throws IllegalArgumentException {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("Invalid age: " + age);
    }
}

// С кастомным исключением
public class InsufficientFundsException extends Exception {
    private double requiredAmount;
    
    public InsufficientFundsException(String message, double requiredAmount) {
        super(message);
        this.requiredAmount = requiredAmount;
    }
    
    public double getRequiredAmount() {
        return requiredAmount;
    }
}

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException(
            "Insufficient funds", 
            amount - balance
        );
    }
    balance -= amount;
}

6. Цепочка исключений (Exception Chaining)

Сохраняем оригинальное исключение для отладки:

try {
    connectToDatabase();
} catch (SQLException e) {
    // Оборачиваем в более высокоуровневое исключение
    throw new DataAccessException(
        "Failed to load user data", 
        e  // Сохраняем cause
    );
}

// Или используем initCause
catch (SQLException e) {
    DataAccessException ex = new DataAccessException("DB error");
    ex.initCause(e);
    throw ex;
}

7. Custom исключения

// Checked Exception - для ожидаемых ошибок
public class PaymentFailedException extends Exception {
    private String paymentId;
    
    public PaymentFailedException(String message, String paymentId) {
        super(message);
        this.paymentId = paymentId;
    }
    
    public String getPaymentId() {
        return paymentId;
    }
}

// Unchecked Exception - для логических ошибок
public class InvalidOrderException extends RuntimeException {
    public InvalidOrderException(String message) {
        super(message);
    }
}

8. Логирование ошибок

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    
    public void createUser(User user) {
        try {
            validateUser(user);
            saveUser(user);
            logger.info("User created: {}", user.getId());
        } catch (IllegalArgumentException e) {
            logger.warn("Invalid user data: {}", e.getMessage());
            throw e;
        } catch (Exception e) {
            logger.error("Failed to create user", e);
            throw new RuntimeException("User creation failed", e);
        }
    }
}

9. Лучшие практики

// ❌ Плохо - игнорирование исключения
try {
    riskyOperation();
} catch (Exception e) {
    // Молчаливая ошибка - опасно!
}

// ❌ Плохо - слишком общий catch
try {
    // Много кода
} catch (Exception e) {
    e.printStackTrace();
}

// ✅ Хорошо - специфичные исключения
try {
    fileOperation();
} catch (FileNotFoundException e) {
    logger.error("File not found: {}", fileName);
    return defaultValue();
} catch (IOException e) {
    logger.error("IO error: {}", e.getMessage(), e);
    throw new DataAccessException("Failed to read file", e);
}

// ✅ Хорошо - проверка перед операцией
if (user != null) {
    user.getName(); // Безопаснее, чем обработка NullPointerException
}

// ✅ Хорошо - валидация на входе
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative");
    }
    this.age = age;
}

10. Обработка ошибок в потоках

// Неправильно - исключение в потоке теряется
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(() -> {
    throw new RuntimeException("Error in thread");
});

// Правильно - использу Future
Future<?> future = executor.submit(() -> {
    throw new RuntimeException("Error in thread");
});

try {
    future.get(); // Выбросит исключение
} catch (ExecutionException e) {
    logger.error("Task failed", e.getCause());
}

// Или используй UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
    logger.error("Uncaught exception in thread", throwable);
});

Вывод: обработка ошибок требует баланса между специфичностью и практичностью. Используйте checked исключения для ожидаемых ошибок, логируйте все проблемы, не скрывайте исключения и сохраняйте информацию о причинах ошибок для отладки.