To define a checked exception you create a subclass (or hierarchy of subclasses) of java.lang.Exception
. For example:
public class FooException extends Exception {
public FooException() { super(); }
public FooException(String message) { super(message); }
public FooException(String message, Throwable cause) { super(message, cause); }
public FooException(Throwable cause) { super(cause); }
}
Methods that can potentially throw or propagate this exception must declare it:
public void calculate(int i) throws FooException, IOException;
… and code calling this method must either handle or propagate this exception (or both):
try {
int i = 5;
myObject.calculate(5);
} catch(FooException ex) {
// Print error and terminate application.
ex.printStackTrace();
System.exit(1);
} catch(IOException ex) {
// Rethrow as FooException.
throw new FooException(ex);
}
You’ll notice in the above example that IOException
is caught and rethrown as FooException
. This is a common technique used to encapsulate exceptions (typically when implementing an API).
Sometimes there will be situations where you don’t want to force every method to declare your exception implementation in its throws clause. In this case you can create an unchecked exception. An unchecked exception is any exception that extends java.lang.RuntimeException
(which itself is a subclass of java.lang.Exception
):
public class FooRuntimeException extends RuntimeException {
...
}
Methods can throw or propagate FooRuntimeException
exception without declaring it; e.g.
public void calculate(int i) {
if (i < 0) {
throw new FooRuntimeException("i < 0: " + i);
}
}
Unchecked exceptions are typically used to denote a programmer error, for example passing an invalid argument to a method or attempting to breach an array index bounds.
The java.lang.Throwable
class is the root of all errors and exceptions that can be thrown within Java. java.lang.Exception
and java.lang.Error
are both subclasses of Throwable
. Anything that subclasses Throwable
may be thrown or caught. However, it is typically bad practice to catch or throw Error
as this is used to denote errors internal to the JVM that cannot usually be «handled» by the programmer (e.g. OutOfMemoryError
). Likewise you should avoid catching Throwable
, which could result in you catching Error
s in addition to Exception
s.
Создание своих классов исключений
Последнее обновление: 23.04.2018
Хотя имеющиеся в стандартной библиотеке классов Java классы исключений описывают большинство исключительных ситуаций, которые могут возникнуть
при выполнении программы, все таки иногда требуется создать свои собственные классы исключений со своей логикой.
Чтобы создать свой класс исключений, надо унаследовать его от класса Exception. Например, у нас есть класс, вычисляющий факториал, и нам надо
выбрасывать специальное исключение, если число, передаваемое в метод, меньше 1:
class Factorial{ public static int getFactorial(int num) throws FactorialException{ int result=1; if(num<1) throw new FactorialException("The number is less than 1", num); for(int i=1; i<=num;i++){ result*=i; } return result; } } class FactorialException extends Exception{ private int number; public int getNumber(){return number;} public FactorialException(String message, int num){ super(message); number=num; } }
Здесь для определения ошибки, связанной с вычислением факториала, определен класс FactorialException
, который наследуется от Exception
и который содержит всю информацию о вычислении. В конструкторе FactorialException в конструктор базового класса Exception передается сообщение об ошибке:
super(message)
. Кроме того, отдельное поле предназначено для хранения числа, факториал которого вычисляется.
Для генерации исключения в методе вычисления факториала выбрасывается исключение с помощью оператора throw: throw new FactorialException("Число не может быть меньше 1", num)
.
Кроме того, так как это исключение не обрабатывается с помощью try..catch, то мы передаем обработку вызывающему методу, используя оператор throws:
public static int getFactorial(int num) throws FactorialException
Теперь используем класс в методе main:
public static void main(String[] args){ try{ int result = Factorial.getFactorial(6); System.out.println(result); } catch(FactorialException ex){ System.out.println(ex.getMessage()); System.out.println(ex.getNumber()); } }
Автор оригинала: Dhananjay Singh.
Обзор
В этой статье мы рассмотрим процесс создания пользовательских как проверенных, так и непроверенных исключений в Java.
Если вы хотите узнать больше об исключениях и обработке исключений в Java, мы подробно рассмотрели это в разделе Обработка исключений в Java: Полное руководство с лучшими и наихудшими практиками
Зачем Использовать Пользовательские Исключения?
Хотя исключения Java, как они есть, охватывают почти все исключительные случаи и условия, ваше приложение может создать специальное пользовательское исключение, уникальное для вашего кода и логики.
Иногда нам необходимо создать свой собственный для представления исключений бизнес-логики, т. Е. исключений, специфичных для нашей бизнес-логики или рабочего процесса. Например Исключение электронной почты NotUniqueException
, Недопустимое исключение состояния пользователя
и т.д.
Они помогают клиентам приложений лучше понять, что пошло не так. Они особенно полезны для обработки исключений для API REST , поскольку различные ограничения бизнес-логики требуют, чтобы клиенту отправлялись разные коды ответов.
Если определение пользовательского исключения не дает преимуществ по сравнению с использованием обычного исключения в Java, нет необходимости определять пользовательские исключения, и вам следует просто придерживаться тех, которые уже доступны – нет необходимости снова изобретать горячую воду.
Исключение С Пользовательской Проверкой
Давайте рассмотрим сценарий, в котором мы хотим проверить электронное письмо, переданное в качестве аргумента методу.
Мы хотим проверить, действительно ли это или нет. Теперь мы могли бы использовать встроенный в Java Исключение IllegalArgumentException
, что нормально, если мы просто проверяем одну вещь, например, соответствует ли она предопределенному регулярному выражению или нет.
Но предположим, что у нас также есть бизнес-условие, чтобы проверить, что все электронные письма в нашей системе должны быть уникальными. Теперь мы должны выполнить вторую проверку (вызов БД/сети). Мы можем, конечно, использовать то же самое Исключение IllegalArgumentException
, но будет неясно, какова точная причина – не прошла ли проверка регулярного выражения по электронной почте или электронное письмо уже существует в базе данных.
Давайте создадим пользовательское исключение, чтобы справиться с этой ситуацией. Чтобы создать исключение, как и любое другое исключение, мы должны расширить java.lang.Исключение
класс:
public class EmailNotUniqueException extends Exception { public EmailNotUniqueException(String message) { super(message); } }
Обратите внимание, что мы предоставили конструктор, который принимает Строку
сообщение об ошибке и вызывает конструктор родительского класса. Теперь это не обязательно, но это обычная практика, чтобы иметь какие-то подробности о произошедшем исключении.
Вызывая super(сообщение)
, мы инициализируем сообщение об ошибке исключения, и базовый класс позаботится о настройке пользовательского сообщения в соответствии с сообщением
.
Теперь давайте используем это пользовательское исключение в нашем коде. Поскольку мы определяем метод, который может вызывать исключение на уровне сервиса, мы пометим его ключевым словом throws
.
Если входное электронное письмо уже существует в нашей базе данных (в данном случае список), мы создаем
наше пользовательское исключение:
public class RegistrationService { List registeredEmails = Arrays.asList("[email protected]", "[email protected]"); public void validateEmail(String email) throws EmailNotUniqueException { if (registeredEmails.contains(email)) { throw new EmailNotUniqueException("Email Already Registered"); } } }
Теперь давайте напишем клиента для нашего сервиса. Поскольку это проверенное исключение, мы должны придерживаться правила обработки или объявления. В следующем примере мы решили “разобраться” с этим:
public class RegistrationServiceClient { public static void main(String[] args) { RegistrationService service = new RegistrationService(); try { service.validateEmail("[email protected]"); } catch (EmailNotUniqueException e) { // logging and handling the situation } } }
Запуск этого фрагмента кода приведет к:
mynotes.custom.checked.exception.EmailNotUniqueException: Email Already Registered at mynotes.custom.checked.exception.RegistrationService.validateEmail(RegistrationService.java:12) at mynotes.custom.checked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:9)
Примечание: Процесс обработки исключений опущен для краткости, но, тем не менее, является важным процессом.
Пользовательское Непроверенное Исключение
Это прекрасно работает, но наш код стал немного запутанным. Кроме того, мы заставляем клиента фиксировать наше исключение в блоке try-catch
. В некоторых случаях это может привести к тому, что разработчики будут вынуждены писать шаблонный код.
В этом случае вместо этого может быть полезно создать пользовательское исключение во время выполнения. Чтобы создать пользовательское непроверенное исключение, мы должны расширить класс java.lang.RuntimeException
.
Давайте рассмотрим ситуацию, когда мы должны проверить, имеет ли электронное письмо действительное доменное имя или нет:
public class DomainNotValidException extends RuntimeException { public DomainNotValidException(String message) { super(message); } }
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
Теперь позвольте использовать его в нашем сервисе:
public class RegistrationService { public void validateEmail(String email) { if (!isDomainValid(email)) { throw new DomainNotValidException("Invalid domain"); } } private boolean isDomainValid(String email) { List validDomains = Arrays.asList("gmail.com", "yahoo.com", "outlook.com"); if (validDomains.contains(email.substring(email.indexOf("@") + 1))) { return true; } return false; } }
Обратите внимание, что нам не нужно было использовать ключевые слова throws
в подписи метода, поскольку это непроверенное исключение.
Теперь давайте напишем клиента для нашего сервиса. На этот раз нам не нужно использовать try-catch
блок:
public class RegistrationServiceClient { public static void main(String[] args) { RegistrationService service = new RegistrationService(); service.validateEmail("[email protected]"); } }
Запуск этого фрагмента кода приведет к:
Exception in thread "main" mynotes.custom.unchecked.exception.DomainNotValidException: Invalid domain at mynotes.custom.unchecked.exception.RegistrationService.validateEmail(RegistrationService.java:10) at mynotes.custom.unchecked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:7)
Примечание: Конечно, вы можете окружить свой код блоком try-catch
, чтобы зафиксировать возникающее исключение, но теперь компилятор не заставляет его выполнять.
Создание исключения, Заключенного в пользовательское исключение
Иногда нам нужно поймать исключение и пересмотреть его, добавив еще несколько деталей. Это обычно происходит, если в вашем приложении определены различные коды ошибок, которые необходимо либо зарегистрировать, либо вернуть клиенту в случае этого конкретного исключения.
Предположим, что ваше приложение имеет стандартный Коды ошибок
класс:
public enum ErrorCodes { VALIDATION_PARSE_ERROR(422); private int code; ErrorCodes(int code) { this.code = code; } public int getCode() { return code; } }
Давайте создадим наше пользовательское исключение:
public class InvalidCurrencyDataException extends RuntimeException { private Integer errorCode; public InvalidCurrencyDataException(String message) { super(message); } public InvalidCurrencyDataException(String message, Throwable cause) { super(message, cause); } public InvalidCurrencyDataException(String message, Throwable cause, ErrorCodes errorCode) { super(message, cause); this.errorCode = errorCode.getCode(); } public Integer getErrorCode() { return errorCode; } }
Обратите внимание, что у нас есть несколько конструкторов, и пусть класс обслуживания решает, какой из них использовать. Поскольку мы пересматриваем исключение, всегда рекомендуется фиксировать первопричину исключения, следовательно, аргумент Throwable
, который может быть передан конструктору родительского класса.
Мы также фиксируем код ошибки в одном из конструкторов и устанавливаем код ошибки
в самом исключении. Код ошибки
может быть использован клиентом для ведения журнала или в любых других целях. Это помогает в более централизованном стандарте обработки исключений.
Давайте напишем наш класс обслуживания:
public class CurrencyService { public String convertDollarsToEuros(String value) { try { int x = Integer.parseInt(value); } catch (NumberFormatException e) { throw new InvalidCurrencyDataException("Invalid data", e, ErrorCodes.VALIDATION_PARSE_ERROR); } return value; } }
Поэтому мы поймали стандартное Исключение NumberFormatException
и выбросили наше собственное Исключение Недопустимых Валютных данных
. Мы передали родительское исключение нашему пользовательскому исключению, чтобы не потерять первопричину, из-за которой они произошли.
Давайте напишем тестовый клиент для этой службы:
public class CurrencyClient { public static void main(String[] args) { CurrencyService service = new CurrencyService(); service.convertDollarsToEuros("asd"); } }
Выход:
Exception in thread "main" mynotes.custom.unchecked.exception.InvalidCurrencyDataException: Invalid data at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:10) at mynotes.custom.unchecked.exception.CurrencyClient.main(CurrencyClient.java:8) Caused by: java.lang.NumberFormatException: For input string: "asd" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:8) ... 1 more
Как вы можете видеть, у нас есть хорошая трассировка стека исключения, которая может быть полезна для целей отладки.
Рекомендации по использованию пользовательских исключений
- Придерживайтесь общего соглашения об именовании во всей экосистеме Java – Все имена пользовательских классов исключений должны заканчиваться на “Исключение”
- Избегайте создания пользовательских исключений, если стандартные исключения из самого JDK могут служить этой цели. В большинстве случаев нет необходимости определять пользовательские исключения.
- Предпочитайте исключения во время выполнения проверенным исключениям. Фреймворки, такие как Spring , заключили все проверенные исключения в исключения времени выполнения, поэтому не заставляют клиента писать стандартный код, который им не нужен или не нужен.
- Предоставьте множество перегруженных конструкторов, основанных на том, как будет создаваться пользовательское исключение. Если он используется для повторного создания существующего исключения, то обязательно предоставьте конструктор, который устанавливает причину.
Вывод
Пользовательские исключения используются для конкретной бизнес-логики и требований. В этой статье мы обсудили необходимость в них, а также рассмотрели их использование.
Код для примеров, используемых в этой статье, можно найти на Github .
Обзор
В этой статье мы рассмотрим процесс создания настраиваемых как
отмеченных, так и непроверенных исключений в Java.
Если вы хотите узнать больше об исключениях и обработке исключений в
Java, мы подробно рассмотрели это в — Обработка исключений в Java:
Полное руководство с лучшими и наихудшими
практиками.
Зачем использовать специальные исключения?
Хотя исключения Java, как таковые, охватывают почти все исключительные
случаи и условия, ваше приложение может генерировать конкретное
настраиваемое исключение, уникальное для вашего кода и логики.
Иногда нам нужно создать собственные для представления исключений
бизнес-логики, то есть исключений, специфичных для нашей бизнес-логики
или рабочего процесса. Например, EmailNotUniqueException
,
InvalidUserStateException
и т. Д.
Они помогают клиентам приложений лучше понять, что пошло не так. Они
особенно полезны для обработки исключений для REST API, поскольку
различные ограничения бизнес-логики требуют отправки клиенту разных
кодов ответа.
Если определение настраиваемого исключения не дает преимуществ по
сравнению с использованием обычного исключения в Java, нет необходимости
определять настраиваемые исключения, и вы должны просто придерживаться
тех, которые уже доступны — не нужно снова изобретать горячую воду.
Пользовательское проверенное исключение
Давайте рассмотрим сценарий, в котором мы хотим проверить электронное
письмо, переданное в качестве аргумента методу.
Мы хотим проверить, действительно ли это. Теперь мы могли бы
использовать встроенное в Java IllegalArgumentException
, что
нормально, если мы просто проверяем одну вещь, например, соответствует
ли она предопределенному REGEX или нет.
Но предположим, что у нас также есть бизнес-условие для проверки того,
что все электронные письма в нашей системе должны быть уникальными.
Теперь нам нужно выполнить вторую проверку (вызов БД / сетевой вызов).
Мы, конечно, можем использовать то же IllegalArgumentException
, но
будет неясно, какова точная причина — было ли электронное письмо не
прошло проверку REGEX или оно уже существует в базе данных.
Давайте создадим специальное исключение для обработки этой ситуации.
Чтобы создать исключение, как и любое другое исключение, мы должны
расширить класс java.lang.Exception
public class EmailNotUniqueException extends Exception {
public EmailNotUniqueException(String message) {
super(message);
}
}
Обратите внимание, что мы предоставили конструктор, который принимает
String
и вызывает конструктор родительского класса. Это не
обязательно, но это обычная практика — иметь какие-то подробности о
произошедшем исключении.
Вызывая super(message)
, мы инициализируем сообщение об ошибке
исключения, а базовый класс заботится о настройке настраиваемого
сообщения в соответствии с message
.
Теперь давайте используем это настраиваемое исключение в нашем коде.
Поскольку мы определяем метод, который может генерировать исключение на
уровне обслуживания, мы отметим его ключевым словом throws
Если входящий адрес электронной почты уже существует в нашей базе данных
(в данном случае это список), мы throw
собственное исключение:
public class RegistrationService {
List<String> registeredEmails = Arrays.asList(" [email protected] ", " [email protected] ");
public void validateEmail(String email) throws EmailNotUniqueException {
if (registeredEmails.contains(email)) {
throw new EmailNotUniqueException("Email Already Registered");
}
}
}
Теперь напишем клиента для нашего сервиса. Поскольку это проверенное
исключение, мы должны придерживаться правила дескриптора или объявления.
В следующем примере мы решили «обработать» это:
public class RegistrationServiceClient {
public static void main(String[] args) {
RegistrationService service = new RegistrationService();
try {
service.validateEmail(" [email protected] ");
} catch (EmailNotUniqueException e) {
// logging and handling the situation
}
}
}
Выполнение этого фрагмента кода даст:
mynotes.custom.checked.exception.EmailNotUniqueException: Email Already Registered
at mynotes.custom.checked.exception.RegistrationService.validateEmail(RegistrationService.java:12)
at mynotes.custom.checked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:9)
Примечание. Процесс обработки исключений опущен для краткости, но,
тем не менее, это важный процесс.
Пользовательское исключение без отметки
Это работает отлично, но наш код стал немного беспорядочным. Кроме того,
мы заставляем клиента фиксировать наше исключение в блоке try-catch
В
некоторых случаях это может привести к тому, что разработчиков заставят
писать шаблонный код.
В этом случае может быть полезно вместо этого создать настраиваемое
исключение времени выполнения. Чтобы создать настраиваемое непроверенное
исключение, мы должны расширить класс java.lang.RuntimeException
Давайте рассмотрим ситуацию, когда нам нужно проверить, есть ли в
электронном письме действительное доменное имя или нет:
public class DomainNotValidException extends RuntimeException {
public DomainNotValidException(String message) {
super(message);
}
}
Теперь позвольте использовать это в нашем сервисе:
public class RegistrationService {
public void validateEmail(String email) {
if (!isDomainValid(email)) {
throw new DomainNotValidException("Invalid domain");
}
}
private boolean isDomainValid(String email) {
List<String> validDomains = Arrays.asList("gmail.com", "yahoo.com", "outlook.com");
if (validDomains.contains(email.substring(email.indexOf("@") + 1))) {
return true;
}
return false;
}
}
Обратите внимание, что нам не нужно использовать throws
в сигнатуре
метода, поскольку это непроверенное исключение.
Теперь напишем клиента для нашего сервиса. На этот раз нам не нужно
использовать блок try-catch
:
public class RegistrationServiceClient {
public static void main(String[] args) {
RegistrationService service = new RegistrationService();
service.validateEmail(" [email protected] ");
}
}
Выполнение этого фрагмента кода даст:
Exception in thread "main" mynotes.custom.unchecked.exception.DomainNotValidException: Invalid domain
at mynotes.custom.unchecked.exception.RegistrationService.validateEmail(RegistrationService.java:10)
at mynotes.custom.unchecked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:7)
Примечание. Конечно, вы можете окружить свой код try-catch
чтобы
зафиксировать возникающее исключение, но теперь оно не принудительно
выполняется компилятором.
Повторное создание исключения, заключенного в настраиваемое исключение
Иногда нам нужно перехватить исключение и повторно выбросить его,
добавив еще несколько деталей. Обычно это происходит, если в вашем
приложении определены различные коды ошибок, которые необходимо либо
зарегистрировать, либо вернуть клиенту в случае этого конкретного
исключения.
Предположим, в вашем приложении есть стандартный класс ErrorCodes
public enum ErrorCodes {
VALIDATION_PARSE_ERROR(422);
private int code;
ErrorCodes(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
Создадим собственное исключение:
public class InvalidCurrencyDataException extends RuntimeException {
private Integer errorCode;
public InvalidCurrencyDataException(String message) {
super(message);
}
public InvalidCurrencyDataException(String message, Throwable cause) {
super(message, cause);
}
public InvalidCurrencyDataException(String message, Throwable cause, ErrorCodes errorCode) {
super(message, cause);
this.errorCode = errorCode.getCode();
}
public Integer getErrorCode() {
return errorCode;
}
}
Обратите внимание, что у нас есть несколько конструкторов, и пусть класс
обслуживания решает, какой из них использовать. Поскольку мы повторно
генерируем исключение, всегда рекомендуется фиксировать основную причину
исключения, следовательно, Throwable
который можно передать
конструктору родительского класса.
errorCode
код ошибки в одном из конструкторов и устанавливаем
errorCode внутри самого исключения. errorCode
может использоваться
клиентом для ведения журнала или любой другой цели. Это помогает в более
централизованном стандарте обработки исключений.
Напишем наш класс обслуживания:
public class CurrencyService {
public String convertDollarsToEuros(String value) {
try {
int x = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new InvalidCurrencyDataException("Invalid data", e, ErrorCodes.VALIDATION_PARSE_ERROR);
}
return value;
}
}
Итак, мы перехватили стандартное NumberFormatException
и выбросили
собственное InvalidCurrencyDataException
. Мы передали родительское
исключение нашему настраиваемому исключению, чтобы не потерять основную
причину, из которой они возникли.
Напишем тестовый клиент для этого сервиса:
public class CurrencyClient {
public static void main(String[] args) {
CurrencyService service = new CurrencyService();
service.convertDollarsToEuros("asd");
}
}
Выход:
Exception in thread "main" mynotes.custom.unchecked.exception.InvalidCurrencyDataException: Invalid data
at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:10)
at mynotes.custom.unchecked.exception.CurrencyClient.main(CurrencyClient.java:8)
Caused by: java.lang.NumberFormatException: For input string: "asd"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:8)
... 1 more
Как видите, у нас есть хорошая трассировка стека исключения, которая
может быть полезна для целей отладки.
Лучшие практики для настраиваемых исключений
- Соблюдайте общее соглашение об именах во всей экосистеме Java — все
имена классов настраиваемых исключений должны заканчиваться на
«Exception». - Избегайте создания пользовательских исключений, если стандартные
исключения из самого JDK могут служить этой цели. В большинстве
случаев определять собственные исключения не требуется. - Предпочитайте исключения времени выполнения установленным
исключениям. Фреймворки, такие как Spring ,
обернули все проверенные исключения в исключения времени выполнения,
следовательно, не заставляя клиента писать шаблонный код, который им
не нужен или не нужен. - Предоставьте много перегруженных конструкторов в зависимости от
того, как будет создано настраиваемое исключение. Если он
используется для повторного создания существующего исключения,
обязательно укажите конструктор, который устанавливает причину.
Заключение
Пользовательские исключения используются для конкретной бизнес-логики и
требований. В этой статье мы обсудили необходимость в них, а также
рассказали об их использовании.
Код примеров, использованных в этой статье, можно найти на
Github .
Создайте пользовательское исключение в Java
1. Вступление
В этом руководстве мы рассмотримhow to create a custom exception in Java.
Мы покажем, как пользовательские исключения реализуются и используются как для проверенных, так и для непроверенных исключений.
2. Необходимость настраиваемых исключений
Java-исключения охватывают почти все общие исключения, которые обязательно должны произойти в программировании.
Однако иногда нам необходимо дополнить эти стандартные исключения нашими собственными.
Основными причинами введения пользовательских исключений являются:
-
Исключения бизнес-логики — исключения, характерные для бизнес-логики и рабочего процесса. Они помогают пользователям приложения или разработчикам понять, в чем именно заключается проблема
-
Чтобы поймать и предоставить конкретную обработку для подмножества существующих исключений Java
Java исключения могут быть проверены и сняты. В следующих разделах мы рассмотрим оба этих случая.
3. Пользовательское Проверенное Исключение
Проверенные исключения — это исключения, которые необходимо обрабатывать явно.
Давайте рассмотрим фрагмент кода, который возвращает первую строку файла:
try (Scanner file = new Scanner(new File(fileName))) {
if (file.hasNextLine()) return file.nextLine();
} catch(FileNotFoundException e) {
// Logging, etc
}
Приведенный выше код является классическим способом обработки проверенных исключений Java. Хотя код выдаетFileNotFoundException,, неясно, какова точная причина — файл не существует или имя файла недействительно.
Чтобы создать собственное исключение, мы должны расширить классjava.lang.Exception.
Давайте посмотрим на пример, создав пользовательское проверяемое исключение под названиемIncorrectFileNameException:.
public class IncorrectFileNameException extends Exception {
public IncorrectFileNameException(String errorMessage) {
super(errorMessage);
}
}
Обратите внимание, что мы также должны предоставить конструктор, который принимаетString в качестве сообщения об ошибке и вызывает конструктор родительского класса.
Это все, что нам нужно сделать, чтобы определить настраиваемое исключение.
Далее, давайте посмотрим, как мы можем использовать пользовательское исключение в нашем примере:
try (Scanner file = new Scanner(new File(fileName))) {
if (file.hasNextLine())
return file.nextLine();
} catch (FileNotFoundException e) {
if (!isCorrectFileName(fileName)) {
throw new IncorrectFileNameException("Incorrect filename : " + fileName );
}
//...
}
Мы создали и использовали настраиваемое исключение, поэтому теперь пользователь может знать, что это за исключение. Этого достаточно? Следовательно, мыlosing the root cause of the exception.
Чтобы исправить это, мы также можем добавить в конструктор параметрjava.lang.Throwable. Таким образом, мы можем передать корневое исключение в вызов метода:
public IncorrectFileNameException(String errorMessage, Throwable err) {
super(errorMessage, err);
}
ТеперьIncorrectFileNameException используется вместе с основной причиной исключения следующим образом:
try (Scanner file = new Scanner(new File(fileName))) {
if (file.hasNextLine()) {
return file.nextLine();
}
} catch (FileNotFoundException err) {
if (!isCorrectFileName(fileName)) {
throw new IncorrectFileNameException(
"Incorrect filename : " + fileName , err);
}
// ...
}
Вот как мы можем использовать собственные исключенияwithout losing the root cause from which they occurred.
4. Пользовательское непроверенное исключение
В нашем же примере предположим, что нам нужно специальное исключение, если имя файла не содержит расширения.
В этом случае нам понадобится настраиваемое непроверенное исключение, подобное предыдущему, поскольку эта ошибка будет обнаруживаться только во время выполнения.
To create a custom unchecked exception we need to extend the java.lang.RuntimeException class:
public class IncorrectFileExtensionException
extends RuntimeException {
public IncorrectFileExtensionException(String errorMessage, Throwable err) {
super(errorMessage, err);
}
}
Следовательно, мы можем использовать это нестандартное исключение в нашем примере:
try (Scanner file = new Scanner(new File(fileName))) {
if (file.hasNextLine()) {
return file.nextLine();
} else {
throw new IllegalArgumentException("Non readable file");
}
} catch (FileNotFoundException err) {
if (!isCorrectFileName(fileName)) {
throw new IncorrectFileNameException(
"Incorrect filename : " + fileName , err);
}
//...
} catch(IllegalArgumentException err) {
if(!containsExtension(fileName)) {
throw new IncorrectFileExtensionException(
"Filename does not contain extension : " + fileName, err);
}
//...
}
5. Заключение
Пользовательские исключения очень полезны, когда нам нужно обработать определенные исключения, связанные с бизнес-логикой. При правильном использовании они могут служить полезным инструментом для лучшей обработки и регистрации исключений.
Код для примеров, использованных в этой статье, доступенover on Github.
Защита от дурака — стандартная конструкция для любого приложения. Рассмотрим, как организовать её в Java.
https://gbcdn.mrgcdn.ru/uploads/post/1217/og_cover_image/cf0ec3cbe152ff0e4cb3936d72d59bea
Одно из стандартных требований ТЗ на разработку ПО – отсутствие ошибок и конфликтов, препятствующих нормальной работе. Путей реализации два – ограничить функциональность и возможности пользователя или создать код, который будет учитывать возможные неприятности.
В java исключением называется любая ошибка, которая возникает в ходе выполнения программы. Это может быть несоответствие типов данных, деление на ноль, обрыв связи с сервером и многое другое. Операции по их поиску и предотвращению называются обработкой исключений.
Иерархия
Прежде чем мы перейдём к практике, давайте познакомимся с видами исключений Джава и их иерархией. В основе всего лежит класс Throwable. Все возможные конфликты кода с машиной и пользователем описаны здесь. Для удобства обработки и чтения класс Throwable имеет подклассы Error и Exception. Error – критические ошибки, которые не обязательно происходят по вине пользователя, обработать их невозможно. Exception – собственно конфликты нашей программы, которые необходимо отлавливать.
Взгляните на упрощённую схему иерархии исключений java:
Как видно, блоки делятся на «два лагеря» по цветам — проверяемые и непроверяемые java исключения. Данная классификация показывает, как их воспринимает компилятор: проверяемые – учитывает, непроверяемые – игнорирует. К первому относится Exception в полном составе, кроме RuntimeException. Все остальные классы исключений – непроверяемые компилятором.
Иерархия классов исключений важна и для правильной организации кода. Допустим, у вас есть несколько блоков обработки. Тогда в начале необходимо указать низшие уровни, а в конце – высшие. В противном случае, будет запущен только первый блок, а остальные – проигнорированы.
Создание обработчика
Для обработки исключений java используются следующие операторы: try, catch, finally, throw, throws. Первые три — стандартная структура вашего блока. По шагам:
- Оператор или часть кода, в которой вам надо отыскать ошибку, помещается в блок try.
- Далее в блоке catch вы указываете, что за исключение надо ловить и как его обрабатывать.
- В блоке finally набор обязательных действий при возникновении ошибки. Обычно это запись данных, закрытие ресурсов и пр. Блок исполняется всегда, вне зависимости от срабатывания catch.
Рассмотрим структуру на примере Джава исключения:
try {
// код, где мы хотим отследить ошибку
}
catch (тип_исключения объект_исключения) {
// код обработки
}
finally {
// что нужно выполнить после завершения блока try
}
Если вы хотите обработать несколько исключений – просто создайте ещё один блок catch.
try {
// код, где мы хотим отследить ошибку
}
catch (тип_исключения_1 объект_исключения_1) {
// код обработки
}
catch (тип_исключения_2 объект_исключения_2) {
// код обработки
}
finally {
// что нужно выполнить после завершения блока try
}
С помощью оператора throw вы можете создавать исключения:
throw экземпляр_Throwable
На практике это выглядит так:
Student stud1;
public void onClick(View view) {
if(stud1 == null){
throw new NullPointerException(«Студента не существует»);
}
}
Включим оператор throw в наш стандартный пример с try-catch:
public void onClick(View view) {
if (stud1 == null) {
try {
throw new NullPointerException(«Студента не существует»);
} catch (NullPointerException e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
Как только обработка дойдёт до оператора throw, дальнейшее выполнение кода будет прекращено. Обработчик рассмотрит ближайший блок try-catch на требуемое исключение, потом следующий и так до конца кода. В случае, если вызвать ява исключение неоткуда – обработчик остановит программу.
Оператор throws используется для методов, которые содержат исключения, но их не обрабатывают.
тип имя_метода(список_параметров) throws список_исключений {
// код метода
}
Несколько исключений в списке необходимо перечислить через запятую. Воспользовавшись такой конструкцией, вы сообщите всем вызывающим методам о необходимости учитывать исключения.
Операторы try можно вкладывать друг в друга. При этом если вложенный обработчик не имеет своего блока catch, он осуществляет его поиск в родительском операторе. Если и там нет – блок обрабатывается системой.
Готовые и новые исключения
Далее приведём список java исключений, которые вам потребуются в работе чаще других:
- ArithmeticException — ошибки вычислений.
- NullPointerException — ссылка на пустое место.
- NegativeArraySizeException — массив отрицательной размерности.
- ArrayStoreException — присвоение элементу массива неправильного типа.
- NumberFormatException — невозможно преобразовать строку в число.
- IllegalArgumentException — неправильный аргумент при вызове метода.
- UnsupportedOperationException — указанной операции не существует.
- TypeNotPresentException — указанного типа не существует.
Все указанные типы java исключений содержатся в классе RuntimeException, а значит, их не надо указывать в блоке throws.
Естественно, система не может содержать всевозможные исключения. Некоторые придётся создавать самостоятельно. Для того, чтобы создать собственное java исключение, вам необходимо унаследовать собственный класс от Exception и переопределить требуемые методы класса Throwable. Или унаследоваться от наиболее близкого по смыслу типа. Рассмотрим на примере программы под android создание java исключения:
package geekbrains.exception;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void testMethod() throws StudentException {
System.out.println(«Возбуждаем StudentException из метода testMethod()»);
throw new StudentException(); // конструктор по умолчанию
}
public void testMethod2() throws StudentException {
System.out.println(«Возбуждаем StudentException из метода testMethod2()»);
throw new StudentException(«Создано во втором методе»);
}
public void onClick(View view) {
try {
testMethod();
} catch (StudentException e) {
e.printStackTrace();
System.out.println(«Исключение перехвачено»);
}
try {
testMethod2();
} catch (StudentException e) {
e.printStackTrace();
}
}
class StudentException extends Exception {
StudentException() {
}
StudentException(String msg) {
super(msg);
}
}
}
Обработка исключений – основа безопасного и качественного кода. С их помощью вы можете управлять действиями пользователя, ходом выполнения программы или добавить вариативности коду.