NO.9 异常: 真诚终归敌不过finally套路 | Java敲黑板系列
开场白
老铁:finally语句特性是Java语言区别于其他语言很重要的一个功能。该特性有一个非常重要的特点就是:无论异常是否发生,finally子句的代码总会被执行。如果能够合理利用该特性,那么将会给程序员带来无比畅快的体验;但是不合理应用,将会让我们跌进一个个的问题坑。为此,今天我们对finally兄弟进行一场专访,有请!
使用finally避免内存泄漏
What:我们在前面的专访(老铁可移步:NO.6 异常: 初识 )中提到过:“由于Java支持垃圾回收机制,为此,如果函数正常结束或异常而*退出,该函数所创建的所有对象会被自动回收,但是对于Socket或File资源必须要执行清理操作,以便资源回收”。如果不对这些资源进行回收,那么就会出现内存泄漏。
How:当时只是说明需要对Socket或File资源进行清理操作,但是并未说明如何进行资源回收。代码1展示了如何在典型业务场景中进行资源回收,避免内存泄漏。
public void reclaimResource() throws IOException{
ServerSocket server = new ServerSocket(5566);
Socket socket = null;
try{
socket = server.accept();
//业务代码.....
}finally{
if(null != socket)
socket.close();//回收资源
if(null != server)
server.close();//回收资源
}
}
}
通过上述代码,无论是否发生异常,finally子句都会得到执行,进而确保socket资源能被回收。
敲黑板
- finally子句不能独立存在,必须要配合try子句或try/catch子句来使用。
- 只要finally子句存在,无论异常是否会发生,它就一定会被执行。
不要在finally块中处理返回值
我们先从代码2说起:
public class FinallyTest {
//把一个字符串转换为整数
public static int getNumber(String str)
throws NumberFormatException {
try {
int number = Integer.parseInt(str);
return number; //1
} catch (NumberFormatException ex) {
throw ex; //2
} finally {
return -1; //3
}
}
//测试客户端
public static void main(String[] args) {
try {
int ret1 = getNumber("abc"); //4
int ret2 = getNumber("123"); //5
System.out.println("ret1=" + ret1 + ";ret2=" + ret2);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
请老铁们思考30秒,考虑一下代码2的结果输出。
答案揭晓: ret1=-1;ret2=-1
会不会觉得有点诧异?无论给测试客户端中传入的参数是什么,getNumber函数的返回值都将是-1,并且永远都不会在main函数中接收到异常。可见,这个结果暴露了两个问题,下面我们就这两个问题分别来进行分析:
问题1:覆盖了try子句中的返回值
当执行代码2中的//4语句时,在getNumber函数中会产生一个NumberFormatException的异常,catch块在捕捉到该异常后直接抛出,之后的代码执行到了finally代码块,就会重置返回值为-1。
当执行代码2中的//5语句时,在getNumber函数中不会产生异常,会继续执行try代码块中的//1语句,但是此时函数并不会马上返回,之后的代码会执行到finally代码块,仍重置返回值为-1。
至此,通过以上的分析,解释了返回值都为-1的现象。
问题2:屏蔽了异常
当执行代码2中的//4语句时,明明抛出了异常,为什么在main函数中却捕捉不到了?
在异常代码块中,代码中加上try代码块就标志JVM运行到该语句时会有一个Throwable线程监视该方法运行,若出现异常,则交由异常逻辑处理,如代码2中就会登记当前的异常类型为NumberFormatException;接着执行器会执行finally代码块,会重新给方法运行状态赋值,按照如上finally的业务代码,也就是告诉调用者“该方法执行正确,没有产生异常,返回值为1”。于是,异常就这样消失了。
敲黑板
不要在finally代码块中处理返回值。有可能会出现以下两种“诡异”现象:
- 覆盖了try子句中的返回值
- 屏蔽了异常
思考题
public class FinallyTest {
static class Color {
private String name;
public Color(String nm) {
this.name = nm;
}
public String getName() {
return this.name;
}
public void setName(String nm) {
this.name = nm;
}
}
public static Color getColor() {
Color color = new Color("Red");
try {
return color;
} catch (Exception ex) {
} finally {
color.setName("Black");
}
return color;
}
// 测试客户端
public static void main(String[] args) {
Color color = getColor();
System.out.println(color.getName());
}
}
请老铁们在留言区踊跃讨论并写下上述思考题的运行结果,答案将在明天的推文中公布。
转载自公众号:代码荣耀
上一篇: 折半查找法