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

В чем разница между RuntimeException и Exception?

1.0 Junior🔥 241 комментариев
#ООП#Основы Java

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

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

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

# RuntimeException vs Exception: ключевые различия

Это одно из самых важных различий в Java. Выбор между ними влияет на дизайн всего приложения.

Иерархия

Throwable
├── Exception
│   ├── RuntimeException
│   │   ├── NullPointerException
│   │   ├── ArithmeticException
│   │   ├── IndexOutOfBoundsException
│   │   └── ...
│   └── Checked Exception
│       ├── IOException
│       ├── SQLException
│       ├── ClassNotFoundException
│       └── ...
└── Error
    ├── OutOfMemoryError
    ├── StackOverflowError
    └── ...

Ключевые различия

АспектRuntimeExceptionException (Checked)
Проверкаcompile-timeCompile-time
ОбработкаОпциональнаОБЯЗАТЕЛЬНА
Когда выброситьProgramming errorExpected error
ПримерыNPE, IndexOOBIOException, SQLException
Catch блокОпциональноОБЯЗАТЕЛЬНО

RuntimeException (Unchecked)

Назначение

Oшибка программиста, которую нельзя предусмотреть
Направо ошибок, которые можно избежать кодом

Примеры

// NullPointerException (самая частая)
String str = null;
str.toUpperCase();  // RuntimeException: NullPointerException

// ArithmeticException
int result = 10 / 0;  // RuntimeException: ArithmeticException

// ArrayIndexOutOfBoundsException
int[] arr = new int[5];
int val = arr[10];  // RuntimeException: ArrayIndexOutOfBoundsException

// ClassCastException
Object obj = "String";
Integer num = (Integer) obj;  // RuntimeException: ClassCastException

Код БЕЗ try-catch (работает)

public int divide(int a, int b) {
    return a / b;  // Может выбросить ArithmeticException
    // НО компилятор НЕ требует try-catch
}

public void doSomething() {
    divide(10, 0);  // Даже без try-catch
}

Как правильно: защита кодом

public int divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("b cannot be zero");
    }
    return a / b;
}

// ИЛИ проверка перед вызовом
public void doSomething() {
    int result;
    if (divisor != 0) {
        result = divide(10, divisor);
    }
}

Exception (Checked)

Назначение

Ожидаемая ошибка, которую можно предусмотреть
Например: файл не существует, network недоступна, БД down

Примеры

// IOException
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // Файла может не быть → IOException
}

// SQLException
public void queryDatabase() throws SQLException {
    Connection conn = DriverManager.getConnection(url);
    // БД может быть недоступна → SQLException
}

// ClassNotFoundException
public void loadClass() throws ClassNotFoundException {
    Class.forName("com.example.NonExistent");
    // Класса может не быть → ClassNotFoundException
}

Код ТРЕБУЕТ try-catch (не скомпилируется без него)

// ❌ ОШИБКА КОМПИЛЯЦИИ
public void readFile(String path) {
    FileInputStream fis = new FileInputStream(path);
    // Unhandled exception: FileNotFoundException
}

// ✅ ПРАВИЛЬНО: throws или try-catch
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
}

