Сравните роль throw и throws при работе Java-метода с исключениями.
throw немедленно создаёт ситуацию исключения во время выполнения: он выбрасывает конкретный объект Throwable. throws только объявляет в сигнатуре метода, какие исключения метод может передать вызывающему коду; сам по себе этот оператор ничего не выбрасывает.
Java использует явное описание проверяемых исключений, чтобы часть ошибок была видна на границах методов ещё на этапе компиляции. Для этого метод сообщает возможные проверяемые исключения через throws, а фактическое возникновение исключения выполняется оператором throw или вызываемым методом.
Такой подход отделяет описание контракта метода от конкретного места, где произошла ошибка. При этом непроверяемые исключения — наследники RuntimeException — могут возникать без обязательного указания в throws.
Если перепутать эти механизмы, можно ошибочно считать, что объявление throws обрабатывает исключение или что любой вызов throw требует объявления в сигнатуре. Это приводит либо к ошибкам компиляции, либо к ложному ощущению, что метод гарантирует обработку ошибки.
Особенно важно различать передачу исключения наружу и его обработку. throws не заменяет catch: он лишь оставляет ответственность за обработку вызывающему коду.
throw принимает один объект типа Throwable или его подкласса. При выполнении оператор немедленно прекращает обычное выполнение текущего метода и запускает поиск подходящего обработчика по стеку вызовов.
throws является частью объявления метода или конструктора. В нём перечисляются типы исключений, которые могут быть переданы наружу. Для проверяемых исключений компилятор требует, чтобы вызывающий код обработал их через catch либо тоже объявил в своём throws.
Например:
В validate throws Exception описывает контракт, а throw new Exception(...) фактически создаёт и выбрасывает исключение. Метод run не обрабатывает его, поэтому передаёт ответственность дальше через собственное throws.
Объявление может содержать throws RuntimeException или throws Error, но для непроверяемых исключений это не требуется компилятором. И наоборот, наличие throws Exception не означает, что метод обязательно выбросит исключение при каждом вызове: это только разрешённая часть его контракта.
Если исключение нужно обработать на текущем уровне, применяется try-catch. Если его нужно передать выше без обработки, используется throws; если требуется сформировать ошибку в конкретной ветке выполнения, используется throw.
Сервис валидирует входные данные и должен сообщить вызывающему коду о нарушении бизнес-правила. Возможны три варианта: вернуть специальное значение, выбросить непроверяемое исключение или выбросить проверяемое исключение и объявить его через throws.
Специальное значение плохо отделяет ошибку от обычного результата и заставляет каждый вызов вручную проверять договорённость. Непроверяемое исключение уменьшает шум в сигнатурах, но скрывает возможный сценарий ошибки от компилятора.
Для операции, где вызывающий код обязан явно принять решение, выбран проверяемый тип: метод использует throw в нарушающей условие ветке и указывает этот тип в throws. В результате граница ответственности видна в API, а вызывающие методы не могут случайно проигнорировать ошибку на этапе компиляции.
throw, не указывая тот же тип в throws?Да, если выбрасываемый тип является непроверяемым: наследуется от RuntimeException или Error. Для проверяемого исключения объявление необходимо, если оно не перехватывается внутри метода и не покрывается более общим допустимым типом в throws.
Например, throw new IllegalArgumentException() не требует throws, потому что IllegalArgumentException — наследник RuntimeException. Это не означает, что такое исключение безопасно или не должно быть документировано.
throws?Нет. throws обозначает возможность и контракт передачи исключения, а не обязательное действие при каждом вызове. Метод может завершиться успешно, выбросить исключение только в определённой ветке или вообще не выбросить его из текущей реализации.
Однако объявление влияет на вызывающий код: для проверяемого типа компилятор всё равно потребует catch или дальнейшее объявление через throws. Поэтому лишний тип в сигнатуре увеличивает связанность и усложняет использование метода.
throws тип, который метод не выбрасывает напрямую?Да. Метод может объявить более общий проверяемый тип, например Exception, даже если фактически он передаёт только IOException или сейчас вообще не выбрасывает исключения. Компилятор допускает это, но вызывающий код будет обязан учитывать весь объявленный тип.
Такое объявление иногда упрощает совместимость реализации, но ухудшает точность контракта. Обычно предпочтительнее указывать минимальный достаточно общий тип, чтобы не заставлять вызывающий код обрабатывать лишние варианты.