← Назад к вопросам
На что укажет компилятор при наличии проверяемых исключений?
2.0 Middle🔥 121 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проверяемые исключения (Checked Exceptions): Контроль компилятора
Отличный вопрос о Java исключениях и compile-time проверках! Позвольме разобраться в том, как компилятор контролирует проверяемые исключения.
Что такое Checked Exception?
Проверяемое исключение — это исключение, которое ОБЯЗАНО быть обработано или объявлено в сигнатуре метода.
// Иерархия исключений
Throwable
├── Exception (CHECKED)
│ ├── IOException (CHECKED)
│ ├── SQLException (CHECKED)
│ ├── ClassNotFoundException (CHECKED)
│ ├── ... (все остальные Exception)
│ └── RuntimeException (UNCHECKED)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ ├── IndexOutOfBoundsException
│ └── ... (остальные Runtime)
└── Error (обычно не ловим)
├── OutOfMemoryError
├── StackOverflowError
└── VirtualMachineError
Ошибки компилятора при Checked Exceptions
Ошибка 1: Не обработано checked исключение
public void readFile() {
// ❌ ОШИБКА КОМПИЛЯТОРА!
// error: unreported exception FileNotFoundException;
// must be caught or declared to be thrown
FileInputStream fis = new FileInputStream("file.txt");
}
// Исправление 1: Обработать try-catch
public void readFileFixed1() {
try {
FileInputStream fis = new FileInputStream("file.txt");
// ...
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
}
// Исправление 2: Объявить throws
public void readFileFixed2() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("file.txt");
}
Ошибка 2: Множественные checked исключения
public void complexOperation() throws IOException, SQLException, ClassNotFoundException {
// Объявляем ВСЕ checked исключения
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
FileWriter fw = new FileWriter("output.txt");
}
// ❌ НЕПРАВИЛЬНО: Забыли один
public void complexOperation() throws IOException, SQLException {
Class.forName("com.mysql.jdbc.Driver"); // ❌ ClassNotFoundException не объявлен!
}
// ✅ ПРАВИЛЬНО: Объявлены все или обработаны
public void complexOperation() throws IOException, SQLException, ClassNotFoundException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
FileWriter fw = new FileWriter("output.txt");
}
Практические примеры
Пример 1: Правильная обработка
public class FileProcessor {
// ✅ ВАРИАНТ 1: Обработка в методе
public String readFile(String filename) {
try {
FileInputStream fis = new FileInputStream(filename);
byte[] data = new byte[1024];
int bytesRead = fis.read(data);
fis.close();
return new String(data, 0, bytesRead);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filename);
return null;
} catch (IOException e) {
System.out.println("IO error: " + e.getMessage());
return null;
}
}
// ✅ ВАРИАНТ 2: Объявление throws
public String readFileWithThrows(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
byte[] data = new byte[1024];
int bytesRead = fis.read(data);
fis.close();
return new String(data, 0, bytesRead);
}
// ✅ ВАРИАНТ 3: Обработка с throws
public void processFile(String filename) throws IOException {
String content = readFileWithThrows(filename);
if (content == null) {
throw new IOException("Failed to read file");
}
}
}
// Использование
public class Main {
public static void main(String[] args) {
FileProcessor processor = new FileProcessor();
// Вариант 1: Обработана в методе
String content1 = processor.readFile("file.txt");
// Вариант 2 & 3: Нужно обработать
try {
String content2 = processor.readFileWithThrows("file.txt");
} catch (IOException e) {
System.out.println("Cannot read file");
}
}
}
Обработка с Try-With-Resources
Java 7+ — автоматическое закрытие ресурсов
// ❌ СТАРЫЙ СПОСОБ: Ручное управление
public String readFileOld(String filename) throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(filename);
byte[] data = new byte[1024];
fis.read(data);
return new String(data);
} finally {
if (fis != null) {
fis.close(); // IOException может быть выброшена!
}
}
}
// ✅ НОВЫЙ СПОСОБ: Try-with-resources
public String readFileNew(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] data = new byte[1024];
fis.read(data);
return new String(data);
}
// fis.close() вызовется автоматически
}
// ✅ МНОЖЕСТВЕННЫЕ РЕСУРСЫ
public void copyFile(String from, String to) throws IOException {
try (FileInputStream fis = new FileInputStream(from);
FileOutputStream fos = new FileOutputStream(to)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
Checked vs Unchecked Exceptions
// CHECKED Exception
public class CheckedExceptionExample {
public void connectToDatabase() throws SQLException {
// Компилятор ТРЕБУЕТ обработать или объявить
Connection conn = DriverManager.getConnection("jdbc:mysql://...");
}
}
// UNCHECKED Exception
public class UncheckedExceptionExample {
public void divideNumbers(int a, int b) {
// Компилятор НЕ требует обработку
int result = a / b; // ArithmeticException — unchecked
}
}
// Сравнение
| Характеристика | Checked | Unchecked |
|--------|---------|----------|
| **Наследует** | Exception | RuntimeException |
| **Проверка** | Compile-time | Runtime |
| **Обязательна обработка** | ДА | НЕТ |
| **Должен быть объявлен** | throws | Нет |
| **Примеры** | IOException, SQLException | NPE, IAE, IndexOOB |
| **Когда** | Предсказуемые ошибки | Программные ошибки |
Рекомендации Spring / Modern Java
Современный подход: Избегать checked exceptions
// ❌ СТАРЫЙ JAVA СПОСОБ: Много checked exceptions
public class UserRepository {
public User findById(Long id) throws SQLException {
// Много try-catch в коде вызова
try {
// database code
} catch (SQLException e) {
throw e;
}
}
}
// ✅ MODERN APPROACH: Обернуть в unchecked
@Repository
public class UserRepository {
public User findById(Long id) {
try {
// database code
} catch (SQLException e) {
throw new DataAccessException("Failed to find user", e);
}
}
}
// Spring автоматически переводит SQLException → DataAccessException
// Это unchecked exception, код более чистый
Spring Data JPA — больше нет checked exceptions
// Старый JDBC (checked exceptions)
public User findById(Long id) throws SQLException {
// Нужно обрабатывать или объявлять
}
// Spring Data JPA (unchecked exceptions)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Компилятор НЕ требует обработку
// Spring оборачивает SQLException в DataAccessException
}
Best Practices
// ✅ ПРАВИЛО 1: Обрабатывай в том месте, где можешь
public void processFile(String filename) {
try {
String content = readFile(filename);
// Process content
} catch (IOException e) {
logger.error("Cannot read file: " + filename, e);
// Handle error appropriately
}
}
// ✅ ПРАВИЛО 2: Не игнорируй исключения
// ❌ ПЛОХО
try {
readFile("file.txt");
} catch (IOException e) {
// Молча игнорируем!
}
// ✅ ХОРОШО
try {
readFile("file.txt");
} catch (IOException e) {
logger.warn("File not found, using default");
useDefaultFile();
}
// ✅ ПРАВИЛО 3: Оборачивай в нужном контексте
public User getUserData(String filename) throws UserDataException {
try {
return parseUserFile(filename);
} catch (IOException e) {
// Оборачиваем в domain-specific исключение
throw new UserDataException("Cannot load user data", e);
}
}
// ✅ ПРАВИЛО 4: Используй try-with-resources
public void readAndProcess(String filename) throws IOException {
try (BufferedReader reader = new BufferedReader(
new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
} // Автоматическое закрытие reader
}
// ✅ ПРАВИЛО 5: Никогда не catch (Exception e)
// ❌ ПЛОХО
try {
complexOperation();
} catch (Exception e) {
// Ловим всё, включая RuntimeException
}
// ✅ ХОРОШО
try {
complexOperation();
} catch (IOException | SQLException e) {
logger.error("Database or IO error", e);
}
Практический пример: Service Layer
// DAO слой (работает с JDBC/Spring Data)
@Repository
public class OrderDAO {
public Order findById(Long id) throws SQLException {
// Работаем с checked exception
try (Connection conn = dataSource.getConnection()) {
// SQL query
} catch (SQLException e) {
throw e; // Или оборачиваем
}
}
}
// Service слой (бизнес-логика)
@Service
public class OrderService {
private final OrderDAO dao;
public OrderDTO getOrder(Long id) {
try {
Order order = dao.findById(id);
return convertToDTO(order);
} catch (SQLException e) {
// Логируем и выбрасываем unchecked
logger.error("Database error", e);
throw new OrderServiceException("Cannot fetch order", e);
}
}
}
// Controller слой
@RestController
public class OrderController {
private final OrderService service;
@GetMapping("/orders/{id}")
public ResponseEntity<OrderDTO> getOrder(@PathVariable Long id) {
// Нет checked exception для обработки
try {
OrderDTO order = service.getOrder(id);
return ResponseEntity.ok(order);
} catch (OrderServiceException e) {
return ResponseEntity.notFound().build();
}
}
}
Выводы
- Checked Exception требуется обработать или объявить
throws - Компилятор контролирует на compile-time
- Используй try-catch для локальной обработки
- Используй throws для передачи ответственности вверх
- Современный подход: оборачивать checked в unchecked
- Используй try-with-resources для управления ресурсами
- Логируй ошибки перед перебросом исключения
- Never catch (Exception e) — ловись специфичные типы