第六章:异常处理......................................................................................................................... 1
1.异常概述.................................................................................................................................... 1
1.1异常的引入....................................................................................................................... 1
1.2异常的概念....................................................................................................................... 2
2.异常分类.................................................................................................................................... 3
2.1异常体系.......................................................................................................................... 3
2.2 Throwable......................................................................................................................... 3
2.3 Error(错误)........................................................................................................................ 4
2.4 Exception(异常)................................................................................................................. 5
2.4.1运行时异常............................................................................................................. 5
2.4.2编译时异常............................................................................................................. 7
3.异常处理.................................................................................................................................... 7
3.1异常的产生过程解析......................................................................................................... 7
3.2抛出异常(throw)................................................................................................................ 8
3.3声明异常(throws)............................................................................................................... 9
3.4捕获异常(try-catch-finally)................................................................................................ 11
3.4.1try-catch组合方式................................................................................................... 11
3.4.2try-catch-finally组合方式........................................................................................ 13
3.4.3try-finally组合方式................................................................................................. 15
4.自定义异常............................................................................................................................... 16
4.1自定义异常格式.............................................................................................................. 16
4.2自定义异常使用.............................................................................................................. 16
5.异常技能补充........................................................................................................................... 17
5.1方法重写中的异常........................................................................................................... 17
5.2异常链............................................................................................................................ 18
现实生活中万物在发展和变化会出现各种各样不正常的现象。
例如:人的成长过程中会生病。
实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了等等。
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理,安全的退出,而不至于程序崩溃。
需求:根据索引获取数组中的元素值,我们需要考虑各种异常情况,伪代码如下:
【示例】根据索引获取数组中的元素值(仅限示意,不能运行)
public static int getValue(int[] arr, int index) { // 索引为负数的时候 if(index < 0) { System.out.println("索引不能为负数!!"); return ???; // 此处该返回什么呢? } // 索引大于等于数组长度的时候 if(index >= arr.length) { System.out.println("索引不能大于等于数组长度!!"); return ???; // 此处该返回什么呢? } // 正常返回元素值 return arr[index]; }
这种方式,有好几个坏处:
那么我们还如何应对以上的异常情况呢?其实JAVA给我们提供了处理异常的机制,就是当程序出现错误,程序安全退出的机制。
实际开发中,异常从面向对象的角度考虑也是一类事物,我们可以向上抽取为异常类。这个异常类可以对一些不正常的现象进行描述,并封装为对象。
我们开始看我们的第一个异常对象,并分析一下异常机制是如何工作的。
【示例】异常的分析案例
public class ExceptionTest { public static void main(String[] args) { test(); } public static void test() { int x = 3 / 0; System.out.println("x:" + x); } }
运行效果图
java是采用面向对象的方式来处理异常的。当程序出现问题时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置、原因等)。
JDK 中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。
Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。
先来看看java中异常的体系结构图解:
异常体系分类
Throwable类是所有异常或错误的超类,它有两个子类:Error和Exception,分别表示错误和异常。其中异常Exception又分为运行时异常(RuntimeException)和编译时异常。
Error和运行时异常,因为程序在编译时不检查异常,所以又称为不检查异常(Unchecked Exception)。
编译时异常,因为程序在编译时异常可以被检查出,所以又称为可检查异常(Checked Exception)
Throwable常用方法介绍:
方法名 | 描述 |
public String getMessage() | 返回此throwable的详细消息字符串。 |
public String toString() | 返回此 throwable 的简短描述 |
public void printStackTrace() | 打印异常的堆栈的跟踪信息 |
Error类是java所有错误类的父类,描述了java运行时系统内部错误和资源耗尽错误。这类错误是我们无法控制的,同时也是非常罕见的错误,表明系统JVM已经处于不可恢复的崩溃状态中,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。
所以错误是很难处理的,一般的开发人员(当然不是你)是无法处理这些错误的,我们在编程中,可以不去处理这类错误。
java.lang包中的Error类
以下是一些常见的Error案例:
【示例】内存溢出案例
public class ExceptionDemo { public static void main(String[] args) { // 数组需要1G的内存,这样子就会造成内存溢出 int[] but = new int[1024*1024*1024]; // 1K-->1M-->1G System.out.println(but); } }
内存溢出,需要的内存超出了java虚拟机管理的内存范围
【示例】栈溢出案例
public class ExceptionDemo { public static void main(String[] args) { test(); } public static void test() { test(); // 一直递归调用,这样子就会造成堆栈溢出 } }
栈溢出,递归调用的层次太深而导致栈溢出时抛出该错误
Exception类所有异常类的父类,其子类对应了各种各样可能出现的异常事件。Error是程序无法处理的错误,但是Exception是程序本身可以处理的异常,在程序中应当尽可能去处理这些异常。
Error与Exception的区别:
发动机什么时候坏?我们普通司机能管吗?不能。发动机什么时候坏是汽车厂发动机制造商的事。 |
Exception又分为两大类:
1、RuntimeException运行时异常
2、CheckedException编译时异常
RuntimeException 和 他 的 所 有 子 类 异 常,都 属 于 运 行 时 期 异 常 。例如:NullPointerException、ClassCastException、IndexOutOfBoundsException、ArithmeticException等。因为程序编译时异常不能被检查出,所以又称为不检查异常(UnCheckedException)。
运行时异常一般是由程序逻辑错误引起的,所以在编写程序时,我们应该从逻辑角度尽可能避免这类异常的发生。
当出现RuntimeException的时候,系统将自动检测并将它们交给缺省的异常处理程序(虚拟机接管并处理),用户可以不必对其处理。比如:我们从来没有人去处理过NullPointerException异常,它就是运行时异常,并且这种异常还是最常见的异常之一。
ArithmeticException异常,异常算术条件时抛出。例如:“除以零”的整数会抛出此类的一个实例。
【示例】ArithmeticException异常案例
public static void main(String[] args) { int x = 0; // 此处对x变量加判断,从而避免ArithmeticException异常 int y = 3 / x; System.out.println("y:" + y); }
ArithmeticException异常
NullPointerException异常(空指针异常),当程序访问一个空对象的成员变量或方法,访问一个空数组的成员时发生。
【示例】NullPointerException异常案例
public static void main(String[] args) { int[] arr = null; // 此处判断arr是否为null,从而避免NullPointerException异常 System.out.println(arr.length); }
NullPointerException异常
ClassCastException异常,当对象转换为不属于实例的子类时发生。
【示例】ClassCastException异常案例
// 父类 class Animal {} // 子类 class Dog extends Animal {} class Cat extends Animal {} // 测试类 public class RuntimeDemo { public static void main(String[] args) { Animal animal = new Dog(); // 转型之前先用instanceof判断类型,从而避免ClassCastException异常 Cat cat = (Cat)animal; } }
ClassCastException异常
ArrayIndexOutOfBoundsException异常,当使用非法索引访问数组时发生, 索引为负数或大于或等于数组的大小。
【示例】ArrayIndexOutOfBoundsException异常案例
public static void main(String[] args) { int[] arr = new int[3]; // 获取数组元素之前,先对数组索引判断 System.out.println(arr[-1]); }
ArrayIndexOutOfBoundsException异常
Exception及其子类(不包含运行异常),统称为编译时异常。因为程序编译时异常可以被检查出,所以又称为可检查异常(CheckedException)。
常见的编译时异常:如IOException、SQLException等以及用户继承于Exception的自定义异常。
对于编译时异常,在编译时就强制要求我们必需对出现的这些异常进行处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,都只能自己去处理可能出现的异常。
【示例】编译时异常案例
CheckedException必须处理,否则无法通过编译
当运行下面的程序时,程序会产生一个数组索引越界异常 ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。
【示例】异常的产生过程案例
// 工具类 class ArraysTool { // 根据索引获取数组中对应元素的值 public static int getValue(int[] arr, int index) { int value = arr[index]; return value; } } // 测试类 public class ExceptionDemo { public static void main(String[] args) { int[] arr = {1, 2, 3}; int value = ArraysTool.getValue(arr, 3); System.out.println(value); } }
上述程序执行过程图解:
在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。
在 java 中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常
具体如何操作呢?
1、创建一个异常对象,并封装一些提示信息(信息可以自己编写)。
2、需要将这个异常对象告知给调用者,通过关键字throw 就可以完成。
throw 用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
抛出异常格式:throw new 异常类名(参数列表);
【示例】throw 抛出异常案例
// 工具类 class ArraysTool { // 根据索引获取数组中对应元素的值 public static int getValue(int[] arr, int index) { // 判断索引是否合法 if(index < 0 || index >= arr.length) { // 当索引不合法时,抛出索引不合法异常。 // 这时就会结束当前方法的执行,并将异常告知给调用者 throw new ArrayIndexOutOfBoundsException("索引不合法"); } int value = arr[index]; return value; } } // 测试类 public class ExceptionDemo { public static void main(String[] args) { int[] arr = {1, 2, 3}; // 调用方法,获取数组中指定索引处元素 int value = ArraysTool.getValue(arr, 3); System.out.println(value); } }
如果以上代码的索引不合法,那么就会抛出以下的异常!
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 索引不合法 at com.bjsxt.exceprion.ArraysTool.getValue(ExceptionDemo.java:10) at com.bjsxt.exceprion.ExceptionDemo.main(ExceptionDemo.java:20) |
声明:将问题标识出来,报告给调用者。如果方法内通过throw 抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws关键字进行声明,让调用者去处理。
声明异常格式:修饰符 返回值类型 方法名(参数列表) throws 异常类名1, 异常类名2 … { }
【示例】throws 声明异常案例
//工具类 class ArraysTool { // 使用throws关键字声明异常,把可能发生的异常报告给调用者。 public static int getValue(int[] arr, int index) throws ArrayIndexOutOfBoundsException { // 判断索引是否合法 if(index < 0 || index >= arr.length) throw new ArrayIndexOutOfBoundsException("索引不合法"); // 返回元素值 return arr[index]; } }
throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
【示例】throws 声明多个异常案例
//工具类 class ArraysTool { // 使用throws关键字声明异常,把可能发生的异常报告给调用者。 public static int getValue(int[] arr, int index) throws NullPointerException, ArrayIndexOutOfBoundsException { // 判断arr是否为null if(null == arr) throw new NullPointerException("对象arr为null"); // 判断索引是否合法 if(index < 0 || index >= arr.length) throw new ArrayIndexOutOfBoundsException("索引不合法"); // 返回元素值 return arr[index]; } }
当程序中出现运行时异常时,方法定义中无需 throws 声明该异常,调用者也无需捕获处理该异常(即没有try-catch),系统会把异常一直默认往上层抛,一直抛到最上层。
当程序中出现非运行时异常时,要么用try-catch语句捕获它,要么用throws语句声明抛出它,否则编译不会通过。
【示例】throws 声明非运行时异常案例
// Demo类 class Demo { /* * 因为NullPointerException是运行时异常,可以不用在方法上使用throws声明 * 而FileNotFoundException是非运行时异常,此处应该在方法上使用throws声明,否则编译不通过 */ public void test(Object obj, String path) throws FileNotFoundException { // 判断obj是否为null if(null == obj) throw new NullPointerException("obj不能为null"); // 创建文件字节读取流对象,如果文件地址不存在会抛出FileNotFoundException异常 FileInputStream is = new FileInputStream(path); } } // 测试类 public class ThrowsExceptionDemo { /* * main()方法中没有对FileNotFoundException异常进行捕获处理,那么应该继续在main()方法上抛出,最后交给JVM来做处理 */ public static void main(String[] args) throws FileNotFoundException { // 调用Demo类中的test()方法 new Demo().test(new Object(), "D://abc.txt"); } }
如果程序出现了异常,自己又解决不了,那么可以把异常声明出来(throws),报告给调用者,让调用者来做处理。
如果出现的异常自己能解决,那么就不用声明异常,而是自己捕获该异常(try-catch-finally),并在catch块中对该异常做处理。
一个try语句必须带有至少一个catch语句块或一个finally语句块 。
try-catch 组合:对代码进行异常检测,并对检测到的异常传递给 catch 处理。
捕获异常格式:
try { // 需要被检测的语句。 } catch(异常类 变量) { // 参数 // 异常的处理语句。 }
try:该代码块中编写可能产生异常的代码,该段代码就是一次捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该段中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。catch中的参数就是被捕获到的异常对象(也就是被抛出的那个异常对象),我们可以根据这个异常对象来获取到该异常的具体信息。
【示例】try-catch捕获异常案例
// 工具类 class ArraysTool { // ArrayIndexOutOfBoundsException属于运行时异常,可以不用强制throws声明 public static int getValue(int[] arr, int index) { // 判断索引是否合法 if(index < 0 || index >= arr.length) { throw new ArrayIndexOutOfBoundsException("索引不合法"); } // 返回元素值 return arr[index]; } } // 测试类 public class TryCatchDemo { public static void main(String[] args) { int[] arr = {1, 2, 3, 4}; try { // 获取元素的值,可能会抛出异常对象 // 如果发生了异常,那么就会结束当前当前代码块的执行 int value = ArraysTool.getValue(arr, 5); System.out.println("value:" + value); // 不执行 } catch(ArrayIndexOutOfBoundsException e) { // 参数被抛出的异常对象 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 打印异常对象,该对象重写了toString()方法 System.out.println("exception:" + e); // 输出栈异常 e.printStackTrace(); } // 无论是否发生异常,该代码都执行 System.out.println("over"); // 执行 } }
一个 try 多个 catch 组合 : 对代码进行异常检测,并对检测的异常传递给 catch 处理。对每种异常信息进行不同的捕获处理。
// 工具类 class ArraysTool { public static int getValue(int[] arr, int index) throws Exception { // 判断arr是否为null if(null == arr) throw new NullPointerException("对象arr不能为null"); // 判断索引是否大于等于arr.length if(index >= arr.length) throw new ArrayIndexOutOfBoundsException("索引不能大于等于数组长度"); // 判断索引是否小于0 if(index < 0) throw new Exception("索引不能小于0"); // 返回元素值 return arr[index]; } } // 测试类 public class TryCatchDemo { public static void main(String[] args) { int[] arr = {1, 2, 3, 4}; try { // 获取元素的值,可能会抛出异常对象 int value = ArraysTool.getValue(arr, -5); System.out.println("value:" + value); // 不执行 } catch (NullPointerException e) { // 空指针异常 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出栈异常 e.printStackTrace(); } catch (ArrayIndexOutOfBoundsException e) {// 索引大于等于数组长度异常 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出栈异常 e.printStackTrace(); } // 多catch的时候,父类异常的catch应该放在最下面!!! catch (Exception e) { // 索引小于0异常 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出栈异常 e.printStackTrace(); } // 无论是否发生异常,该代码都执行 System.out.println("over"); // 执行 } }
注意:这种异常处理方式,要求多个 catch 中的异常不能相同,并且若 catch 中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的 catch 处理,父类异常在下面的 catch 处理。
try-catch-finally组合:检测异常,并传递给 catch 处理,并在 finally 中进行资源释放。
捕获异常格式:
try { // 需要被检测的语句。 } catch(异常类 变量) { // 参数 // 异常的处理语句。 } finally { // 一定会被执行的语句,用于释放资源。 }
try:该代码块中编写可能产生异常的代码,该段代码就是一次捕获并处理的范围。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理,可以一个key对应多个catch。
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而 finally 就是解决这个问题的,在 finally 代码块中存放的代码都是一定会被执行的。
【示例】try-catch-finally捕获异常案例
// 工具类 class ArraysTool { public static int getValue(int[] arr, int index) { // 判断arr是否为null if(arr == null) throw new NullPointerException("对象arr不能为null"); // 判断索引是否合法 if(index < 0 || index >= arr.length) throw new ArrayIndexOutOfBoundsException("索引不合法"); // 返回元素值 return arr[index]; } } // 测试类 public class TryCatchDemo { public static void main(String[] args) { int[] arr = {1, 2, 3, 4}; try { // 获取元素的值,可能会抛出异常对象 int value = ArraysTool.getValue(arr, -2); System.out.println("value:" + value); // 不执行 } catch (NullPointerException e) { // 空指针异常 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出栈异常 e.printStackTrace(); } catch (ArrayIndexOutOfBoundsException e) { // 索引不合法异常 // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出栈异常 e.printStackTrace(); } finally { // 无论异常是否发生,都需要执行finally中的代码。 // finally块中的代码一般用于释放资源。 System.out.println("执行finally"); } // 无论是否发生异常,该代码都执行 System.out.println("over"); // 执行 } }
注意:即使在try或catch中添加return,finally中的代码都会执行,除非调用System.exit(0);做退出虚拟机的操作,这样finally中的代码才不会执行。
【示例】在try或catch中添加return案例
public static void main(String[] args) { int[] arr = {1, 2, 3, 4}; try { // 获取元素的值,可能会抛出异常对象 int value = arr[4]; System.out.println("value:" + value); return; // 添加return; } catch (ArrayIndexOutOfBoundsException e) { return; // 添加return; } finally { // 在try或catch中添加return操作,finally块中的代码依旧会执行 System.out.println("执行finally"); // 执行 } }
【示例】在try或catch中添加System.exit(0);案例
public static void main(String[] args) { int[] arr = {1, 2, 3, 4}; try { // 获取元素的值,可能会抛出异常对象 int value = arr[3]; System.out.println("value:" + value); System.exit(0); // 退出虚拟机操作 } catch (ArrayIndexOutOfBoundsException e) { System.exit(0); // 退出虚拟机操作 } finally { // 在try或catch中添加System.exit(0);操作,finally块中的代码才不会执行 System.out.println("不执行finally"); // 不执行 } }
try-finally 组合: 对代码进行异常检测,检测到异常后因为没有catch,所以一样会被默认 jvm 抛出。异常是没有捕获处理的。但是功能所开启资源需要进行关闭,所有finally只为关闭资源。
捕获异常格式:
try { // 需要被检测的语句。 } finally { // 一定会被执行的语句,用于释放资源。 }
【示例】try-finally组合案例
// 抛出Exception异常 public static void main(String[] args) throws Exception { try { // 非运行时异常,如果没有用catch捕获,那么应该在方法上声明异常 throw new Exception("自定义异常"); } finally { // 关闭资源操作。。。 } }
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类。
java中所有的异常类,都是继承Throwable,或者继承Throwable的子类。这样该异常才可以被throw抛出,因为异常体系具备一个可抛性,即可以使用throw 关键字。
自定义的类应该包含两个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
自定义异常格式:
class 自定义异常名 extends Exception或RuntimeException { public 自定义异常名() { // 默认调用父类无参构造方法 } public 自定义异常名(String msg) { // 调用父类具有异常信息的构造方法 super(msg); } }
在 Person 类的有参数构造方法中,进行年龄范围的判断,若年龄为负数或大于130岁,则抛出
AgeOutOfBoundsException异常。
【示例】自定义异常使用案例
// 自定义异常类 class AgeOutOfBoundsException extends Exception { // 无参构造方法 public AgeOutOfBoundsException() {} // 有参构造方法 public AgeOutOfBoundsException(String msg) { super(msg); } } // Person类 class Person { int age; public Person(int age) throws AgeOutOfBoundsException { // 判断年龄是否合法 if(age < 0 || age >= 130) { throw new AgeOutOfBoundsException("年龄数值非法异常"); } // 进行属性赋值 this.age = age; } } // 测试类 public class MyException { public static void main(String[] args) { try { // 初始化对象,可能会抛出AgeOutOfBoundsException异常 Person p = new Person(140); System.out.println("age:" + p.age); } catch (AgeOutOfBoundsException e) { // 获取异常信息 System.out.println("msg:" + e.getMessage()); // 输出堆栈异常 e.printStackTrace(); } } }
自定义异常继承于Exception或继承RuntimeException的区别?
原则:子类声明的异常范围不能超过父类声明范围。
class Parent { public void show(){} } class Child extends Parent { public void show() throws Exception {} // 编译错误 }
class AAException extends Exception {} class BBException extends AAException {} class Parent { public void show() throws AAException {} } class Child extends Parent { // public void show() {} // 不声明异常,编译通过 // 声明父类方法异常的父类,编译错误 // public void show() throws Exception {} // 声明与父类方法相同的异常或该异常的子类,编译通过 // public void show() throws AAException {}// 编译通过 public void show() throws BBException {}// 编译通过 // 子类声明的异常可以有多个,但是都必须是父类方法相同的异常或该异常的子类 public void show() throws BBException, AAException {}// 编译通过 }
异常需要封装,但是仅仅封装还是不够的,还需要传递异常。一个系统的友好型的标识,友好的界面功能是一方面,另一方面就是系统出现非预期的情况的处理方式了。
有时候我们会捕获一个异常后再抛出另一个异常,这就形成了一个异常链。
【示例】链案例
// 自定义异常 class DenominatorZeroException extends Exception { public DenominatorZeroException() {} public DenominatorZeroException(String msg) { super(msg); } } // 计算器类 class Calculator { // 除法运算 public static int division(int num, int den) throws DenominatorZeroException { int result = 0; try { result = num/den; } catch(ArithmeticException e) { // 抛出一个更详细的异常 throw new DenominatorZeroException("分母不能为0"); } return result; } } public class ExceptionTest07 { public static void main(String[] args) { try { // 两个数做除法运算,可以会发生算数异常 Calculator.division(5, 0); } catch (DenominatorZeroException e) { e.printStackTrace(); } } }
众所周知,所有被打开的系统资源,比如流、文件、Socket连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重大的生产事故。
在JDK1.7以前,我们想要关闭资源就必须的finally代码块中完成。
【示例】JDK7之前资源的关闭的方式
/** * 测试类 */ public class Test { public static void main(String[] args) throws IOException { FileInputStream inputStream = null; try { // 创建字节输入流,注意:“file.txt”文件在项目中不存在,则会抛出异常 inputStream = new FileInputStream("file.txt"); // 执行存储数据的操作,此处省略。。。 } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (inputStream != null) { // 关闭流 inputStream.close(); } } } }
JDK7及以后关闭资源的正确姿势:try-with-resource,该语法格式:
try(/*需要关闭的资源*/){ // 容易出现异常的代码 }catch(Exception e) { // 处理异常 }
Resource的定义:所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。
【示例】实现对try-with-resource的验证
/** * 资源类 */ class Resource implements AutoCloseable { public void sayHello() { System.out.println("hello"); } @Override public void close() throws Exception { System.out.println("Resource is closed"); } } /** * 测试类 */ public class Test{ public static void main(String[] args) { try(Resource resource = new Resource()) { resource.sayHello(); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
【示例】当存在多个打开资源的时候
/** * 资源类 */ class Resource1 implements AutoCloseable { public void sayHello() { System.out.println("Resource1 hello"); } @Override public void close() throws Exception { System.out.println("Resource is closed"); } } class Resource2 implements AutoCloseable { public void sayHello() { System.out.println("Resource2 hello"); } @Override public void close() throws Exception { System.out.println("Resource is closed"); } } /** * 测试类 */ public class Test{ public static void main(String[] args) { try(Resource1 r1 = new Resource1(); Resource2 r2 = new Resource2()) { r1.sayHello(); r2.sayHello(); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
通过try-with-resource来释放资源,即使资源很多,代码也可以写的很简洁,如果用JDK1.7之前的方式去关闭资源,那么资源越多,用fianlly关闭资源时嵌套也就越多。