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

Для чего нужна сигнатура Runtime в исключениях?

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

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

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

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

# Runtime исключения и их сигнатура

Понятие

RuntimeException (или unchecked exception) — это исключения, которые не требуют явной обработки или объявления в сигнатуре метода. Они возникают во время выполнения программы (runtime) и могут быть не перехвачены.

В отличие от checked exceptions, которые проверяются на этапе компиляции и должны быть либо перехвачены, либо объявлены в сигнатуре метода через throws.

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

Throwable
├── Error (критические ошибки VM)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── ...
├── Exception
│   ├── Checked Exception (IOException, SQLException, ...)
│   │   └── Должны быть объявлены в throws
│   └── RuntimeException (unchecked)
│       ├── NullPointerException
│       ├── ArrayIndexOutOfBoundsException
│       ├── ClassCastException
│       ├── ArithmeticException
│       └── ...

Сигнатура Runtime исключений

1. RuntimeException НЕ требует объявления в throws

// ✅ Правильно - RuntimeException НЕ нужен throws
public String getName() {
    // Может выбросить NullPointerException, но throws не нужен
    return user.getName().toUpperCase();
}

// Вызов
String name = getName(); // Компилируется без ошибок

2. Checked Exception ТРЕБУЕТ throws

// ❌ Ошибка компиляции - IOException checked exception
public String readFile(String path) {
    FileReader reader = new FileReader(path);  // IOException!
    return null;
}

// ✅ Правильно - IOException объявлен в throws
public String readFile(String path) throws IOException {
    FileReader reader = new FileReader(path);
    return null;
}

// Вызов требует обработки
try {
    String content = readFile("file.txt");
} catch (IOException e) {
    // Обработка
}

Встроенные RuntimeException

1. NullPointerException

public class NullPointerExceptionExample {
    public void processUser(User user) {
        // RuntimeException - не нужен throws
        String name = user.getName(); // NPE если user == null
    }
    
    // Правильный подход - валидация
    public void processUserSafe(User user) {
        if (user == null) {
            throw new IllegalArgumentException("User cannot be null");
        }
        String name = user.getName();
    }
}

2. ArrayIndexOutOfBoundsException

public void accessArray() {
    int[] numbers = {1, 2, 3};
    int value = numbers[10];  // RuntimeException - не нужен throws
}

// Правильный подход
public void accessArraySafe(int[] numbers, int index) {
    if (index < 0 || index >= numbers.length) {
        throw new IllegalArgumentException("Index out of bounds");
    }
    int value = numbers[index];
}

3. ClassCastException

public void castObject(Object obj) {
    // RuntimeException
    String str = (String) obj;  // Может выбросить ClassCastException
}

// Правильный подход
public void castObjectSafe(Object obj) {
    if (!(obj instanceof String)) {
        throw new IllegalArgumentException("Object must be String");
    }
    String str = (String) obj;
}

4. ArithmeticException

public int divide(int a, int b) {
    return a / b;  // RuntimeException если b == 0
}

// Правильный подход
public int divideSafe(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("Divisor cannot be zero");
    }
    return a / b;
}

5. IllegalArgumentException

public class User {
    private int age;
    
    public User(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Age must be between 0 and 150");
        }
        this.age = age;
    }
}

Сигнатура Runtime vs Checked исключений

Сравнение

// RuntimeException - не нужен throws
public User findUserById(Long id) {
    if (id == null) {
        throw new IllegalArgumentException("ID cannot be null");
    }
    return userRepository.findById(id);
}

// Checked Exception - нужен throws
public String readConfigFromFile(String path) throws IOException {
    FileReader reader = new FileReader(path);
    // ...
    return config;
}

// Вызов Runtime
User user = findUserById(null); // Может выбросить, но компилятор не требует обработки

// Вызов Checked
try {
    String config = readConfigFromFile("config.xml");
} catch (IOException e) {
    // Обязателен try-catch
}

Создание собственных RuntimeException

Пример 1: Бизнес-логика

public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(Long userId) {
        super("User with id " + userId + " not found");
    }
}

public class UserService {
    public User getUser(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
        // Не нужен throws!
    }
}

