Могу ли я перехватить несколько исключений Java в одном предложении catch?


703

В Java я хочу сделать что-то вроде этого:

try {
    ...     
} catch (/* code to catch IllegalArgumentException, SecurityException, 
            IllegalAccessException, and NoSuchFieldException at the same time */) {
   someCode();
}

...вместо:

try {
    ...     
} catch (IllegalArgumentException e) {
    someCode();
} catch (SecurityException e) {
    someCode();
} catch (IllegalAccessException e) {
    someCode();
} catch (NoSuchFieldException e) {
    someCode();
}

Есть какой-либо способ сделать это?

Ответы:


1134

Это стало возможным начиная с Java 7 . Синтаксис для блока multi-catch:

try { 
  ...
} catch (IOException | SQLException ex) { 
  ...
}

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

Также обратите внимание, что вы не можете перехватить и ExceptionA, и ExceptionB в одном и том же блоке, если ExceptionB наследуется, прямо или косвенно, от ExceptionA. Компилятор будет жаловаться:

Alternatives in a multi-catch statement cannot be related by subclassing
  Alternative ExceptionB is a subclass of alternative ExceptionA

81
TT - зачем переопределять оператор bitwise or( |)? Почему бы не использовать запятую или оператор, имеющий более похожее значение, logical or( ||)?
— ArtOfWarfare

11
@ArtOfWarfare Может быть, они подумали, что это уже не будет иметь значения после того, как они уже разработали синтаксис множественных границ для дженериков.
— JimmyB

12
Знак XOR (I) не совпадает со знаком OR (||), A | B означает либо A, либо B, но не оба A || B означает либо A, либо B, либо оба, так что для исключений это либо исключение A, либо исключение B, но не оба одновременно. Вот почему они использовали XOR sing вместо OR, и вы можете ясно видеть, что когда исключение - броски, если вы добавили 2 исключения, одно из них является
— подтипом

41
@ user1512999 в Java, побитовое значение XOR равно ^ (каретка), а побитовое ИЛИ равно | (труба) docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
— Льюис

6
Стоит отметить, что тип исключения, перехваченного в блоке multi-catch, оценивается для самого производного общего родителя
— yanpas

104

Не совсем до Java 7, но я бы сделал что-то вроде этого:

Java 6 и раньше

try {
  //.....
} catch (Exception exc) {
  if (exc instanceof IllegalArgumentException || exc instanceof SecurityException || 
     exc instanceof IllegalAccessException || exc instanceof NoSuchFieldException ) {

     someCode();

  } else if (exc instanceof RuntimeException) {
     throw (RuntimeException) exc;     

  } else {
    throw new RuntimeException(exc);
  }

}



Java 7

try {
  //.....
} catch ( IllegalArgumentException | SecurityException |
         IllegalAccessException |NoSuchFieldException exc) {
  someCode();
}

11
Обратите внимание, что ваш пример Java 6 нарушает способность компилятора определять, что будет выброшено откуда.
— MichaelBlume

2
@MichaelBlume Правда, что не так плохо. Вы всегда можете получить оригинальное исключение с exc.getCause(). В качестве примечания, Роберт К. Мартин (среди прочих) рекомендует использовать непроверенные исключения (компилятор не имеет представления о том, какие исключения будут выбрасываться оттуда); обратитесь к главе 7: Обработка ошибок в его книге « Чистый код» .
— user454322

4
В вашем примере с Java 6 не следует ли перебрасывать исходное исключение вместо создания нового экземпляра исключения, т.е. throw excвместо throw new RuntimeException(exc)?
— Дэвид ДеМар

5
Это довольно плохая практика с точки зрения читабельности.
— Раджеш Дж Адвани

3
Экземпляр операции немного дорог, лучше избегать как можно больше.
— Парамеш Корракути

23

В Java 7 вы можете определить несколько предложений catch, таких как:

catch (IllegalArgumentException | SecurityException e)
{
    ...
}

16

Если существует иерархия исключений, вы можете использовать базовый класс для перехвата всех подклассов исключений. В вырожденном случае вы можете перехватить все исключения Java с помощью:

try {
   ...
} catch (Exception e) {
   someCode();
}

В более распространенном случае, если RepositoryException является базовым классом, а PathNotFoundException является производным классом, тогда:

try {
   ...
} catch (RepositoryException re) {
   someCode();
} catch (Exception e) {
   someCode();
}

Приведенный выше код будет перехватывать RepositoryException и PathNotFoundException для одного вида обработки исключений, а все остальные исключения объединяются. Начиная с Java 7, согласно ответу @ OscarRyz выше:

try { 
  ...
} catch( IOException | SQLException ex ) { 
  ...
}

