我可以在同一个 catch 子句中捕获多个 Java 异常吗?
Posted
技术标签:
【中文标题】我可以在同一个 catch 子句中捕获多个 Java 异常吗?【英文标题】:Can I catch multiple Java exceptions in the same catch clause? 【发布时间】:2011-03-30 14:00:30 【问题描述】:在 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();
有什么办法吗?
【问题讨论】:
【参考方案1】:这是可能的since Java 7。多捕获块的语法是:
try
...
catch (IllegalArgumentException | SecurityException | IllegalAccessException |
NoSuchFieldException e)
someCode();
但请记住,如果所有异常都属于同一个类层次结构,您可以简单地捕获该基本异常类型。
另外请注意,如果ExceptionB
直接或间接继承自ExceptionA
,则不能在同一块中同时捕获ExceptionA
和ExceptionB
。编译器会抱怨:
Alternatives in a multi-catch statement cannot be related by subclassing
Alternative ExceptionB is a subclass of alternative ExceptionA
解决此问题的方法是仅在异常列表中包含祖先异常,因为它也会捕获后代类型的异常。
【讨论】:
T.T - 为什么要重新定义bitwise or
(|
) 运算符?为什么不使用逗号或具有更相似含义的运算符logical or
(||
)?
@ArtOfWarfare 也许他们已经想出了泛型的multiple bounds 的语法后认为这不再重要了。
XOR 符号 (I) 与 OR (||) 不同,A | B 表示 A 或 B 但 不是 A || B 表示 A 或 B 或两者都表示,因此对于异常,它是异常 A 或异常 B,但不能同时表示两者。这就是为什么他们使用 XOR sing 而不是 OR 的原因,如果您放置 2 个异常,其中一个是另一个的子类型,您可以清楚地看到何时抛出异常
@user1512999 在 Java 中,按位 XOR 是 ^(插入符号),按位 OR 是 | (管道)docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
值得一提的是,multi-catch 块中捕获的异常类型被评估为最衍生的公共父项【参考方案2】:
不完全是在 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();
【讨论】:
请注意,您的 Java 6 示例破坏了编译器判断从哪里抛出什么的能力。 @MichaelBlume 是的,这还不错。您始终可以使用exc.getCause()
获得原始异常。作为旁注,Robert C. Martin(以及其他人)建议使用未经检查的异常(编译器不知道会从那里抛出什么样的异常);参考他的书Clean code中的Chapter 7: Error Handling。
在您的 Java 6 示例中,您不应该重新抛出原始异常而不是创建新的异常实例,即 throw exc
而不是 throw new RuntimeException(exc)
?
从可读性的角度来看,这是非常糟糕的做法。
Instanceof操作有点费力,最好尽量避免。【参考方案3】:
在 Java 7 中,您可以定义多个 catch 子句,例如:
catch (IllegalArgumentException | SecurityException e)
...
【讨论】:
【参考方案4】:如果存在异常层次结构,您可以使用基类来捕获异常的所有子类。在退化的情况下,您可以通过以下方式捕获 所有 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 )
...
【讨论】:
BTW catch 子句按顺序处理,因此如果您将父异常类放在子类之前,则它永远不会被调用,例如:try ... catch (Exception e) someCode(); catch (RepositoryException re) // 从未到达 其实正是因为它永远无法到达,所以这样的代码甚至无法编译。【参考方案5】:不,在 Java 7 之前,每个客户一个。
只要在所有情况下都采取相同的操作,就可以捕获超类,例如 java.lang.Exception。
try
// some code
catch(Exception e) //All exceptions are caught here as all are inheriting java.lang.Exception
e.printStackTrace();
但这可能不是最佳做法。只有当您有实际处理它的策略时,您才应该捕获异常 - 并且记录和重新抛出不是“处理它”。如果您没有纠正措施,最好将其添加到方法签名中,并让它冒泡给可以处理这种情况的人。
使用 JDK 7 及更高版本,您可以这样做:
try
...
catch (IllegalArgumentException | SecurityException | IllegalAccessException | NoSuchFieldException e)
someCode();
【讨论】:
我可以请你改写关于捕获 java.lang.Exception 的部分吗?我意识到这是一个例子,但我觉得有些人可能会读到这个答案并说,“哦,好吧,我会抓住异常然后”,而这可能不是他们想要(或应该)做的。跨度> 我知道这一点,但我不想这样做...哦,好吧,我猜我会遇到 4 个问题,直到 Java 的下一个版本...跨度> @duffymo:记录和重新抛出有什么问题?除了它使代码混乱之外,它相当于不捕获它,不是吗。从一般错误处理策略的角度来看。不好的是记录和不重新抛出。 我不考虑记录和重新处理任何事情。我更愿意让它冒泡给可以做一些有意义的事情的人。在这种情况下,异常不应逃逸的最后一层(例如 Web 应用程序中的控制器)应该是记录错误的层。 只有我一个人觉得没有为我自动生成日志是很荒谬的吗?似乎每次一段代码可能抛出异常时,我们都必须编写同样愚蠢的日志消息。【参考方案6】:user454322 在 Java 6(即 android)上的答案更简洁(但不那么冗长,也许不是首选)替代方法是捕获所有 Exception
s 并重新抛出 RuntimeException
s。如果您计划在堆栈中进一步捕获其他类型的异常(除非您也重新抛出它们),这将不起作用,但会有效地捕获所有已检查异常。
例如:
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 块之后执行一些代码。
【讨论】:
这种方法可以防止编译器确定“catch 块”是否可达。【参考方案7】:在 pre-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.
【讨论】:
应该是一个不错的解决方案。将caught
的最终控制权插入finally
块中如何?
这需要比原始问题更多的行。【参考方案8】:
很简单:
try
// Your code here.
catch (IllegalArgumentException | SecurityException | IllegalAccessException |
NoSuchFieldException e)
// Handle exception here.
【讨论】:
【参考方案9】:对于 kotlin,目前还不能,但他们已经考虑添加它:Source 但现在,只是一个小技巧:
try
// code
catch(ex:Exception)
when(ex)
is SomeException,
is AnotherException ->
// handle
else -> throw ex
【讨论】:
【参考方案10】:在异常层次结构中捕获恰好是父类的异常。 This is of course, bad practice。在您的情况下,常见的父异常恰好是 Exception 类,并且捕获作为 Exception 实例的任何异常确实是不好的做法 - NullPointerException 之类的异常通常是编程错误,通常应该通过检查空值来解决。
【讨论】:
以上是关于我可以在同一个 catch 子句中捕获多个 Java 异常吗?的主要内容,如果未能解决你的问题,请参考以下文章
在我的 C# 代码中显示错误“前一个 catch 子句已经捕获了这个或超类型 `System.Exception' 的所有异常”