异常与资源管理(自动关闭资源细节)

程序中因错误而抛出异常时,原本的执行流程就会中断,抛出异常处之后的程序代码就不会被执行,如果程序开启了相关资源,使用完毕后应当关闭资源。若因错误而抛出异常,应当考虑是否还能正确地关闭资源。

1、使用 finally 如果创建 FileInputstream实例就会开启文档,不使用时,应该调用 close()关闭文档。 Fileutil中是通过 Scanner搭配 FileInputstream来读取文档,实际上 Scanner()对象有个 close()方法,可以关闭 Scanner相关资源与搭配的Fileinputstream。 例如:

package errorDemo;
import java.io.*;
import java.util.*;

public class FileUtil {
	public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		Scanner scan = new Scanner(new FileInputStream(name));
			while (scan.hasNext()) {
				txt.append(scan.nextLine()).append('\n');
			}
		scan.close();
		return txt.toString();
	}
}
复制代码

如果 scanner. close()前发生了任何异常,执行流程就会中断,因此 scanner. close()就可能不会执行,因此 Scanner搭配的 Fileinputstream?就不会被关闭。 你想要的是无论如何,最后一定要执行关闭资源的动作,try、 catch语法还可以搭配finally,无论try区块中有无发生异常,若撰写有 finally区块,则finally区块一定会被执行。例如:

package errorDemo;
import java.io.*;
import java.util.*;

public class FileUtil {
	public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		Scanner scan = null;
		try {
			scan = new Scanner(new FileInputStream(name));
			while (scan.hasNext()) {
				txt.append(scan.nextLine()).append('\n');
			}
		} finally {
			if(scan!=null) {
				scan.close();
			}
		}
		return txt.toString();
	}
}
复制代码

由于finaly区块一定会被执行,这个范例中 scanner原先是null,若 FileInputStream创建失败,则 scanner就有可能还是null,因此在 finally区块中必须先检查 scanner是否有参考对象,有的话才进一步调用 close()方法,否则scanner参考至null又打算调用close方法,反而会抛出 NullPointerException。 如果程序撰写的流程中先return了,而且也有finally区块,那 finally区块会先执行完后,再将值返回。例如,下面这个会先显示 ggg再显示1:

package errorDemo;

public class FinallyDemo {
	public static void main(String[] args) {
		System.out.println(test(true));
	}
	
	static int test(boolean flag) {
		try {
			if(flag) {
				return 1;
			}			
		} finally {
			// TODO: handle finally clause
			System.out.println("ggg");
		}
		return 0;
	}

}
复制代码

2、自动尝试关闭资源 在使用try、 finally尝试关闭资源时,会发现程序撰写的流程是类似的,就如先前 FileUtil,你会先检查 scanner是否为null,再调用close()方法关闭 Scanner。在JDK7之后,新增了尝试关闭资源 (try-with- Resources)语法

package errorDemo;

import java.io.*;
import java.util.*;

public class FileUtil2 {
	public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		try (Scanner scan = new Scanner(new FileInputStream(name))) {
			while (scan.hasNext()) {
				txt.append(scan.nextLine()).append('\n');
			}
		}
		return txt.toString();
	}
}

复制代码

想要尝试自动关闭资源的对象,是撰写在try之后的括号中,如果无须 catch处理任何异常,可以不用撰写,也不用撰写finally自行尝试关闭资源。JDK7的尝试关闭资源语法是编译程序蜜糖,尝试反编译:

...
public class FileUtil2 {
	public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		Scanner scan = new Scanner(new FileInputStream(name));
		Throwable localThrowable2 = null;
		try{
			while (scan.hasNext()) {
				txt.append(scan.nextLine()).append('\n');
			}
		}catch(Throwable LocalThrowable1){//捕捉所有错误
				localThrowable2 = LocalThrowable1;
				throw LocalThrowable1;
				}
		finally{
			if(scan!=null){//如果scan参考了Scanner实例
				if(localThrowable2!=null){//若前面catch到其他异常
					try{
						scan.close();//尝试关闭Scanner实例
					}catch(Throwable x2){//万一关闭时发生错误
						localThrowable2.addSuppressed(x2);//在原异常对象中记录
					}
				}else{
					scan.close();//若前面没有发生任何异常,直接关闭
				}
			}
		}
		return txt.toString();
	}
}