// Вызов
User user = userService.getUser(999); // Может выбросить UserNotFoundException

Пример 2: Валидация данных

public class Order {
    private List<Item> items;
    
    public Order(List<Item> items) {
        if (items == null || items.isEmpty()) {
            throw new IllegalArgumentException("Order must have at least one item");
        }
        this.items = items;
    }
    
    public void addItem(Item item) {
        if (item == null) {
            throw new IllegalArgumentException("Item cannot be null");
        }
        this.items.add(item);
    }
}

Пример 3: Состояние объекта

public class Database {
    private boolean connected = false;
    
    public void execute(String query) {
        if (!connected) {
            throw new IllegalStateException("Database is not connected");
        }
        // Выполнение запроса
    }
    
    public void connect() {
        connected = true;
    }
}

RuntimeException vs Checked Exception

Когда использовать RuntimeException

// ✅ Использовать RuntimeException:

// 1. Ошибки программиста
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative");
    }
}

// 2. Логические ошибки
public void withdraw(double amount) {
    if (amount > balance) {
        throw new IllegalStateException("Insufficient funds");
    }
}

// 3. Ошибки конфигурации
String dbUrl = System.getenv("DB_URL");
if (dbUrl == null) {
    throw new IllegalStateException("DB_URL environment variable not set");
}

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

// ✅ Использовать Checked Exception:

// 1. Операции с файлами
public void readFile(String path) throws IOException {
    // IOException - требует обработки
}

// 2. Сетевые операции
public void fetchData(String url) throws IOException {
    // Сетевая ошибка - требует обработки
}

// 3. Парсинг и конвертация
public Date parseDate(String dateStr) throws ParseException {
    // Ошибка формата - требует обработки
}

Обработка RuntimeException

Правильный подход: предотвращение вместо обработки

// ❌ Неправильно - ловля RuntimeException
public String processName(User user) {
    try {
        return user.getName().toUpperCase();
    } catch (NullPointerException e) {
        return "UNKNOWN";
    }
}

// ✅ Правильно - валидация
public String processName(User user) {
    if (user == null || user.getName() == null) {
        return "UNKNOWN";
    }
    return user.getName().toUpperCase();
}

Когда можно ловить RuntimeException

// Обработка в главном обработчике
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException e) {
        // Логирование
        logger.error("Runtime error", e);
        
        // Возврат ошибки клиенту
        return ResponseEntity.status(500)
            .body(new ErrorResponse("Internal server error"));
    }
}

Практический пример: REST контроллер

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
        // getUserById() выбросит UserNotFoundException (RuntimeException)
        // Не нужно объявлять throws!
        User user = userService.getUser(id);
        return ResponseEntity.ok(userMapper.toDTO(user));
    }
    
    @PostMapping
    public ResponseEntity<UserDTO> createUser(@RequestBody UserCreateRequest request) {
        // Валидация через @Valid будет выбросить ValidationException (RuntimeException)
        if (request.getAge() < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
        User user = userService.createUser(request);
        return ResponseEntity.status(201).body(userMapper.toDTO(user));
    }
}

@ControllerAdvice
public class ExceptionHandler {
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
        return ResponseEntity.status(404)
            .body(new ErrorResponse(e.getMessage()));
    }
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
        return ResponseEntity.status(400)
            .body(new ErrorResponse(e.getMessage()));
    }
    
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponse> handleRuntime(RuntimeException e) {
        return ResponseEntity.status(500)
            .body(new ErrorResponse("Internal server error"));
    }
}

Резюме

АспектRuntimeExceptionChecked Exception
ПроверкаНа этапе выполненияНа этапе компиляции
Объявление в throwsНЕ требуетсяТРЕБУЕТСЯ
ПерехватОпционаленОбязателен
Когда использоватьОшибки программиста, логические ошибкиI/O, сеть, парсинг
ПримерыNullPointerException, IllegalArgumentExceptionIOException, SQLException

Разница в сигнатуре существенна:

  • RuntimeException дают программисту гибкость, но требуют внимания к обработке ошибок
  • Checked Exception принуждают явно обрабатывать ошибки, но загромождают код