Исключения ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-30

throw и throws в Java: разница и примеры

Одна буква меняет смысл: throw выбрасывает исключение, а throws только предупреждает о нём. Уберите throws IllegalAccessException из заголовка метода, внутри которого стоит throw new IllegalAccessException("demo"), — и программа не скомпилируется: error: unreported exception IllegalAccessException; must be caught or declared to be thrown.

throw в Java — оператор, который выбрасывает конкретный объект исключения (экземпляр класса Throwable или его подкласса) и прерывает обычный ход выполнения: управление переходит к ближайшему подходящему блоку catch. throws — часть объявления метода или конструктора, где перечисляются типы исключений, которые он может передать вызывающему коду; для проверяемых (checked) исключений такое объявление обязательно.

Чем throw отличается от throws

throw — исполняемая инструкция в теле метода: она выбрасывает исключение прямо в момент выполнения. throws — декларация в сигнатуре метода: сама ничего не выбрасывает, а сообщает компилятору и вызывающему коду, какие исключения метод может пропустить наружу. Обычно они работают в паре: throw внутри метода, throws в его заголовке.

Критерий throw throws
Что это Оператор (инструкция) Часть объявления метода или конструктора
Где пишется В теле метода, в любом блоке В сигнатуре, после списка параметров
Что указывается Один объект: throw new IOException("...") Один или несколько классов через запятую: throws IOException, ParseException
Что делает Выбрасывает исключение и прерывает выполнение Ничего не выполняет: только объявляет, что исключение может выйти из метода
Когда обязателен Когда программа сама должна сообщить об ошибке Когда checked-исключение не перехвачено внутри метода

Оба ключевых слова в одном методе:

public void readConfig() throws IOException {     // throws: объявление
    throw new IOException("Файл config.txt не найден"); // throw: выброс
}

Оператор throw и throw new: как выбросить исключение

Исключение в Java выбрасывают инструкцией throw, после которой стоит объект типа Throwable. Чаще всего объект создаётся тут же оператором new: throw new IllegalArgumentException("сообщение"). После выброса оставшийся код блока не выполняется, а JVM ищет подходящий catch в текущем методе и дальше вверх по стеку вызовов.

Общая форма:

throw throwableInstance;

throwableInstance должен быть объектом класса Throwable или производного от него подкласса. Получить такой объект можно двумя способами: создать его оператором new или взять параметр блока catch (об этом — в разделе Повторный выброс исключения).

Пример: метод проверяет аргумент и сам сообщает об ошибке через throw new.

public class ThrowNewExample {
    static int divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Делитель не может быть равен нулю");
        }
        return a / b;
    }

    public static void main(String[] args) {
        System.out.println(divide(10, 2));
        System.out.println(divide(10, 0));
    }
}

Вывод:

5
Exception in thread "main" java.lang.IllegalArgumentException: Делитель не может быть равен нулю
    at ThrowNewExample.divide(ThrowNewExample.java:4)
    at ThrowNewExample.main(ThrowNewExample.java:11)

IllegalArgumentException — непроверяемое исключение, поэтому throws в объявлении divide() не нужен. Какой стандартный класс выбрать для throw new:

Исключение Когда выбрасывать Пример
IllegalArgumentException Метод получил недопустимое значение аргумента throw new IllegalArgumentException("Возраст: " + age)
IllegalStateException Аргументы в порядке, но объект в неподходящем состоянии throw new IllegalStateException("Соединение уже закрыто")
NullPointerException Передан null там, где он запрещён Objects.requireNonNull(name, "name") — выбрасывает его сам
UnsupportedOperationException Операция не поддерживается реализацией throw new UnsupportedOperationException("Только чтение")

Если ни один стандартный класс не описывает ошибку предметной области, создайте свой — см. урок Создание подклассов исключений.

Полезно знать

Стек вызовов записывается в объект исключения в момент его создания (конструктор Throwable вызывает fillInStackTrace()), а не в момент throw. Если создать исключение в одном методе, а выбросить в другом, трасса укажет на место создания. Поэтому исключение обычно создают прямо в инструкции throw new.

Повторный выброс исключения: throw e

Перехваченное исключение можно выбросить снова инструкцией throw e внутри блока catch. Так делают, когда метод должен отреагировать на ошибку (записать в лог, отменить часть работы), но решение о дальнейшей обработке оставить вызывающему коду. Выбрасывается тот же объект — с исходным сообщением и стеком вызовов.

public class ThrowExample {
    public static void doSomething() {
        try {
            throw new NullPointerException("demo");
        } catch (NullPointerException e) {
            System.out.println("Перехвачено внутри doSomething.");
            throw e; // повторно выбросить исключение
        }
    }

    public static void main(String[] args) {
        try {
            doSomething();
        } catch (NullPointerException e) {
            System.out.println("Повторный перехват: " + e);
        }
    }
}