7
BTW предложения catch обрабатываются по порядку, поэтому, если вы помещаете родительский класс исключений перед дочерним классом, он никогда не вызывается, например: try {...} catch (Exception e) {someCode (); } catch (RepositoryException re) {// никогда не достигался}
— Майкл Шопсин

4
На самом деле именно потому, что он никогда не будет достигнут, такой код даже не компилируется.
— полигенасмазочные материалы

15

Нет, по одному на каждого клиента.

Вы можете поймать суперкласс, такой как java.lang.Exception, если вы выполняете одно и то же действие во всех случаях.

try {
    // some code
} catch(Exception e) { //All exceptions are caught here as all are inheriting java.lang.Exception
    e.printStackTrace();
}

Но это не может быть лучшей практикой. Вы должны ловить исключение только тогда, когда у вас есть стратегия для его фактической обработки - а регистрация и повторная обработка не являются «обработкой». Если у вас нет корректирующего действия, лучше добавьте его в сигнатуру метода и дайте всплыть кому-то, кто может справиться с ситуацией.


20
Могу ли я попросить вас перефразировать часть о ловле java.lang.Exception? Я понимаю, что это пример, но я чувствую, что некоторые люди могут прочитать этот ответ и сказать: «О, хорошо, тогда я просто поймаю исключение», когда это, вероятно, не то, что они хотят (или должны) сделать.
— Роб Хруска

2
Я знал об этом, но я не хочу этого делать ... О, ну, думаю, я застрял с 4 уловами тогда, до следующей версии Java ...
— froadie

@duffymo: Что не так с ведением журнала и повторным броском? За исключением того, что он загромождает код, его эквивалент не перехватывает его, не так ли. Рассматривается с точки зрения общей стратегии обработки ошибок. Что плохо, так это регистрация, а не повторное выбрасывание.
— Фрэнк Остерфельд

5
Я не рассматриваю ведение журнала и повторную обработку чего-либо. Я предпочел бы позволить этому пузыриться кому-то, кто может сделать что-то значимое. Последний слой, где исключения никогда не должны выходить (например, контроллеры в веб-приложении), должен регистрировать ошибку в этом случае.
— duffymo

Я единственный, кто находит абсурдным, что журнал не генерируется автоматически для меня? Кажется, мы все должны писать одно и то же глупое сообщение в журнале каждый раз, когда какой-то фрагмент кода может вызвать исключение.
— ArtOfWarfare

10

Более чистая (но менее многословная и, возможно, не столь предпочтительная) альтернатива ответу пользователя user454322 на Java 6 (т. Е. На Android) будет заключаться в том, чтобы перехватывать все Exceptions и перебрасывать RuntimeExceptions. Это не сработает, если вы планируете перехватывать другие типы исключений дальше по стеку (если только вы не перебрасываете их), но будет эффективно перехватывать все проверенные исключения.

Например:

try {
    // CODE THAT THROWS EXCEPTION
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        // this exception was not expected, so re-throw it
        throw e;
    } else {
        // YOUR CODE FOR ALL CHECKED EXCEPTIONS
    } 
}

Это, как говорится, для многословия, возможно, было бы лучше установить логическую или некоторую другую переменную и на основе этого выполнить некоторый код после блока try-catch.


1
Этот подход не позволяет компилятору определить, будет ли доступен «блок захвата».
— the_new_mr

3

В до 7 как насчет:

  Boolean   caught = true;
  Exception e;
  try {
     ...
     caught = false;
  } catch (TransformerException te) {
     e = te;
  } catch (SocketException se) {
     e = se;
  } catch (IOException ie) {
     e = ie;
  }
  if (caught) {
     someCode(); // You can reference Exception e here.
  }

3
будет хорошим решением. Как насчет вставки окончательного контроля caughtв finallyблоке?
— Andrea_86

Это требует больше строк, чем оригинальный вопрос.
— Леандро Глоссман

1

Да. Вот способ с использованием разделителя трубы (|),

try
{
    .......
}    
catch
{
    catch(IllegalArgumentException | SecurityException | IllegalAccessException | NoSuchFieldException e)
}

Что это за стиль кода? Блок catch в блок try?
— Сэм

1

Для kotlin это пока невозможно, но они решили добавить его: Source
Но пока, просто небольшая хитрость:

try {
    // code
} catch(ex:Exception) {
    when(ex) {
        is SomeException,
        is AnotherException -> {
            // handle
        }
        else -> throw ex
    }
}

0

Поймать исключение, которое оказывается родительским классом в иерархии исключений. Это, конечно, плохая практика . В вашем случае общим родительским исключением является класс Exception, и перехват любого исключения, которое является экземпляром Exception, действительно является плохой практикой - исключения, такие как NullPointerException, обычно являются ошибками программирования и обычно должны решаться путем проверки нулевых значений.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.