java的异常、finally和return

前言

本文主要讲解java的异常体系及有关finally和return的返回问题。

继承体系

java中所有异常类的基类都是Throable类。它的两大子类分别是Error和Exception。Error表示错误,是程序处理不了的。

Exception类又分为运行时异常(RuntimeException异常)和非运行时异常(或称为受检异常)。顾名思义,运行时异常是程序在运行时才能被检查出来的异常,可以不强制抛出。而受检异常则必须要处理,或通过try-catch捕获,或抛出。

在这里插入图片描述

关于finally和return

在异常块中,有finally块,表示在返回前的清理工作,常和return搭配使用。这里先要介绍有关java栈的有关知识

java栈是JVM的一个线程私有的区域。栈中每个栈帧为每个方法执行的线程模型,每个栈帧的入栈出栈代表方法的的执行与退出。简单说来,一个方法执行,在JVM中就是一个栈帧被放在栈顶执行的过程。

栈帧中包含 操作数栈,局部变量表return操作就是将操作数栈栈顶的值返回。在此之前已经将计算结果放到了操作数栈顶)

return [expression] 的执行过程为

  1. 计算return表达式值,即 [expression],将计算结果放入栈顶
  2. 执行return,返回栈顶值。

加上finally后,return的逻辑变为

  1. 计算return表达式值,放入栈顶
  2. 将栈顶值拿出来放入局部变量表中,以便执行finally
  3. 执行finally语句
  4. 将第二步操作数栈的值拿回来,放回栈顶
  5. 执行return,返回栈顶元素

由此可知,return的逻辑没有变,只是由于finally的存在,将return的值暂存起来,以便finally语句能使用栈,等使用完后再将返回值替换回来。finally语句块设计出来的目的只是为了让方法执行一些重要的收尾工作,而不是用来计算返回值的

所以, finally一般不能修改返回值,原因如上,在执行finally时,返回值就确定了,已经存起来了。可是,下面两种情况除外,

  1. 如果在finally语句中有return,结果就直接返回了。
  2. 返回的是引用类型,在finally中修改了引用指向的内容。

下面是示例

  1. return和finally的先后顺序
    //基本类型,finally修改了值,但没影响返回值
    // 返回 10
    @Test
    public void testReturnValue() {
        System.out.println(testValue1());
    }

    public int testValue1() {

        int a = 0;
        try {
            a = 10;
            return a;
        } catch (Exception e) {
            a = 20;
            return a;
        } finally {
            a = 30;
        }
    }
  1. finally中有返回值

    // finally中有返回语句,返回30,当finally有return时,就直接返回了
    @Test
    public void testReturnValue() {
        System.out.println(testValue1());
    }

    public int testValue1() {

        int a = 0;
        try {
            a = 10;
            return a;
        } catch (Exception e) {
            a = 20;
            return a;
        } finally {
            a = 30;
            return a;
        }
    }
  1. 返回值为引用类型,在finally修改了

   // 创建一个测试用的引用类
   class Node {
        private int value;

        public Node(int value) {
            this.value = value;
        }
    }

    @Test
    public void testReturnValue() {

        System.out.println(testValue1().value);
    }

    public Node testValue1() {

        Node node = null;
        try {
            // 创建引用
            node = new Node(100);
            return node;
        } finally {
            // 在finally中修改值
            node.value = 300;
        }
    }

大家先想想返回值是什么,可以知道,按照之前的说法,在return会把返回值暂存起来,然后执行finally,这里finally执行修改了实际引用所指向的内容,所以返回值为300

  1. 返回值为引用,在finally中修改了引用指向

// 创建一个测试用的引用类
   class Node {
        private int value;

        public Node(int value) {
            this.value = value;
        }
    }

    @Test
    public void testReturnValue() {

        System.out.println(testValue4().value);
    }

    public Node testValue4() {

        Node node = null;
        try {
            // 创建引用
            node = new Node(100);
            return node;
        } finally {
            // 在finally中修改引用
           node = new Node(300);
        }
    }

这个示例又输出什么呢?可以看到它和上面的那个示例不一样的地方,示例3修改了引用指向的对象,而示例4只是修改了引用。还记得上面说的原理吗?在return返回前,会将结果复制到局部变量表,此时该引用就不变了,后续finally的修改已经和它没关系了。类似于值传递和引用传递的关系(java都是值传递,对于引用只是复制了一份而已,这个也一样)。

输出 300

总结

  1. finally有返回值,返回结果为finally中的值,try的return值会被覆盖掉,一般不建议这样做。
  2. finally没有返回值,try或catch的return会被寄存起来,一般finally不会修改到返回值,除非finally修改了该引用指向的实际内存内容。

参考文章

  1. JAVA中return与finally的先后关系
  2. return与finally到底谁先执行
  3. Java中try catch finally语句中含有return语句的执行情况(总结版)
  4. 关于 Java 中 finally 语句块的深度辨析
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值