Вывод:

Перехвачено внутри doSomething.
Повторный перехват: java.lang.NullPointerException: demo

Если нужно сменить тип исключения, создают новое и передают исходное как причину: throw new IllegalStateException("Не удалось загрузить заказ", e). Почему без второго аргумента теряется исходная ошибка — в разделе Типичные промахи новичков.

Точный повторный выброс (Java 7+)

Начиная с Java 7 компилятор анализирует, какие проверяемые исключения реально может выбросить блок try. Поэтому после catch (Exception e) { throw e; } метод может объявлять не throws Exception, а точный тип:

import java.io.IOException;

public class PreciseRethrow {
    static void read(boolean fail) throws IOException {
        try {
            if (fail) {
                throw new IOException("Нет доступа к файлу");
            }
            System.out.println("Файл прочитан");
        } catch (Exception e) {
            System.out.println("Лог: " + e.getMessage());
            throw e; // компилятор знает: здесь может быть только IOException
        }
    }

    public static void main(String[] args) {
        try {
            read(false);
            read(true);
        } catch (IOException e) {
            System.out.println("Обработано в main: " + e);
        }
    }
}

Вывод:

Файл прочитан
Лог: Нет доступа к файлу
Обработано в main: java.io.IOException: Нет доступа к файлу

Правило работает, только если параметр e не переприсваивается в catch (effectively final). Добавьте строку e = new Exception(); — и получите error: unreported exception Exception; must be caught or declared to be thrown.

Оператор throws в сигнатуре метода и checked-исключения

Ключевое слово throws пишется в объявлении метода после списка параметров и перечисляет исключения, которые метод не обрабатывает сам. Для проверяемых (checked) исключений оно обязательно: если метод может выбросить такое исключение и не перехватывает его, без throws возникнет ошибка компиляции. Для Error, RuntimeException и их подклассов throws не требуется.

Смысл правила: если метод способен вызвать исключение, которое сам не обрабатывает, вызывающий код должен об этом знать, чтобы обезопасить себя — перехватить исключение или передать его дальше. Какие классы относятся к проверяемым, разобрано в уроке Иерархия исключений: checked и unchecked.

Пример использования оператора throws:

public class ThrowsGenerateExample {
    public static void throwOne() throws IllegalAccessException {
        System.out.println("Внутри throwOne.");
        throw new IllegalAccessException("demo");
    }

    public static void main(String[] args) {
        try {
            throwOne();
        } catch (IllegalAccessException e) {
            System.out.println("Выброс " + e);
        }
    }
}

Вывод:

Внутри throwOne.
Выброс java.lang.IllegalAccessException: demo

IllegalAccessException наследует Exception напрямую, то есть это проверяемое исключение. Если убрать throws IllegalAccessException из сигнатуры throwOne(), компилятор остановится на строке с throw:

ThrowsGenerateExample.java:4: error: unreported exception IllegalAccessException; must be caught or declared to be thrown
        throw new IllegalAccessException("demo");
        ^

Такая же ошибка появится в main, если убрать try-catch вокруг вызова throwOne(): вызывающий метод обязан либо перехватить объявленное исключение, либо сам объявить его в throws — например, public static void main(String[] args) throws IllegalAccessException.

Непроверяемые исключения тоже можно указать в throws — это не ошибка, а документация для читателя кода; компилятор от вызывающего метода их обработки не потребует. Объявлять throws могут и конструкторы; что разрешено при переопределении методов и в конструкторах подкласса, рассказано в уроке Исключения при наследовании.

Распространённое заблуждение

throws не обрабатывает исключение, а только перекладывает ответственность на вызывающий код. Если исключение дойдёт до main, объявленного с throws Exception, программа всё равно завершится с выводом стека вызовов — так же, как без всякой обработки.

Несколько исключений в throws

В throws можно перечислить любое количество типов исключений через запятую, например void load() throws IOException, ParseException. Порядок перечисления не важен. Вызывающий код должен обработать каждое проверяемое исключение из списка: отдельными блоками catch, одним multi-catch или общим суперклассом.

import java.io.IOException;
import java.text.ParseException;

public class MultipleThrowsExample {
    static void load(int mode) throws IOException, ParseException {
        if (mode == 1) {
            throw new IOException("Файл недоступен");
        }
        if (mode == 2) {
            throw new ParseException("Неверная дата", 0);
        }
        System.out.println("Загружено");
    }

    public static void main(String[] args) {
        for (int mode = 0; mode <= 2; mode++) {
            try {
                load(mode);
            } catch (IOException | ParseException e) {
                System.out.println("Ошибка: " + e);
            }
        }
    }
}

Вывод:

Загружено
Ошибка: java.io.IOException: Файл недоступен
Ошибка: java.text.ParseException: Неверная дата

