В чем разница между Checked и Runtime Exception?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между Checked и Runtime Exception
Это одна из фундаментальных концепций Java. Различие состоит в том, как компилятор их обрабатывает и когда они возникают.
Иерархия Exception в Java
Throwable
├── Error (критичные ошибки, перезагружать JVM)
└── Exception
├── Checked Exception (наследники Exception кроме RuntimeException)
│ ├── IOException
│ ├── SQLException
│ ├── FileNotFoundException
│ └── CustomCheckedException
│
└── Unchecked Exception (RuntimeException и его наследники)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── IllegalArgumentException
└── ArithmeticException
Checked Exception (Проверяемые исключения)
Определение: Исключения, которые компилятор заставляет обрабатывать (catch или throws).
Наследуют от: Exception (но не от RuntimeException)
Когда возникают: В runtime, но должны быть обработаны в compile time
// ✅ Правильно — обработано или объявлено
public class FileReader {
// Способ 1: Catch
public String readFile(String path) {
try {
FileInputStream fis = new FileInputStream(path);
// ...
} catch (FileNotFoundException e) {
System.err.println("Файл не найден: " + e.getMessage());
return null;
}
}
// Способ 2: Throws
public String readFileThrows(String path) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(path);
// ...
return content;
}
}
// ❌ Ошибка компиляции — checked exception не обработано
public String readFile(String path) {
FileInputStream fis = new FileInputStream(path); // Ошибка компиляции!
return "content";
}
Примеры Checked Exception:
// IOException — checked exception
try {
FileInputStream fis = new FileInputStream("file.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// SQLException — checked exception (база данных)
try {
Connection conn = DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
// InterruptedException — checked exception (потоки)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Создание собственного Checked Exception:
// Наследуем от Exception
public class InvalidUserException extends Exception {
public InvalidUserException(String message) {
super(message);
}
public InvalidUserException(String message, Throwable cause) {
super(message, cause);
}
}
// Использование
public User getUser(Long id) throws InvalidUserException {
if (id == null || id <= 0) {
throw new InvalidUserException("User ID must be positive");
}
// ...
}
// Обработка
try {
User user = getUser(-1);
} catch (InvalidUserException e) {
System.err.println("Invalid user: " + e.getMessage());
}
Runtime Exception (Неоковиваемые исключения)
Определение: Исключения, которые компилятор НЕ заставляет обрабатывать.
Наследуют от: RuntimeException (который наследует от Exception)
Когда возникают: В runtime, но могут остаться необработанными
// ✅ Компилируется, даже без try-catch
public int divide(int a, int b) {
return a / b; // RuntimeException: ArithmeticException (если b == 0)
}
// Использование без обработки
int result = divide(10, 0); // Приложение упадёт если b == 0
// Но можно обработать, если хочешь
try {
int result = divide(10, 0);
} catch (ArithmeticException e) {
System.err.println("Деление на ноль: " + e.getMessage());
}
Примеры Runtime Exception:
// NullPointerException
String str = null;
int length = str.length(); // NPE!
// ArrayIndexOutOfBoundsException
int[] arr = new int[5];
int val = arr[10]; // ArrayIndexOutOfBoundsException!
// IllegalArgumentException
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
// ClassCastException
Object obj = "string";
Integer num = (Integer) obj; // ClassCastException!
Создание собственного Runtime Exception:
// Наследуем от RuntimeException
public class InvalidOperationException extends RuntimeException {
public InvalidOperationException(String message) {
super(message);
}
}
// Использование — можно не ловить
public void performAction(String action) {
if (action == null) {
throw new InvalidOperationException("Action cannot be null");
}
// ...
}
// Компилируется, даже без try-catch
performAction(null); // Вызовет InvalidOperationException
Сравнительная таблица
| Аспект | Checked Exception | Runtime Exception |
|---|---|---|
| Наследует от | Exception | RuntimeException |
| Обязательная обработка | Да (компилятор заставляет) | Нет (опционально) |
| Когда возникает | Runtime | Runtime |
| Примеры | IOException, SQLException | NullPointerException, IllegalArgumentException |
| Производительность | Немного медленнее | Быстрее |
| Использование | Предсказуемые ошибки | Непредсказуемые ошибки программы |
Когда использовать какой?
Используй Checked Exception когда:
- Ошибка вероятна и предсказуема
- Клиент кода может разумно восстановиться
- Это внешний ресурс (файлы, БД, сеть)
public class DatabaseService {
// Checked exception — клиент должен знать о возможной ошибке
public User getUserFromDB(Long id) throws SQLException {
// Может выбросить SQLException — нужно обработать
}
}
// Клиент кода
try {
User user = databaseService.getUserFromDB(1);
} catch (SQLException e) {
// Может переподключиться, логировать, уведомить пользователя
logger.error("Database error: " + e.getMessage());
}
Используй Runtime Exception когда:
- Ошибка указывает на проблему в коде (баг)
- Клиент кода не может разумно восстановиться
- Это программная ошибка (проверяющая инварианты)
public class UserService {
// Runtime exception — ошибка в коде клиента
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
}
// Клиент просто передаёт правильные данные
userService.setAge(25); // OK
userService.setAge(-5); // IllegalArgumentException — баг в клиенте
Практические примеры
Checked Exception — IO операции:
public class FileLogger {
public void log(String message) throws IOException {
try (FileWriter fw = new FileWriter("log.txt", true)) {
fw.write(message + "\n");
} // IOException — checked, должна быть обработана
}
}
// Использование
try {
logger.log("User logged in");
} catch (IOException e) {
System.err.println("Failed to log: " + e.getMessage());
}
Runtime Exception — валидация данных:
public class User {
private String email;
public void setEmail(String email) {
if (email == null || email.isEmpty()) {
throw new IllegalArgumentException("Email cannot be empty");
}
if (!email.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
this.email = email;
}
}
// Использование — обработка необязательна
User user = new User();
user.setEmail("invalid"); // IllegalArgumentException
Современный подход (Java 8+)
В современной Java часто избегают checked exceptions в пользу:
// Вместо checked exception — Optional
public Optional<User> findUser(Long id) {
// Нет throws, но может вернуть empty
return userRepository.findById(id);
}
// Использование
User user = userService.findUser(1).orElse(null);
Или использовать Result type:
public class Result<T> {
private final T value;
private final Exception error;
public static <T> Result<T> success(T value) {
return new Result<>(value, null);
}
public static <T> Result<T> failure(Exception error) {
return new Result<>(null, error);
}
}
public Result<User> getUserFromDB(Long id) {
try {
return Result.success(database.getUser(id));
} catch (SQLException e) {
return Result.failure(e);
}
}
Вывод
Checked Exception:
- Обязательная обработка
- Для предсказуемых ошибок (IO, БД, сеть)
- Заставляет клиента думать об ошибках
Runtime Exception:
- Опциональная обработка
- Для программных ошибок (баги)
- Более гибкие для использования
Модернизм Java идёт в сторону Runtime Exceptions и функциональных типов (Optional, Result), нежели классических Checked Exceptions.