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

Каким типом являются элементы в Exception: проверяемыми или непроверяемым?

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

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

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

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

Исключения в Java: проверяемые vs непроверяемые

Исключения (Exceptions) в Java делятся на два типа: проверяемые (checked) и непроверяемые (unchecked). Это фундаментальное понимание необходимо для написания надёжного Java кода.

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

Throwable
├── Exception
│   ├── Checked Exception (должны обрабатываться или объявляться)
│   │   ├── IOException
│   │   ├── SQLException
│   │   ├── ClassNotFoundException
│   │   └── ... (наследуют Exception, но не RuntimeException)
│   │
│   └── Unchecked Exception (RuntimeException и наследники)
│       ├── RuntimeException
│       │   ├── NullPointerException
│       │   ├── ArrayIndexOutOfBoundsException
│       │   ├── IllegalArgumentException
│       │   ├── IllegalStateException
│       │   └── ...
│
└── Error (не должны ловиться, критичные)
    ├── OutOfMemoryError
    ├── StackOverflowError
    └── ...

1. Проверяемые исключения (Checked Exceptions)

Проверяемые исключения — это исключения, которые должны быть либо:

  1. Обработаны (try-catch)
  2. Объявлены в сигнатуре метода (throws)
// IOException — проверяемое исключение
public class FileReader {
    
    // Способ 1: Обработать исключение
    public String readFile(String filePath) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            // Чтение файла
            return content;
        } catch (IOException e) {
            // Обработка ошибки
            System.err.println("Cannot read file: " + e.getMessage());
            return null;
        }
    }
    
    // Способ 2: Объявить в throws
    public String readFileThrows(String filePath) throws IOException {
        FileInputStream fis = new FileInputStream(filePath);
        // Чтение файла
        return content;
        // IOException распространяется вверх по call stack'у
    }
    
    // Способ 3: Обрабатываем и объявляем (редко)
    public String readFileHybrid(String filePath) throws IOException {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            return content;
        } catch (FileNotFoundException e) {
            throw new IOException("File not found: " + filePath, e);
        }
    }
}

Примеры проверяемых исключений:

  • IOException — ошибки ввода-вывода
  • SQLException — ошибки БД
  • ClassNotFoundException — класс не найден
  • InterruptedException — поток прерван
  • FileNotFoundException — файл не найден
  • ParseException — ошибка парсинга

Проверка компилятором:

public void methodWithoutException() {
    FileInputStream fis = new FileInputStream("file.txt");  // ОШИБКА!
    // Compiler error: Unhandled exception: java.io.FileNotFoundException
    // Нужно либо try-catch, либо throws
}

2. Непроверяемые исключения (Unchecked Exceptions)

Непроверяемые исключения — это RuntimeException и его наследники. Компилятор не требует их обработки.

public class UncheckedExceptions {
    
    // Компилятор НЕ требует обработки
    public void divideByZero() {
        int result = 10 / 0;  // ArithmeticException (unchecked)
        // Компилятор не ругается
    }
    
    // NullPointerException
    public void nullPointerExample() {
        String str = null;
        int length = str.length();  // NullPointerException
        // Компилятор не требует обработки
    }
    
    // Но можно обрабатывать
    public void handleUnchecked() {
        try {
            Object obj = null;
            obj.toString();
        } catch (NullPointerException e) {
            System.err.println("Null pointer caught: " + e.getMessage());
        }
    }
}

Примеры непроверяемых исключений:

  • NullPointerException — null reference
  • ArrayIndexOutOfBoundsException — индекс за границами
  • ClassCastException — неправильный cast
  • IllegalArgumentException — неверный аргумент
  • NumberFormatException — ошибка формата числа
  • ArithmeticException — деление на ноль

Сравнительная таблица

АспектChecked ExceptionUnchecked Exception
Родительский классException (но не RuntimeException)RuntimeException
Требование обработки✓ Да, обязательно✗ Нет, опционально
Проверка компилятором✓ Да✗ Нет
ПримерыIOException, SQLExceptionNullPointerException, IllegalArgumentException
Когда возникаетВ runtime, предсказуемоВ runtime, часто неожиданно
Что делатьОбработать или объявитьПредотвратить логикой

Выбор между checked и unchecked

Когда использовать Checked Exception:

// Ошибки ввода-вывода
public byte[] readFileContent(String path) throws IOException {
    return Files.readAllBytes(Paths.get(path));
}

