在Java SE 7中,你可以使用单个catch
语句块处理一种或多种类型的异常,并以改进的异常类型检查方式来重新抛出异常。
我们先来看一段代码:
catch (IOException ex) { logger.log(ex); throw ex; catch (SQLException ex) { logger.log(ex); throw ex; }
在Java SE 7发布之前,由于变量ex
存在不同的类型,因此想要创建一个公共方法来清除重复的代码是非常困难的。不过从Java SE 7版本开始,你可以编写如下代码来去除重复的代码:
//多个异常类型之间用"|"隔开 catch (IOException|SQLException ex) { logger.log(ex); throw ex; }
注意:如果一个catch
语句块处理的异常类型超过1个,那就隐式地表示被catch
的参数变量(例如上面的ex)是一个final
的变量,你不能在catch
语句块内对其重新赋值。
使用单个catch
语句块处理多种异常类型比使用多个catch
语句块,每个语句块只处理一种类型的异常所编译生成的字节码更小,因此也更好。一个处理多个异常类型的catch
语句块在被Java编译器编译时并不会生成重复的字节码,字节码中也没有重复的异常处理程序。
与以前版本相比,Java SE 7 的编译器能够对再次抛出的异常(rethrown exception)做出更精确的分析。这使得你可以在一个方法声明的throws
从句中指定更具体的异常类型。我们先来看下面的一个例子:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
这个例子中的try
语句块可能会抛出FirstException或者SecondException类型的异常。设想一下,你想在rethrowException方法声明的throws
从句中指定这些异常类型。在Java SE 7之前的版本,你无法做到。因为在catch
子句中的异常参数e
是java.lang.Exception类型的,catch
子句对外抛出异常参数e
,你只能在rethrowException方法声明的throws
从句中指定抛出的异常类型为java.lang.Exception (或其父类java.lang.Throwable)。
不过,在Java SE 7中,你可以在rethrowException方法声明的throws
从句中指定抛出的异常类型为FirstException和SecondException。Java SE 7的编译器能够判定这个被throw
语句抛出的异常参数e
肯定是来自于try
子句,而try子句只会抛出FirstException或SecondException类型的异常。尽管catch
子句的异常参数e
是java.lang.Exception类型,但是编译器可以判断出它是FirstException或SecondException类型的一个实例:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
不过,如果catch
捕获的异常变量在catch
子句中被重新赋值,那么异常类型检查的分析将不会启用,因此在这种情况下,你不得不在方法声明的throws
从句中指定异常类型为java.lang.Exception。
更具体地说,从Java SE 7开始,当你在单个catch
子句中声明一种或多种类型的异常,并且重新抛出这些被捕获的异常时,需符合下列条件,编译器才会对再次抛出的异常进行类型验证:
try
子句会抛出该异常。在此之前,没有其他的
catch
子句捕获该异常。该异常类型是
catch
子句捕获的多个异常中的一个异常类型的父类或子类。