Нюансы списка throws:

  • throws Exception покрывает все проверяемые исключения сразу, но скрывает от вызывающего кода, что именно может пойти не так. Перечисляйте конкретные типы.
  • Подкласс и суперкласс вместе (throws FileNotFoundException, IOException) — допустимо, хотя избыточно. В multi-catch такое сочетание, наоборот, ошибка компиляции: см. Multi-catch: несколько исключений в одном catch.

Что можно и нельзя бросать через throw

После throw допускается только выражение типа Throwable или его подкласса. Примитивные типы вроде int или char, строки и любые другие классы, кроме Throwable и его наследников, использовать для выброса исключений нельзя — это ошибка компиляции. Если же ссылка подходящего типа равна null, код скомпилируется, но при выполнении будет выброшен NullPointerException.

Код Компилируется? Что произойдёт
throw new IllegalArgumentException("..."); Да Непроверяемое исключение, throws не нужен
throw new IOException("..."); Только с catch или throws IOException Иначе unreported exception IOException
throw e; (параметр catch) Да Повторный выброс того же объекта
throw "Ошибка"; Нет incompatible types: String cannot be converted to Throwable
throw 42; Нет incompatible types: int cannot be converted to Throwable
throw null; или ссылка, равная null Да При выполнении — NullPointerException вместо задуманного исключения

Что выведет этот код?

public class ThrowNullExample {
    public static void main(String[] args) {
        RuntimeException ex = null;
        try {
            throw ex;
        } catch (NullPointerException e) {
            System.out.println("Поймано: " + e);
        }
    }
}

Вывод в Java 15 и новее (подробные сообщения NPE включены по умолчанию):

Поймано: java.lang.NullPointerException: Cannot throw exception because "<local1>" is null

Если класс скомпилирован с ключом -g, вместо <local1> будет имя переменной ex. В Java 8 сообщение пустое: Поймано: java.lang.NullPointerException. Блок catch (RuntimeException e) здесь тоже сработал бы: NullPointerException — его подкласс.

Код после throw недостижим

Инструкция throw всегда завершает текущий блок, поэтому инструкция, стоящая сразу после неё в том же блоке, недостижима, и компилятор выдаёт ошибку unreachable statement. По той же причине метод с возвращаемым значением может заканчиваться на throw без return.

static void stop() {
    throw new IllegalStateException("Стоп");
    System.out.println("Никогда не выполнится"); // error: unreachable statement
}

Если throw стоит внутри if, код после if остаётся достижимым: компилятор не знает заранее, выполнится ли условие. А вот так завершать метод можно — ошибки missing return statement не будет:

static int parsePositive(String s) {
    int n = Integer.parseInt(s);
    if (n > 0) {
        return n;
    }
    throw new IllegalArgumentException("Ожидалось положительное число: " + s);
}

Вызов parsePositive("7") вернёт 7, parsePositive("-3") выбросит IllegalArgumentException: Ожидалось положительное число: -3, а parsePositive("abc") — NumberFormatException из Integer.parseInt().

Часто задаваемые вопросы

Можно ли использовать throw без throws?

Да, в двух случаях: если выбрасывается непроверяемое исключение (RuntimeException, Error и их подклассы) или если проверяемое исключение перехватывается блоком catch в том же методе. Если checked-исключение выходит за пределы метода, throws обязателен. Правило с примером — в разделе Оператор throws в сигнатуре метода.

Можно ли объявить в throws исключение, которое метод на самом деле не выбрасывает?

Да, для throws компилятор этого не проверяет: void run() throws IOException {} компилируется. Но тогда каждый вызывающий метод обязан обработать или объявить IOException. С catch наоборот: catch для проверяемого исключения, которое блок try выбросить не может, — ошибка компиляции.

Что означает throws Exception у метода main?

Метод main передаёт проверяемые исключения JVM, не обрабатывая их. Если такое исключение произойдёт, JVM напечатает стек вызовов и завершит поток main. В учебных примерах это удобно, в рабочем коде лучше обрабатывать исключения или объявлять конкретные типы.

Чем throws отличается от тега @throws в Javadoc?

throws — часть кода, его проверяет компилятор. @throws (синоним @exception) — тег документации в комментарии /** ... */: он описывает, в каких случаях метод выбрасывает исключение, и часто используется для непроверяемых исключений, например @throws IllegalArgumentException если amount отрицательный. Компилятор этот тег не проверяет.

Почему в лямбде внутри forEach нельзя выбросить IOException?

Метод accept функционального интерфейса Consumer не объявляет throws, поэтому проверяемое исключение не может выйти из тела лямбды — компилятор сообщит unreported exception IOException. Его перехватывают внутри лямбды и при необходимости оборачивают в UncheckedIOException или другое непроверяемое исключение.

Practice

Reinforce this lesson with hands-on tasks, each with a level and a time estimate.

All module tasks →

Comments

Please log in or register to have a possibility to add comment.