Как эффективно использовать исключения Java

Java задняя часть Python JVM

Исключения в Java — это особенность языка Java. Это также то, что часто используется в повседневном кодировании. Но вы действительно понимаете исключения?

Вот несколько классических вопросов для интервью об исключениях:

  • Какова структура класса и основные отношения наследования, связанные с исключениями в Java?
  • Какие улучшения внесла Java 7 в синтаксис исключений?
  • Что такое исключения времени выполнения и декларативные исключения? В чем разница между ними?
  • В чем проблема «отсутствует исключение (покрытие исключения)»?
  • Что такое цепочка исключений?
  • Что такое переопределение возвращаемого значения?
  • Некоторые лучшие практики при написании исключений?

Если вы можете понять ответы на приведенные выше вопросы, то поздравляю, вы уже знакомы с исключениями Java.

Если какие-то вопросы остались неясными? Неважно, просто прочитайте эту статью.

иерархия исключений

Первый на карте

Оставляя в стороне следующие исключения, мы можем сосредоточиться в основном на четырех категориях:

  • Throwable
  • Error
  • Exception
  • RuntimeException

из них потому чтоErrorЭто означает «ошибка», что в основном является серьезной ошибкой. если ты понимаешьJVM, должно быть правильноOutOfMemoryErrorиStackOverflowErrorЭти два класса более знакомы.

Как правило, когда мы пишем код, они могут использовать больше isExceptionКласс иRuntimeExceptionсвоего рода.

Это наследствоExceptionкласс или наследованиеRuntimeExceptionкласс хорошо? Мы рассмотрим это позже в разделе «Оптимальные методы написания исключений».

Java7 и исключения

Java7 ненормальный сделал два улучшения. первыйtry-with-resources, второйпоймать несколько исключений.

try-with-resources

Так называемая попытка с ресурсами — это синтаксический сахар. Фактически функция close() ресурса вызывается автоматически. Подобно оператору with в Python.

Без попытки с ресурсами, когда мы используем объекты ресурсов, такие как io, мы обычно пишем:

String getReadLine() throws IOException {
    BufferedReader br = new BufferedReader(fileReader);
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Используйте нотацию try-with-resources:

String getReadLine() throws IOException {
    try (BufferedReader br = new BufferedReader(fileReader)) {
        return br.readLine();
    }
}

Очевидно, что компилятор автоматически добавляет ByteCode для оценки того, является ли объект NULL после TRUE-RESORCES, и если он не нулевой, вызовите функцию CLACK ().

Функция Close() автоматически вызывается только при наличии интерфейса Java.lang.AutoCloseable или объекта java.io.closable (фактически следующего из интерфейса Java.lang.AutoCloseable).

Немного отличается то, что java.io.Closable требует от разработчика обеспечения многократного вызова функции закрытия. Функция close() AutoCloseable не обязана быть идемпотентной. Подробности см. в Javadoc.

Однако следует отметить, что попытка использования ресурсов появитсяпокрытие исключенийпроблема, так сказатьcatchИсключения, генерируемые блоком, могут быть вызваныclose()Исключение, создаваемое методом, переопределяется. Мы поговорим о покрытии исключений в следующем подразделе.

Множественное улово исключения

Перейдите непосредственно к коду:

public static void main(String[] args) {
    try {
        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        int c = a / b;
        System.out.println("result is:" + c);
    } catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException ie) {
        System.out.println("发生了以上三个异常之一。");
        ie.getMessage();
        // 捕捉多异常时,异常变量默认有final修饰,
        // 所以下面代码有错:
        // ie = new ArithmeticException("test");
    }
}

Suppressed

еслиcatchблокировать иfinallyЧто, если все блоки генерируют исключения? См. анализ в следующем подразделе.

Исключения времени выполнения и декларативные исключения

Так называемое исключение времени выполнения относится кRuntimeException, вам не нужно явно перехватывать исключение времени выполнения и не нужно объявлять его в методе.

И наоборот, если ваше исключение является простоException, это должно быть захвачено явно.

Образец кода:

void test() {
    hasRuntimeException();
    try {
        hasException();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

void hasException() throws Exception {
    throw new Exception("exception");
}

void hasRuntimeException() {
    throw new RuntimeException("runtime");
}

Хотя мы можем видеть из диаграммы аномальной структуры,RuntimeExceptionунаследовано отException. Но Java «особо обрабатывает» исключения во время выполнения. Поэтому, если вам нужны такие исключения в вашей программе, вы можете наследоватьRuntimeException.

И если исключение явно не требуется передавать на верхний уровень для захвата и обработки, мы рекомендуемПредпочитать исключения во время выполнения, потому что это сделает ваш код более лаконичным.

Что такое покрытие исключений

Как мы упоминали ранее, вfinallyзаблокировать ресурс вызоваclose()метод, можно бросить исключение. В то же время мы можем бытьcatchБлок выдал еще одно исключение. ТакcatchИсключения, выбрасываемые блоком, будутfinallyИсключение блока "съедено".

Глядя на этот код, вызываяtest()Что будет выработать метод?

void test() {
    try {
        overrideException();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

void overrideException() throws Exception {
    try {
        throw new Exception("A");
    } catch (Exception e) {
        throw new Exception("B");
    } finally {
        throw new Exception("C");
    }
}

будет выводитьC. Видно, что вcatchблокироватьBбыл съеден.

JDK предоставляет два метода SuppressedДля решения этой проблемы:

// 调用test会输出:
// C
// A
void test() {
    try {
        overrideException();
    } catch (Exception e) {
        System.out.println(e.getMessage());
        Arrays.stream(e.getSuppressed())
                .map(Throwable::getMessage)
                .forEach(System.out::println);
    }
}

void overrideException() throws Exception {
    Exception catchException = null;
    try {
        throw new Exception("A");
    } catch (Exception e) {
        catchException = e;
    } finally {
        Exception exception = new Exception("C");
        exception.addSuppressed(catchException);
        throw exception;
    }
}

цепочка исключений

Вы можете бросить новое исключение при использованииinitCauseметод, указывающий, какое исключение вызвало исключение, и, наконец, формирующий цепочку исключений.

Для получения подробной информации, пожалуйста, обратитесь к статье о цепочке исключений перед официальной учетной записью.

переопределение возвращаемого значения

Подобно предыдущей проблеме «покрытия исключений»,finallyблок будет перезаписанtryиcatchВозвращаемое значение блока.

Таким образом, лучшая практика заключается в том, чтобы неfinalyиспользование блокаreturn!!!

Лучшие практики?

  1. Используйте Runtimeexception, если вы можете
  2. попробуй не бытьfinallyБлок выдает исключение или возвращает значение
  3. Попробуйте использовать новый синтаксис Java7 для исключений
  4. Блок try-catch можно превратить в отдельный метод, чтобы сделать код более лаконичным — см. главу 7 книги «Путь к очистке кода».
  5. Запись журналов исключений можно комбинировать с журналом иprintStackTrace

Справочная статья