1. Введение
В этом уроке мы рассмотрим бросок
и броски
в Java. Мы объясним, когда мы должны использовать каждый из них.
Далее мы покажем несколько примеров их основного использования.
2. Броски
и броски
Начнем с краткого введения. Эти ключевые слова связаны с обработкой исключений. Исключения возникают, когда нормальный поток нашего приложения нарушается.
Причин может быть много. Пользователь может отправить неправильные входные данные. Мы можем потерять связь или может произойти другая непредвиденная ситуация. Хорошая обработка исключений является ключом к тому, чтобы наше приложение работало после появления этих неприятных моментов.
Мы используем ключевое слово throw
, чтобы явно вызвать исключение из кода. Это может быть любой метод или статический блок. Это исключение должно быть подклассом Throwable.
Кроме того, это может быть сам Throwable
. Мы не можем генерировать несколько исключений одним броском
.
Ключевое слово Throws
может быть помещено в объявление метода. Он обозначает, какие исключения могут быть выброшены из этого метода. Мы должны обрабатывать эти исключения с помощью try-catch.
Эти два ключевых слова не взаимозаменяемы!
3. Добавьте
Java
Давайте рассмотрим базовый пример с генерацией исключения из метода.
Прежде всего, представьте, что мы пишем простой калькулятор. Одним из основных арифметических действий является деление. В связи с этим нас попросили реализовать эту функцию:
public double divide(double a, double b) {
return a / b;
}
Поскольку мы не можем делить на ноль, нам нужно внести некоторые изменения в наш существующий код. Кажется, это хороший момент для создания исключения.
Давай сделаем это:
public double divide(double a, double b) {
if (b == 0) {
throw new ArithmeticException("Divider cannot be equal to zero!");
}
return a / b;
}
Как видите, мы использовали ArithmeticException
, который идеально подходит для наших нужд. Мы можем передать один параметр конструктора String
, который является сообщением об исключении.
3.1. Надлежащая практика
Мы всегда должны отдавать предпочтение наиболее конкретному исключению. Нам нужно найти класс, который лучше всего подходит для нашего исключительного мероприятия. Например, вызовите исключение NumberFormatException
вместо исключения IllegalArgumentException.
Мы должны избегать создания неспецифического Exception
.
Например, в пакете java.lang
есть класс Integer
. Давайте взглянем на одно из объявлений фабричного метода: ``
public static Integer valueOf(String s) throws NumberFormatException
Это статический фабричный метод, который создает экземпляр Integer из
String.
В случае неправильного ввода String
метод выдаст NumberFormatException.
Хорошая идея — определить наше собственное, более описательное исключение. В нашем классе калькулятора
это может быть, например , DivideByZeroException.
Давайте посмотрим на пример реализации:
public class DivideByZeroException extends RuntimeException {
public DivideByZeroException(String message) {
super(message);
}
}
3.2. Обертка существующего исключения
Иногда мы хотим обернуть существующее исключение в исключение, определенное нами.
Давайте начнем с определения нашего собственного исключения:
public class DataAcessException extends RuntimeException {
public DataAcessException(String message, Throwable cause) {
super(message, cause);
}
}
Конструктор принимает два параметра: сообщение об исключении и причину, которая может быть любым подклассом Throwable.
Напишем фальшивую реализацию функции findAll()
:
public List<String> findAll() throws SQLException {
throw new SQLException();
}
Теперь в SimpleService
вызовем функцию репозитория, что может привести к SQLException:
public void wrappingException() {
try {
personRepository.findAll();
} catch (SQLException e) {
throw new DataAccessException("SQL Exception", e);
}
}
Мы повторно выбрасываем SQLException
, завернутое в наше собственное исключение, называемое DataAccessException.
Все проверяется следующим тестом:
@Test
void whenSQLExceptionIsThrown_thenShouldBeRethrownWithWrappedException() {
assertThrows(DataAccessException.class,
() -> simpleService.wrappingException());
}
Для этого есть две причины. Во-первых, мы используем перенос исключений, потому что остальному коду не нужно знать о каждом возможном исключении в системе.
Также компонентам более высокого уровня не нужно знать ни о компонентах нижнего уровня, ни об исключениях, которые они выдают.
3.3. Multi-Catch с Java
Иногда используемые нами методы могут генерировать множество различных исключений.
Давайте взглянем на более обширный блок try-catch:
try {
tryCatch.execute();
} catch (ConnectionException | SocketException ex) {
System.out.println("IOException");
} catch (Exception ex) {
System.out.println("General exception");
}
Метод execute
может генерировать три исключения: SocketException, ConnectionException, Exception.
Первый блок catch будет перехватывать ConnectionException
или SocketException
. Второй блок catch будет перехватывать Exception
или любой другой подкласс Exception.
Помните, что мы всегда должны сначала поймать более подробное исключение.
Мы можем поменять порядок наших блоков catch. Тогда мы никогда не поймаем SocketException
и ConnectionException
, потому что все пойдет на перехват с Exception
.
4. Броски
в Java
Мы добавляем броски
в объявление метода.
Давайте взглянем на одно из наших предыдущих объявлений метода:
public static void execute() throws SocketException, ConnectionException, Exception
Метод может генерировать несколько исключений. Они разделяются запятыми в конце объявления метода. Мы можем помещать в броски
как проверенные, так и непроверенные исключения . Ниже мы описали разницу между ними.
4.1. Проверенные и непроверенные исключения
Проверенное исключение означает, что оно проверено во время компиляции. Обратите внимание, что мы должны обработать это исключение. В противном случае метод должен указать исключение с помощью ключевого слова throws
.
Наиболее распространенными проверенными исключениями являются IOException, FileNotFoundException, ParseException. FileNotFoundException
может быть сгенерировано, когда мы создаем FileInputStream
из File.
Вот короткий пример:
File file = new File("not_existing_file.txt");
try {
FileInputStream stream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Мы можем избежать использования блока try-catch, добавив броски
в объявление метода:
private static void uncheckedException() throws FileNotFoundException {
File file = new File("not_existing_file.txt");
FileInputStream stream = new FileInputStream(file);
}
К сожалению, функция более высокого уровня по-прежнему должна обрабатывать это исключение. В противном случае мы должны поместить это исключение в объявление метода с ключевым словом throws.
Напротив, непроверенные исключения не проверяются во время компиляции.
Наиболее распространенные непроверенные исключения: ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException.
Непроверенные исключения генерируются во время выполнения. Следующий код вызовет исключение NullPointerException.
Вероятно, это одно из самых распространенных исключений в Java.
Вызов метода для нулевой ссылки приведет к этому исключению:
public void runtimeNullPointerException() {
String a = null;
a.length();
}
Давайте проверим это поведение в тесте:
@Test
void whenCalled_thenNullPointerExceptionIsThrown() {
assertThrows(NullPointerException.class,
() -> simpleService.runtimeNullPointerException());
}
Пожалуйста, помните, что этот код и тест не имеют большого смысла. Объяснение исключений времени выполнения предназначено только для учебных целей.
В Java каждый подкласс Error
и RuntimeException
является непроверяемым исключением. Проверенным исключением является все остальное в классе Throwable
.
5. Вывод
В этой статье мы обсудили разницу между двумя ключевыми словами Java: throw
и throws.
Мы рассмотрели основы использования и немного поговорили о передовой практике .
Затем мы поговорили о проверенных и непроверенных исключениях.
Как всегда, исходный код можно найти на нашем GitHub .
Если вы хотите глубже изучить обработку исключений в Java, ознакомьтесь с нашей статьей об исключениях Java .