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资源能被回收。

敲黑板

  1. finally子句不能独立存在,必须要配合try子句或try/catch子句来使用。
  2. 只要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代码块中处理返回值。有可能会出现以下两种“诡异”现象:

  1. 覆盖了try子句中的返回值
  2. 屏蔽了异常

思考题

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());
    }
}

请老铁们在留言区踊跃讨论并写下上述思考题的运行结果,答案将在明天的推文中公布。

转载自公众号:代码荣耀
图1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值