究极异常处理逻辑——多层次异常的处理顺序

多层次异常的处理顺序

之前在牛客做笔试题的时候,由于遇到了一道多层次异常相关的题,所以对多层次异常的处理顺序格外留心,在上周工作增加并发锁的时候,由于是锁的嵌套,也就是 异常处理 嵌套 异常处理。于是决定深究一下多层次异常处理的优先级。

零、代码执行环境

IDEA版本:2022.1.3

JDK版本:jdk1.8.0_181

一、try、catch 和 finally 中 return 的优先级问题

首先,我们先来探究产生是否产出异常对 try、catch 和 finally 中 return 的优先级 的影响

测试代码:


public class Test {  
  
    public static void main(String[] args) {  
        System.out.println("产生异常时:a = " + test1());
        System.out.println("未产生异常时:a = " + test2());
    
  
    public static String test1() {  
        String res = "初始内容 ";  
        try {  
            int a = 1 / 0;  
            return res + " + try 返回了";  
        } catch (Exception e) {  
            res += " + catch 执行了";  
            return res + " + catch 返回了";  
        } finally {  
            res += " + finally 执行了";  
            return res + " + finally 返回了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
  
    public static String test2() {  
        String res = "初始内容 ";  
        try {  
//            int a = 1 / 0;  
            return res + " + try 返回了";  
        } catch (Exception e) {  
            res += " + catch 执行了";  
            return res + " + catch 返回了";  
        } finally {  
            res += " + finally 执行了";  
            return res + " + finally 返回了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }
}

运行结果:

产生异常时:初始内容  + catch 执行了 + finally 执行了 + finally 返回了
不产生异常时:初始内容  + finally 执行了 + finally 返回了

结果分析:

通过结果可以看出,无论是否产生异常,一旦 finally {} 语句块中含有 retuan 那么都会执行 finally {} 语句中的 retuan 语句。

二、try 和 catch 中 return 的优先级问题

测试代码:

  
  
    public static void main(String[] args) {  
        System.out.println("产生异常时:" + test1());  
        System.out.println("不产生异常时:" + test2());  
    }  
  
    public static String test1() {  
        String res = "初始内容 ";  
        try {  
//            int a = 1 / 0;  
            return res + " + try 返回了";  
        } catch (Exception e) {  
            res += " + catch 执行了";  
            return res + " + catch 返回了";  
        } finally {  
            System.out.println("finally 执行了");  
            res += " + finally 执行了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }
      
    public static String test2() {  
        String res = "初始内容 ";  
        try {  
            int a = 1 / 0;  
            return res + " + try 返回了";  
        } catch (Exception e) {  
            res += " + catch 执行了";  
            return res + " + catch 返回了";  
        } finally {  
            System.out.println("finally 执行了");  
            res += " + finally 执行了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
}

运行结果:

finally 执行了
不产生异常时:初始内容  + try 返回了
finally 执行了
产生异常时:初始内容  + catch 执行了 + catch 返回了

结果分析:

不产生异常时:执行 try {} 语句中的 retuan 语句。
产生异常时:执行 catch {} 语句中的 retuan 语句

值得注意的是: finally {} 语句中的语句也执行了,但是对于字符串 res 的操作没有生效。

如果是返回基本类型的值,那么在缓存时也是缓存值本身,所以后面在finally块中重新赋值时,方法返回的值不会受finally块中重新赋值的影响;

如果返回的是引用类型的值,那么在缓存时,缓存的是引用类型对象的引用,所以虽然后面在finally块中重新赋值时(重新指向另一个对象),方法返回的值不会受到影响,但是如果是修改对象的属性,那么会影响到返回的值。

参考链接:finally语句中对变量进行赋值的问题

三、finally 中 return 对 catch 中异常的影响


public class Test {  
  

    public static void main(String[] args) {  
        System.out.println("finally 有 return 时:" + test1());  
        System.out.println("finally 无 return 时:" + test2());  
    }  
    
	public static String test1() {  
	    String res = "初始内容 ";  
	    try {  
	        int a = 1 / 0;  
	        return res;  
	    } catch (Exception e) {  
	        int a = 1 / 0;  
	        res += " + catch 执行了";  
	        return res;  
	    } finally {  
	        res += " + finally 执行了";  
	        return res ;  
	    }  
	    // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
	}  
	  
	public static String test2() {  
	    String res = "初始内容 ";  
	    try {  
	        int a = 1 / 0;  
	        return res;  
	    } catch (Exception e) {  
	        int a = 1 / 0;  
	        res += " + catch 执行了";  
	        return res;  
	    } finally {  
	        res += " + finally 执行了";  
	    }  
	    // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
	}
  
}

运行结果:

finally 有 return 时:初始内容  + finally 执行了
Exception in thread "main" java.lang.ArithmeticException: / by zero

结果分析:

根据结果可以看出,如果 finally {} 代码块中有 retuan 时, catch {} 中的异常不会被处理。

四、多层次 try 、 catch 和 finally 中 return 的优先级问题


public class Test {  
  
  
    public static void main(String[] args) {  
        System.out.println("不产生异常时:" + test1());  
        System.out.println("内部 try 产生异常时:" + test2());  
        System.out.println("内部 catch 也产生异常时:" + test3());  
    }  
  
    public static String test1() {  
        String res = "初始内容";  
        try {  
            try {  
//                int a = 1 / 0;  
                return res;  
            } catch (Exception e) {  
                res += " + 内部代码块的 catch 执行了";  
                return res;  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
                return res + " + 内部 finally 返回了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res;  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
            return res + " + 外部 finally 返回了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
  
    public static String test2() {  
        String res = "初始内容";  
        try {  
            try {  
                int a = 1 / 0;  
                return res;  
            } catch (Exception e) {  
                res += " + 内部代码块的 catch 执行了";  
                return res;  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
                return res + " + 内部 finally 返回了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res;  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
            return res + " + 外部 finally 返回了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
  
    public static String test3() {  
        String res = "初始内容";  
        try {  
            try {  
                int a = 1 / 0;  
                return res;  
            } catch (Exception e) {  
                int a = 1 / 0;  
                res += " + 内部代码块的 catch 执行了";  
                return res;  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
                return res + " + 内部 finally 返回了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res;  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
            return res + " + 外部 finally 返回了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }
  
}

运行结果:

不产生异常时:初始内容 + 内部代码块的 finally 执行了 + 外部代码块的 finally 执行了 + 外部 finally 返回了
内部 try 产生异常时:初始内容 + 内部代码块的 catch 执行了 + 内部代码块的 finally 执行了 + 外部代码块的 finally 执行了 + 外部 finally 返回了
内部 catch 也产生异常时:初始内容 + 内部代码块的 finally 执行了 + 外部代码块的 finally 执行了 + 外部 finally 返回了

结果分析:

不产生异常时:正常执行 try {} 中语句,由于外部代码块 finally {} 中有 return 语句,所以只执行外部 finally {} 中的 return 语句。符合我们在 一、try 和 finally 中 return 的优先级问题 中的结论。

try 产生异常时:代码执行 catch {} 部分,由于外部代码块 finally {} 中有 return 语句,所以只执行外部 finally {} 中的 return 语句。符合我们在 一、try 和 finally 中 return 的优先级问题 中的结论。

try 和 catch 同时产生异常时:由于外部代码块 finally {} 中有 return 语句,所以程序不会处理 catch {} 中的异常,故结果和不产生异常时相同。符合我们在 三、finally 中 return 对 catch 中异常的影响 中的结论。

五、多层次 try 和 catch 中 return 的优先级问题

为了验证最后一个结论,我们在 finally {} 中去掉 return 语句,再次执行。

测试代码:


public class Test {  
  
  
    public static void main(String[] args) {  
        System.out.println("不产生异常时:" + test1());  
        System.out.println("内部 try 产生异常时:" + test2());  
        System.out.println("内部 catch 也产生异常时:" + test3());  
    }  
  
    public static String test1() {  
        String res = "初始内容";  
        try {  
            try {  
//                int a = 1 / 0;  
                return res + " + 内部代码块的 try 返回";  
            } catch (Exception e) {  
                res += " + 内部代码块的 catch 执行了";  
                return res + " + 内部代码块的 catch 返回";  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res + " + 外部代码块的 catch 返回";  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
  
    public static String test2() {  
        String res = "初始内容";  
        try {  
            try {  
                int a = 1 / 0;  
                return res + " + 内部代码块的 try 返回";  
            } catch (Exception e) {  
                res += " + 内部代码块的 catch 执行了";  
                return res + " + 内部代码块的 catch 返回";  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res + " + 外部代码块的 catch 返回";  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }  
  
    public static String test3() {  
        String res = "初始内容";  
        try {  
            try {  
                int a = 1 / 0;  
                return res + " + 内部代码块的 try 返回";  
            } catch (Exception e) {  
                int a = 1 / 0;  
                res += " + 内部代码块的 catch 执行了";  
                return res + " + 内部代码块的 catch 返回";  
            } finally {  
                res += " + 内部代码块的 finally 执行了";  
            }  
            // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
        } catch (Exception e) {  
            res += " + 外部代码块的 catch 执行了";  
            return res + " + 外部代码块的 catch 返回";  
        } finally {  
            res += " + 外部代码块的 finally 执行了";  
        }  
        // finally 中有 return 的话,这之后的代码由于不会被执行,而产生会编译错误  
    }
}

运行结果:

不产生异常时:初始内容 + 内部代码块的 try 返回
内部 try 产生异常时:初始内容 + 内部代码块的 catch 执行了 + 内部代码块的 catch 返回
内部 catch 也产生异常时:初始内容 + 内部代码块的 finally 执行了 + 外部代码块的 catch 执行了 + 外部代码块的 catch 返回

结果分析:

不产生异常时:正常执行 try {} 中的 return

try 产生异常时:由于 finally {} 中没有 return 语句,所以执行 catch {} 中有 return 语句,但是 finally {} 中的逻辑会执行。

catch 也产生异常时:由于内部 finally {} 中没有 return 语句,所以异常会向外抛出,被外部 catch {} 捕捉到,执行外部 catch {} 中有 return 语句。其中外部 finally {} 中的逻辑均会执行。

六、写在后面

欢迎关注,会经常记录一些学习思考笔记。如有错误与补充,感谢您的指出。

欢迎随时留言讨论,与君共勉,知无不答!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RCNN(Region-based Convolutional Neural Networks)算法是一种用于目标检测的深度学习算法。其思想是将输入图像分割成不同的区域,然后对每个区域进行卷积神经网络的特征提取,最后通过分类模块判断每个区域是否包含感兴趣的目标。 RCNN算法的主要步骤包括: 1. 输入图像经过Selective Search算法对图像进行区域提取,生成多个候选区域。 2. 将每个候选区域调整为同样的尺寸,以适应卷积神经网络的输入要求。 3. 使用预训练的卷积神经网络(如AlexNet或VGGNet)对每个候选区域进行前向计算,提取该区域的特征表示。 4. 将每个候选区域的特征表示输入到一个全连接层中进行分类,判断该区域是否包含目标物体,并输出预测结果。 5. 对于输出的预测结果,使用非极大值抑制(Non-Maximum Suppression,NMS)算法进行处理,去除重叠的检测结果。 RCNN算法的核心思想就是利用卷积神经网络来提取图像的特征表示,然后通过分类模块对每个候选区域进行分类预测。相比于传统的滑动窗口方法,RCNN算法只对候选区域进行处理,大大减少了计算量。 然而,RCNN算法存在的一个问题是速度较慢,因为需要对每个候选区域分别进行卷积神经网络的计算。为了提高速度,后续的改进算法如Fast R-CNN和Faster R-CNN应运而生。 总的来说,RCNN算法通过将图像分割为候选区域,并利用卷积神经网络提取区域特征,实现了目标检测的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值