我可以在同一catch子句中捕获多个Java异常吗?


699

在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();
}

有什么办法吗?

Answers:


1130

从Java 7开始,这已经成为可能。多捕获块的语法为:

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

但是请记住,如果所有异常都属于同一类层次结构,则可以简单地捕获该基本异常类型。

还要注意,如果从ExceptionA直接或间接继承了ExceptionB,则不能在同一块中同时捕获ExceptionA和ExceptionB。编译器会抱怨:

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 2013年

11
@ArtOfWarfare也许他们已经想出了泛型的多个范围的语法,但是他们认为这不再重要了。
JimmyB

12
XOR符号(I)与OR(||),A | B表示A或B,不是同时A || B表示A或B或两者都表示,因此对于例外来说,它是exceptionA或exceptionB,但不是同时出现。这就是为什么他们使用XOR而不是OR的原因,并且如果您放置2个异常,其中一个是另一个的子类型,则可以清楚地看到引发异常时的情况
user1512999

41
Java中的@ user1512999,按位XOR为^(脱字符),按位OR为| (管道) docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Lewis Baumstark

6
值得一提的是,在多捕获块中捕获的异常类型被评估为最派生的公共父级
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()。作为附带说明,Robert C. Martin(除其他外)建议使用未经检查的异常(编译器不知道会从那里抛出什么样的异常)。请参阅 他的书Clean code中的第7章:错误处理
2013年

4
在您的Java 6示例中,您应该不应该抛出原始异常而不是创建新的异常实例,即throw exc而不是throw new RuntimeException(exc)
David DeMar 2014年

5
从可读性的角度来看,这是非常糟糕的做法。
Rajesh J Advani 2014年

3
Instanceof操作的成本很高,最好避免使用。
Paramesh Korrakuti 2015年

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){//从未到达}
Michael Shopsin 2010年

4
实际上,正是由于永远无法实现,所以此类代码甚至无法编译。
polygenelubricants 2010年

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的部分吗?我意识到这是一个例子,但是我觉得有些人可能会读到这个答案,然后说:“哦,好吧,那我就赶上Exception”,那时那可能不是他们想要(或不应该)做的。
罗布·赫鲁斯卡

2
我知道这一点,但我不想这么做...哦,好吧,我猜那时候我有4个问题,直到Java的下一个版本……
froadie 2010年

@duffymo:记录和重新抛出有什么问题?除了使代码混乱外,它等同于不捕获它,不是吗。从一般错误处理策略的角度来看。不好的是记录而不是重新抛出。
弗兰克·奥斯特菲尔德

5
我不考虑记录日志并重新处理任何东西。我更愿意让它冒充那些可以做有意义的事情的人。在这种情况下,永远都不应逃脱异常的最后一层(例如,Web应用程序中的控制器)应该是记录错误的那一层。
duffymo,2010年

我是唯一发现没有为我自动生成日志的说法很荒谬的人吗?似乎每当某段代码可能引发异常时,我们所有人都必须编写相同的愚蠢日志消息。
ArtOfWarfare 2013年

10

在Java 6(即Android)上,替代user454322的答案的更简洁(但不太冗长,也许不是首选)的方法是捕获所有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
这种方法使编译器无法确定是否可以到达“ catch块”。
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
wuold是一个不错的解决方案。如何caughtfinally块中插入的最终控件?
Andrea_86

与原始问题相比,这需要更多行。
Leandro Glossman

1

是。这是使用pipe(|)分隔符的方法,

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这样的异常通常是编程错误,通常应通过检查null值来解决。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.