异常
jdk1.7对try--catch--finally的异常处理模式进行了增强,下面我们依次来看增强的方面。
1. 为了防止异常覆盖,给Throwable类增加了addSuppressed方法,可以将一个异常信息追加到另一个异常信息之后:
1 /** 2 * 这是第一种防止前面异常被覆盖的方法,通过在finally块中判断前面是否有异常抛出 3 * 如果有则最终抛出的异常为原来的异常,没有则最终抛出的异常为finally块中的异常。 4 * 此时只能抛出一种异常信息。 5 * @param fileName 6 */ 7 private void readFile(String fileName) { 8 FileInputStream input = null; 9 IOException readException = null; 10 try { 11 input = new FileInputStream(fileName); 12 } catch (IOException ex) { 13 readException = ex; 14 } finally { 15 if (input != null) { 16 try { 17 input.close(); 18 } catch (IOException e) { 19 // 如果前面没有出现异常,则说明整个异常是此处产生的 20 if (readException == null) { 21 readException = e; 22 } 23 } 24 } 25 26 if (readException != null) { 27 throw new RuntimeException(readException); 28 } 29 } 30 } 31 32 /** 33 * 这是第二种防止异常被覆盖的方法,利用jdk7的新特性。通过在finally块的异常捕获代码中判断前面是否抛出异常,如果抛出异常 34 * 则将finally块中抛出的异常追加在前面的异常信息之后。这样同时可以抛出两种异常信息类型。 35 * @param fileName 36 */ 37 private void readFile2(String fileName) { 38 FileInputStream input = null; 39 IOException readException = null; 40 try { 41 input = new FileInputStream(fileName); 42 } catch (FileNotFoundException e) { 43 readException = e; 44 } finally { 45 if (input != null) { 46 try { 47 input.close(); 48 } catch (IOException e) { 49 // 如果前面抛出的异常不为空,这里将finally块中的异常信息添加到原异常信息后面 50 if (readException != null) { 51 readException.addSuppressed(e); 52 } else { 53 readException = e; 54 } 55 } 56 } 57 if (readException != null) { 58 throw new RuntimeException(readException); 59 } 60 } 61 }
2. catch块增强,可以同时捕获多个异常,来进行统一的处理:
1 /** 2 * 这里测试jdk7的新特性,一个catch语句中可以捕获多种异常,以 | 分割。 3 */ 4 private void catchMore() { 5 // 在jdk1.7新特性这本书中说一个catch块中可以同时捕获属于父子关系的异常(只要子在前父在后,同分开的catch块中的顺序),但实际上在jdk1.8中时不允许的。 6 try { 7 int a = Integer.valueOf("aaa"); 8 throw new IOException(); 9 } 10 /* 11 会报NumberFormatException已经被捕获 12 catch (NumberFormatException | RuntimeException | IOException e) { 13 14 }*/ catch (NumberFormatException | IOException e) { 15 16 } catch (RuntimeException e) { 17 } 18 }
3. throw语句增强,异常在第二次抛出之后,仍然能够准确的知道最原始的异常类型:
1 /** 2 * jdk1.7之后,即使异常被重新抛出,编译器也知道原始异常类型,而不会被再抛出的异常类型所干扰。 3 * 如果在jdk1.6或者之前的版本,第二个catch只能是ExceptionA,因为原始的ExceptionASub2被抹去了。 4 */ 5 private void testRecatch() { 6 try { 7 throw new ExceptionASub2(); 8 } catch (ExceptionA e) { 9 try { 10 throw e; 11 } catch (ExceptionASub2 e2) { // 如果是catch (ExceptionASub1 e2) 那么会报编译错误,因为编译器知道原始异常是ExceptionASub2 12 13 } 14 } 15 } 16 17 class ExceptionA extends Exception {} 18 class ExceptionASub1 extends ExceptionA {} 19 class ExceptionASub2 extends ExceptionA {}
4. try语句增强,try块可以进行资源管理:
1 /** 2 * jdk1.7之后,对try块进行了增强,使其中声明的资源总是可以正确的被释放,而不需要多余的finally块来单独处理。 3 * 这有点像python的 with open("a.txt") as file 语句一样。 4 * 需要注意的是,此时资源必须实现AutoCloseable接口,实际上jdk1.7中通过 5 * public interface Closeable extends AutoCloseable,将Closeable继承于AutoCloseable接口。 6 * 如果我们要自己实现资源的关闭,只需直接实现AutoCloseable接口即可。 7 */ 8 private void tryWithResource() { 9 String fileName = "a.txt"; 10 try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { 11 12 } catch (FileNotFoundException e) { 13 14 } catch (IOException e) { 15 16 } 17 }
变长参数
jdk1.7在变长参数和范型结合使用的时候,增加了一个@SafeVarargs。通过该注解来告诉编译器参数类型的安全性,以此来解决每次调用都出现编译警告的问题。
1 /** 2 * 在jdk1.7之前,需要使用@SuppressWarnings("unchecked")注解来给每一个调用该方法的地方取消警告 3 * 。这是因为变长参数的实际值时通过数组来传递的,而数组中传递的时不可具化的范型对象,自身存在类型安全问题,所以编译器 4 * 会给出警告。这在调用java.utils.Arrays.asList方法和java.util.Collections.addAll方法中也会遇到。 5 * jdk1.7中提供了在该方法声明的地方加上@SafeVarargs注解。来表示该方法在与范型结合使用的时候不会出现类型安全问题。 6 * 此时再调用该方法,编译器不会给出警告信息。 7 * 不过需要注意的是,该方法必须声明为static或者final方法,否则会出现编译错误。 8 */ 9 @SafeVarargs 10 public static<T> T useVarargs(T... args) { 11 return args.length > 0 ? args[0] : null; 12 }