复制代码

若一个异常被 catch后的处理过程引发另一个异常,通常会抛出第一个异常作为响应, addSuppresse()方法是JDKT在java.lang.Throwable中新增的方法,可将第二个异常记录在第一个异常之中,JDK7中与之相对应的是 getSuppressed()方法,可返回 Throwable[],代表先前被addSuppressed()记录的各个异常对象。
使用自动尝试关闭资源语法时,也可以搭配 catch。如在发生FileNotFoundException 时显示堆栈追踪信息:

public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		try (Scanner scan = new Scanner(new FileInputStream(name))) {
			while (scan.hasNext()) {
				txt.append(scan.nextLine()).append('\n');
			}
		}catch(FileInputStream e){
			e.printStackTrace();
			throw e;
		}
		return txt.toString();
	}
复制代码

使用JAD反编译后可以看到,实际上前一个反编译程序片段中一部分,是产生在另一个try、 catch区块中:

...
public class FileUtil2 {
	public static String readFile(String name) throws FileNotFoundException {
		StringBuilder txt = new StringBuilder();
		try{
			Scanner scan = new Scanner(new FileInputStream(name));
			Throwable localThrowable2 = null;
			try {
				while (scan.hasNext()) {
					txt.append(scan.nextLine()).append('\n');
				}
			}catch(Throwable LocalThrowable1){
					localThrowable2 = LocalThrowable1;
					throw LocalThrowable1;
					}
			finally{
				if(scan!=null){
					if(localThrowable2!=null){
						try{
							scan.close();
						}catch(Throwable x2){
							localThrowable2.addSuppressed(x2);
						}
					}else{
						scan.close();
					}
				}
			}
		}catch(FileInputStream ex){
			ex.printStackTrace();
			throw ex;
		}
			return txt.toString();
	}
}
复制代码

使用自动尝试关闭资源语法时,并不影响你对特定异常的处理。自动尝试关闭资源语法仅协助你关闭资源,而非用于处理异常。从反编译的程序代码中也可以看到,使用尝试关闭资源语法时,不要试图自行撰写程序代码关闭资源,这样会造成重复调用close()方法。 3 、java. lang. AutoCloseable JDK7的尝试关闭资源语法可套用的对象,必须操作java.lang. AutoCloseable接口。它是JDK7新增的仅定义了close()的接口。 只要操作AutoCloseable接口就可以套用至尝试自动关闭资源语法:

package errorDemo;

public class AutoCloseableDemo {
	public static void main(String[] args) {
		try (Resource res=new Resource()){//括号内执行完自动关闭
			res.doSome();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Resource implements AutoCloseable{
	
	void doSome() {
		System.out.println("gggg");
	}
	
	@Override
	public void close() throws Exception {
		System.out.println("资源关闭");
		
	}
	
}
复制代码

执行结果:gggg 资源关闭 尝试自动关闭资源语法可以关闭两个以上对象资源,中间以分号分开:

package errorDemo;

public class AutoCloseableDemo {
	public static void main(String[] args) {
		try (Resource res = new Resource(); Resource2 res2 = new Resource2()) {//try区间执行完毕后统一关闭资源
			res.doSome();
			res2.doOther();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Resource implements AutoCloseable {
	void doSome() {
		System.out.println("some");
	}
	@Override
	public void close() throws Exception {
		System.out.println("资源关闭");
	}
}

class Resource2 implements AutoCloseable {
	void doOther() {
		System.out.println("other");
	}
	@Override
	public void close() throws Exception {
		System.out.println("资源2关闭");
	}
}
复制代码

执行结果:some other 资源2关闭 资源关闭 try括号中,越后面的对象资源越早被关闭。反编译发现每个AutoCloseable对象,都独立使用一个try、catch、finally,try括号越后面的对象会越在内层的try、catch、finally中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值