谈谈Java的try..catch...

咱们在写Java的try..catch的时候,每每须要在最后加上finally子句关闭一些IO资源,好比java

?函数

1
2
3
4
5
6
7
8
9
10
11
12
InputStream is;
try {
     is=openInputStream();
     // do something
} catch (IOException e){
     e.printStaceTrace(e);
} finally {
     try {
         is.close();
     } catch (IOException e ){
     }
}
   

但 是在使用这种模式时,即便是Java老手,偶尔也会犯一些错误。好比上面这段代码,当openInputStream()函数在执行过程当中抛出异常,那么 变量is的值仍为null,此时执行is.close()会抛出NullPointerException. 因为NullPoiterException不是IOException的子类,所以它不能被catch块捕获,而是直接往调用层抛出去. 一种改进的写法就是在关闭流的时候先进行非空判断,但这样代码会显得很啰嗦。我的认为比较优雅的写法是直接调用commons-io包提供的 IOUtils.closeQuitely()方法关闭流(或者本身封装一个closeQuitely()方法)。ui

使用这种写法还有一种好处,就是当遇到关闭多个IO资源时不容易出错,好比下面这段代码: spa

?.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
InputStream is;
OutputStream os ;
try {
     is=openInputStream();
     // do something
} catch (IOException e){
     e.printStaceTrace(e);
} finally {
     try {
         if  (is !=  null  )  is.close();
         if  (os !=  null  )  os.close();
     } catch (IOException e ){
     }
}
   

当is.close()发生错误的时候,os.close()就没法被执行,从而致使os所引用的资源没有被释放。 code

也许Oracle也以为这种try .. catch ... finally的样板代码太不必,所以在JDK 7中对try 子句进行了一些改造,免去编写一些手动关闭资源的代码,让代码看起来更紧凑更简洁。好比上面的代码在JDK 7下能够改为:orm


1
2
3
4
5
6
7
8
try (
  InputStream is = openInputStream();
  OutputStream os = openOutStream();
){
   // do something 
} catch (IOException e){
     e.printStaceTrace(e);
}
   

Oracle 把这里的try(..)语句叫作try-with-resource语句。须要注意的是,try(.. )中变量所引用的对象都必须是实现了java.io.AutoClosable接口的实例,当退出try ..catch块时,JDK会自动调用close()方法。 也就是说,try-with-resource语句中的resource(资源)不只限于IO资源。 对象

 

这里有必要对try-with-resource语句的一些细节进行补充说明: blog

  • JDK会确保全部资源的close()方法被调用,无论close()方法是否抛出异常, 而调用的顺序和资源声明的顺序相反接口

  • try-with-resource语句中全部抛出的异常都会被捕获。若是多个异常被抛出,后面所抛出的异常会被suppress(抑 制)在前一个异常中,catch块最终只拿到最早抛出的那个异常。能够依次经过调用Throwable类定义的getSuppressed()得到被 suppressed(抑制)的异常

仍是上面那个例子,

  • 当退出try .. catch.块的时候,JDK会先调用os.close(),而后是is.close(), 若是两次close()都抛出IOException, 那么is.close()所抛出的异常会被suppress(抑制)在os.close()所抛出的异常中,最终catch块只捕获到 os.close()所抛出的异常。能够经过getSuppressed()方法拿到is.close()所抛出的异常。

  • 若是调用openInputStream()的时候就发生IOException,那么openOutputStream()就不会被调 用,os.close()和is.close()也不会被调用, catch块捕捉到 调用openInputStream()时所抛出的异常。 

  • 若是调用openOutputStream()发生IOException(用记号 e1表示), 那么is.close()仍是会被调用,  若是此时is.close()又抛出IOException(用记号 e2表示),那么e2会被suppress到e1中,而catch块捕捉到的异常是 e1. 

 

除了对try块作了改造,JDK 7还对catch部分进行了简化,容许把多个catch子句合并。 好比: 

?

1
2
3
4
5
6
7
8
try (
  InputStream is = openInputStream();
  OutputStream os = openOutStream();
){
   // do something   
} catch (IOException  | XMLParseException | XPathException e){
     e.printStaceTrace(e);
}
   

 

此外,当你从新抛出多个异常时,再也不须要详细定义异常类型了,编译器已经知道你具体抛出的是哪一个异常了你只需在方法定义的时候声明须要抛出的异常便可。好比

?

1
2
3
4
5
6
7
8
// 虽然这里用Exception匹配抛出的IOException,到编译器知道实际上抛给上层的异常是IOException
         public  void  doIO()  throws  IOException {
             try {
                 throw  new  IOException();
             } catch (Exception e){
                 throw  e;
             }
         }
   

PS : 这个特性我想不到会带来什么好处

 

JDK 7还有其余有趣的语法新特性,好比二进制字面量,用下划线分割长数字,泛型参数的类型推断,switch支持字符串匹配等等。 如今JDK 8又引入了一些有用的特性。在不须要考虑向后兼容的前提下, 适当并灵活运用一些语法特性,可让咱们的代码在必定程度上显得更清晰,更简洁。

相关文章
相关标签/搜索