// Ошибки БД
public User getUserFromDatabase(Long id) throws SQLException {
    // ...
}

// Network ошибки
public String fetchFromApi(String url) throws IOException {
    // ...
}

Правило: Используй checked exception, когда вызывающий код может обоснованно восстановиться от ошибки.

Когда использовать Unchecked Exception:

// Ошибки валидации (код ошибка вызывающего)
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative");
    }
    this.age = age;
}

// Программные ошибки (нужна мена логики)
public String getString(List<String> list, int index) {
    if (index < 0 || index >= list.size()) {
        throw new IndexOutOfBoundsException("Invalid index: " + index);
    }
    return list.get(index);
}

// Null checks
public void processUser(User user) {
    if (user == null) {
        throw new IllegalArgumentException("User cannot be null");
    }
    // ...
}

Правило: Используй unchecked exception для программных ошибок, которые вызываются некорректным кодом вызывающего.

Практические примеры

1. Правильная обработка checked exception

public class DataImporter {
    
    public void importDataFromFile(String filePath) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(fis)
            );
            
            String line;
            while ((line = reader.readLine()) != null) {
                processLine(line);
            }
            
            reader.close();
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + filePath);
            // Можем создать файл или уведомить пользователя
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            // Retry логика
        }
    }
    
    private void processLine(String line) {
        // Логика обработки
    }
}

2. Правильная валидация с unchecked exception

public class User {
    private String email;
    private int age;
    
    public User(String email, int age) {
        if (email == null || email.isEmpty()) {
            throw new IllegalArgumentException("Email cannot be empty");
        }
        if (!email.contains("@")) {
            throw new IllegalArgumentException("Email format invalid");
        }
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Age must be between 0 and 150");
        }
        
        this.email = email;
        this.age = age;
    }
}

// Использование
public static void main(String[] args) {
    try {
        User user = new User("john@example.com", 30);
    } catch (IllegalArgumentException e) {
        System.err.println("Invalid user data: " + e.getMessage());
    }
}

3. Обёртывание checked exception

public class DataService {
    
    // Оборачиваем checked exception в unchecked
    public List<User> getAllUsers() {
        try {
            return userRepository.fetchFromDatabase();
        } catch (SQLException e) {
            // SQLException — checked, но мы не можем восстановиться
            throw new DataAccessException("Failed to fetch users", e);
        }
    }
}

public class DataAccessException extends RuntimeException {
    public DataAccessException(String message, Throwable cause) {
        super(message, cause);
    }
}

Best Practices

1. Не игнорируй исключения

// ПЛОХО: Invisible exception
try {
    riskyOperation();
} catch (IOException e) {
    // Пустой catch — это ошибка!
}

// ХОРОШО: Логирование или пробрас
try {
    riskyOperation();
} catch (IOException e) {
    log.error("Operation failed", e);
    throw new RuntimeException("Failed to complete operation", e);
}

2. Ловай конкретные исключения

// ПЛОХО: Слишком общее
try {
    fileOperation();
} catch (Exception e) {
    // Можешь случайно поймать Throwable
}

// ХОРОШО: Конкретное исключение
try {
    fileOperation();
} catch (IOException e) {
    // Только expected исключения
}

3. Используй try-with-resources

// ПЛОХО: Риск утечки ресурсов
FileInputStream fis = new FileInputStream("file.txt");
try {
    // ...
} finally {
    fis.close();  // Может не выполниться
}

// ХОРОШО: Автоматическое закрытие
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // fis.close() выполнится автоматически
}

Выводы

Проверяемые исключения (Checked):

  • ✓ Обязательно обрабатывать
  • ✓ Явно объявлять в throws
  • ✓ Для ошибок, которые можно восстановить
  • ✓ IOException, SQLException, ClassNotFoundException

Непроверяемые исключения (Unchecked):

  • ✓ Опционально обрабатывать
  • ✓ Не нужно объявлять в throws
  • ✓ Для программных ошибок
  • ✓ NullPointerException, IllegalArgumentException, ArithmeticException

Правило большого пальца:

  • Используй checked exceptions для внешних ошибок (файлы, сеть, БД)
  • Используй unchecked exceptions для ошибок логики кода
  • Всегда логируй исключения с полным контекстом
  • Предпочитай specific exceptions в catch блоках