Пользовательские исключения
Стандартная библиотека Java содержит десятки готовых классов исключений, но иногда полезно создать собственный — чтобы выразить семантику предметной области и передавать вместе с ошибкой дополнительные данные (номер пина, имя датчика, код ответа).
Создание пользовательского исключения сводится к наследованию от одного из классов:
``Exception`` — для checked-исключений, которые вызывающий код обязан обработать.
``RuntimeException`` — для unchecked-исключений, которые обычно сигнализируют о баге в коде (нарушение контракта, неверные аргументы).
Имя класса по соглашению заканчивается на Exception. Полезно предоставить несколько
конструкторов, повторяющих сигнатуры родителя.
Синтаксис
public class SensorException extends Exception {
public SensorException(String message) {
super(message);
}
public SensorException(String message, Throwable cause) {
super(message, cause);
}
}
Пример 1. Checked-исключение
class SensorTimeoutException extends Exception {
public SensorTimeoutException(String message) {
super(message);
}
}
public class CheckedDemo {
static int readDistance() throws SensorTimeoutException {
throw new SensorTimeoutException("RCWL-9610A не ответил за 1000 мс");
}
public static void main(String[] args) {
try {
readDistance();
} catch (SensorTimeoutException e) {
System.out.println("Ошибка: " + e.getMessage());
}
}
}
Output:
Ошибка: RCWL-9610A не ответил за 1000 мс
Пример 2. Unchecked-исключение
class InvalidPinException extends RuntimeException {
public InvalidPinException(int pin) {
super("Недопустимый пин: " + pin);
}
}
public class UncheckedDemo {
static void setupPin(int pin) {
if (pin < 0 || pin > 53) {
throw new InvalidPinException(pin);
}
System.out.println("Пин " + pin + " готов");
}
public static void main(String[] args) {
setupPin(13);
try {
setupPin(100);
} catch (InvalidPinException e) {
System.out.println(e.getMessage());
}
}
}
Output:
Пин 13 готов
Недопустимый пин: 100
Пример 3. Передача дополнительных данных
class SensorReadException extends Exception {
private final String sensorName;
private final int errorCode;
public SensorReadException(String sensorName, int errorCode, String message) {
super(message);
this.sensorName = sensorName;
this.errorCode = errorCode;
}
public String getSensorName() { return sensorName; }
public int getErrorCode() { return errorCode; }
}
public class CustomDataDemo {
public static void main(String[] args) {
try {
throw new SensorReadException("DHT22", 42, "CRC mismatch");
} catch (SensorReadException e) {
System.out.println("Датчик: " + e.getSensorName());
System.out.println("Код: " + e.getErrorCode());
System.out.println("Текст: " + e.getMessage());
}
}
}
Output:
Датчик: DHT22
Код: 42
Текст: CRC mismatch
Пример 4. Цепочка с cause
class ConfigLoadException extends Exception {
public ConfigLoadException(String message, Throwable cause) {
super(message, cause);
}
}
public class ChainDemo {
static void loadConfig() throws ConfigLoadException {
try {
Integer.parseInt("bad");
} catch (NumberFormatException e) {
throw new ConfigLoadException("Не удалось загрузить config.ini", e);
}
}
public static void main(String[] args) {
try {
loadConfig();
} catch (ConfigLoadException e) {
System.out.println(e.getMessage());
System.out.println("Причина: " + e.getCause().getClass().getSimpleName());
}
}
}
Output:
Не удалось загрузить config.ini
Причина: NumberFormatException
Пример 5. Иерархия пользовательских исключений
class DeviceException extends Exception {
public DeviceException(String msg) { super(msg); }
}
class MotorException extends DeviceException {
public MotorException(String msg) { super(msg); }
}
class ServoException extends DeviceException {
public ServoException(String msg) { super(msg); }
}
public class HierarchyDemo {
static void runDevice(int id) throws DeviceException {
if (id == 1) throw new MotorException("Мотор заклинило");
if (id == 2) throw new ServoException("Серво не отвечает");
}
public static void main(String[] args) {
for (int i = 1; i <= 2; i++) {
try {
runDevice(i);
} catch (DeviceException e) {
System.out.println(e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
}
}
Output:
MotorException: Мотор заклинило
ServoException: Серво не отвечает
Подводные камни
Предупреждение
Не наследуйтесь от ``Throwable`` или ``Error`` напрямую — это нарушает соглашения.
Для checked-исключения добавьте поле
serialVersionUID, если класс будет сериализован.Избегайте слишком общих исключений — конкретный тип помогает в отладке.
Конструктор должен вызывать ``super(message)`` — иначе
getMessage()вернётnull.Не создавайте исключения там, где достаточно вернуть результат — это дорого по производительности из-за заполнения stack trace.
См. также
Примечание
Материал подготовлен на основе официальной документации Oracle Java Tutorials — Creating Exception Classes (Oracle Free Documentation License). Текст переведён и переработан, примеры кода написаны заново.