throw и throws

Ключевые слова throw и throws в Java похожи, но выполняют разные функции:

  • ``throw`` — оператор, который явно выбрасывает объект-исключение из кода.

  • ``throws`` — модификатор сигнатуры метода, который объявляет, какие checked-исключения метод может пробросить наружу.

Различие checked / unchecked важно: компилятор требует обрабатывать или объявлять через throws только checked-исключения (наследники Exception, кроме RuntimeException). Unchecked (RuntimeException и его потомки) можно бросать без всяких объявлений.

Синтаксис

// throw — выбросить исключение
throw new IllegalArgumentException("pin должен быть >= 0");

// throws — объявить в сигнатуре
public void readConfig(String path) throws IOException {
    // ...
}

Пример 1. throw для проверки аргументов

public class ValidatePin {
    static void setPin(int pin) {
        if (pin < 0 || pin > 53) {
            throw new IllegalArgumentException("Недопустимый пин: " + pin);
        }
        System.out.println("Пин " + pin + " настроен");
    }

    public static void main(String[] args) {
        setPin(13);
        try {
            setPin(99);
        } catch (IllegalArgumentException e) {
            System.out.println("Поймано: " + e.getMessage());
        }
    }
}

Output:

Пин 13 настроен
Поймано: Недопустимый пин: 99

Пример 2. throws в сигнатуре метода

import java.io.IOException;

public class ThrowsExample {
    static String readSensorName() throws IOException {
        throw new IOException("Датчик не отвечает");
    }

    public static void main(String[] args) {
        try {
            String name = readSensorName();
            System.out.println(name);
        } catch (IOException e) {
            System.out.println("Ошибка I/O: " + e.getMessage());
        }
    }
}

Output:

Ошибка I/O: Датчик не отвечает

Пример 3. Несколько типов в throws

import java.io.IOException;

public class MultipleThrows {
    static void process(String input) throws IOException, NumberFormatException {
        if (input == null) {
            throw new IOException("Нет данных от Arduino");
        }
        int v = Integer.parseInt(input);
        System.out.println("Значение: " + v);
    }

    public static void main(String[] args) {
        try {
            process("42");
            process("abc");
        } catch (IOException e) {
            System.out.println("I/O: " + e.getMessage());
        } catch (NumberFormatException e) {
            System.out.println("Парсинг: " + e.getMessage());
        }
    }
}

Output:

Значение: 42
Парсинг: For input string: "abc"

Пример 4. Проброс исключения вызывающему

public class Rethrow {
    static int parsePositive(String s) {
        int v = Integer.parseInt(s);
        if (v <= 0) {
            throw new IllegalArgumentException("Ожидалось > 0, получили " + v);
        }
        return v;
    }

    public static void main(String[] args) {
        try {
            System.out.println(parsePositive("0"));
        } catch (IllegalArgumentException e) {
            System.out.println("Главный поймал: " + e.getMessage());
        }
    }
}

Output:

Главный поймал: Ожидалось > 0, получили 0

Пример 5. Цепочка исключений (causes)

public class ChainedException {
    static void loadConfig() {
        try {
            int v = Integer.parseInt("not-a-number");
        } catch (NumberFormatException e) {
            throw new IllegalStateException("Конфиг повреждён", e);
        }
    }

    public static void main(String[] args) {
        try {
            loadConfig();
        } catch (IllegalStateException e) {
            System.out.println("Главное: " + e.getMessage());
            System.out.println("Причина: " + e.getCause());
        }
    }
}

Output:

Главное: Конфиг повреждён
Причина: java.lang.NumberFormatException: For input string: "not-a-number"

Подводные камни

Предупреждение

  • Checked-исключения нужно либо ловить, либо объявлять — иначе ошибка компиляции.

  • Unchecked можно не объявлять в throws, но добавить можно для документации.

  • ``throw null`` выбросит NullPointerException, а не «ничего».

  • Переопределяемый метод не может объявлять более широкий ``throws``, чем у родителя.

  • Не оборачивайте исключения без сохранения cause — теряете оригинальный stack trace.

См. также

Примечание

Материал подготовлен на основе официальной документации Oracle Java Tutorials — Throwing Exceptions (Oracle Free Documentation License). Текст переведён и переработан, примеры кода написаны заново.