// ИЛИ
public void readFile(String path) {
    try {
        FileInputStream fis = new FileInputStream(path);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Сравнение в коде

Пример 1: Разделение нулл

// RuntimeException — защищаются кодом
public String getName(User user) {
    if (user == null) {  // Проверка перед использованием
        throw new IllegalArgumentException("User cannot be null");
    }
    return user.getName();
}

// Exception — обработка в try-catch
public String readConfig(String filename) throws IOException {
    try {
        return Files.readString(Paths.get(filename));
    } catch (IOException e) {
        logger.error("Failed to read config", e);
        throw e;  // Пробрасываем дальше
    }
}

Пример 2: Выбор между ними

// ОШИБКА: используется Checked Exception, где нужна RuntimeException
public int parseNumber(String str) throws NumberFormatException {
    // NumberFormatException — это RuntimeException
    // throws не нужен, но это плохой стиль
    return Integer.parseInt(str);
}

// ПРАВИЛЬНО: без throws
public int parseNumber(String str) {
    try {
        return Integer.parseInt(str);
    } catch (NumberFormatException e) {
        throw new IllegalArgumentException("Invalid number", e);
    }
}

// ИЛИ просто отправляем вверх (RuntimeException)
public int parseNumber(String str) {
    return Integer.parseInt(str);  // Может выбросить NPE
}

Когда выбросить что

Выбрось RuntimeException, если

✅ Это ошибка программиста (logic error)
✅ Можно избежать checks в коде
✅ Это неожиданно и обработать нельзя
✅ Пример: NullPointerException, IndexOutOfBoundsException

Выбрось Checked Exception (extends Exception), если

✅ Это ожидаемая ошибка
✅ Вызывающий должен её обработать
✅ Её нельзя избежать checks
✅ Пример: IOException, SQLException

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

Сценарий 1: Пользовательский ввод

// ❌ НЕПРАВИЛЬНО: RuntimeException для ожидаемой ошибки
public void processAge(String ageStr) {
    if (Integer.parseInt(ageStr) < 0) {
        throw new RuntimeException("Age cannot be negative");
    }
}

// ✅ ПРАВИЛЬНО: Checked Exception
public void processAge(String ageStr) throws InvalidAgeException {
    int age = Integer.parseInt(ageStr);
    if (age < 0) {
        throw new InvalidAgeException("Age cannot be negative");
    }
}

// Вызывающий ДОЛЖЕН обработать
try {
    processAge(userInput);
} catch (InvalidAgeException e) {
    System.out.println("Invalid input: " + e.getMessage());
}

Сценарий 2: Работа с файлами

// IOException — Checked Exception (обязательно обработать)
public void copyFile(String source, String dest) throws IOException {
    Files.copy(Paths.get(source), Paths.get(dest));
    // IOException — ожидаемая ошибка (файла может не быть)
}

// Обработка
try {
    copyFile("input.txt", "output.txt");
} catch (IOException e) {
    logger.error("Failed to copy file", e);
}

Сценарий 3: Бизнес-логика

// ❌ ПЛОХО: Checked Exception для внутренней ошибки
public void updateUser(User user) throws InternalException {
    if (user.getId() == null) {
        throw new InternalException("User ID is null");
    }
}

// ✅ ХОРОШО: RuntimeException для internal bugs
public void updateUser(User user) {
    if (user.getId() == null) {
        throw new IllegalStateException("User ID is null");
    }
}

Кастомные исключения

RuntimeException

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

// Использование — БЕЗ throws
public String getConfig(String key) {
    if (config.get(key) == null) {
        throw new InvalidConfigException("Missing config: " + key);
    }
    return config.get(key);
}

Checked Exception

public class DatabaseException extends Exception {
    public DatabaseException(String message) {
        super(message);
    }
    
    public DatabaseException(String message, Throwable cause) {
        super(message, cause);
    }
}

// Использование — С throws
public User getUser(Long id) throws DatabaseException {
    try {
        return repository.findById(id);
    } catch (SQLException e) {
        throw new DatabaseException("Failed to get user: " + id, e);
    }
}

Java 8+ и Checked Exception

Функциональное программирование вынуждает использовать Unchecked

// ❌ Functional interface не может выбросить Checked Exception
list.forEach(item -> readFile(item));  // Ошибка, если readFile throws IOException

// ✅ Обёртываем в RuntimeException
list.forEach(item -> {
    try {
        readFile(item);
    } catch (IOException e) {
        throw new UncheckedIOException(e);  // RuntimeException
    }
});

// ИЛИ используем утилиту
list.forEach(unchecked(this::readFile));

Мой совет

Java 8-

✅ Использовать Checked Exception для внешних ошибок (IO, DB)
✅ Использовать RuntimeException для программиста ошибок
✅ Это контракт между классом и вызывающим

Java 8+

✅ Все ещё используй Checked Exception для expected errors
❌ НО функциональное программирование делает это сложным
✅ Альтернатива: использовать Optional<T> или Result<T>

Итог

RuntimeException (Unchecked):
- Ошибка программиста
- Не требует обработки (compile не требует)
- Примеры: NPE, ArithmeticException, IndexOutOfBoundsException

Exception (Checked):
- Ожидаемая ошибка
- ТРЕБУЕТ try-catch или throws
- Примеры: IOException, SQLException

Правило: если вызывающий ДОЛЖЕН обработать → Checked Exception
         если это bug → RuntimeException
В чем разница между RuntimeException и Exception? | PrepBro