【java】异常处理及捕获的理解

一、Java异常机制

java中的异常都继承自Throwable类,其子类分成Error和Exception。其中不需要我们捕获去处理的只有Error和RuntimeException,原因如下:
在这里插入图片描述

  1. Error是发生了严重错误,程序一般对此无能为力,比如OutOfMemoryError\NoClassDefFoundError\StackOverFlowError,即便我们捕获了也无能为力;
  2. RunTimeException类及其子类产生的原因往往是因为糟糕的编程引起的,比如NullPointerException,我们应该避免这类编程错误,而不是靠trycatch来处理;
  3. 上面两类Error和RuntimeException都是运行时错误,编译器在编译阶段是不会检查的异常,我们称之为非受查异常。除了Error和RuntimeException及其子类其它的称为受查异常,在编译时强制检查的异常,Exception类的IOException类及其子类就是受查异常,这类异常如果没有使用try-catch去捕获异常并处理或者throws/throw向上抛出异常,编译不会通过,这类错误往往不是因为程序引起的错误,如读取的文件不存在,在编程阶段往往是无法避免的,因此需要处理这些异常。

编程中应该注意的点:
在这里插入图片描述
在这里插入图片描述

二、Try catch finally的执行机制

最近经常会被Try catch finally中各种return的执行顺序所困扰,在这里记录一下。
比较好的博客:https://www.cnblogs.com/lanxuezaipiao/p/3440471.html

直接看程序会更明了:
问题:请给出下面例子的输出结果
例1:

public class FinallyTest1 {

    public static void main(String[] args) {
        
        System.out.println(test1());
    }

    public static int test1() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80; 
        }
        catch (Exception e) {

            System.out.println("catch block");
        }
        finally {
            
            System.out.println("finally block");
            
            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
        }
        
        return b;
    }   
}

输出:

try block
finally block
b>25, b = 100
100

说明try中的return语句执行之后,返回之前会执行finally中的代码。
例2:如果finally里也有return语句

public class FinallyTest2 {

    public static void main(String[] args) {

        System.out.println(test2());
    }

    public static int test2() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            return 200;
        }
        // return b;
    }
}

输出:

try block
finally block
b>25, b = 100
200

说明:finally里的return直接返回了,就不管try中是否还有返回语句,这里还有个小细节需要注意,finally里加上return过后,finally外面的return b就变成不可到达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错。

例三:如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变。
测试用例1:

public class FinallyTest3 {

    public static void main(String[] args) {

        System.out.println(test3());
    }

    public static int test3() {
        int b = 20;

        try {
            System.out.println("try block");
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b = 150;
        }
        return 2000;
    }
}

输出:

try block
finally block
b>25, b = 100
100

测试用例2:

import java.util.*;

public class FinallyTest6
{
    public static void main(String[] args) {
        System.out.println(getMap().get("KEY").toString());
    }
     
    public static Map<String, String> getMap() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("KEY", "INIT");
         
        try {
            map.put("KEY", "TRY");
            return map;
        }
        catch (Exception e) {
            map.put("KEY", "CATCH");
        }
        finally {
            map.put("KEY", "FINALLY");
            map = null;
        }
         
        return map;
    }
}

运行结果是:FINALLY,而不是TRY,也不是null。

说明:测试用例1中对变量b重新赋值,不会影响到try中return结果,测试用例2中对map重新赋值为null,也不会影响到try中return的map,但是对map的key进行重新赋值会最终影响到try中return的map,原因是java中是都是值传递而不是引用传递。

例四:是不是每次返回的一定是try中的return语句呢?那么finally外的return b不是一点作用没吗?

public class FinallyTest4 {

    public static void main(String[] args) {

        System.out.println(test4());
    }

    public static int test4() {
        int b = 20;

        try {
            System.out.println("try block");

            b = b / 0;

            return b += 80;
        } catch (Exception e) {

            b += 15;
            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b += 50;
        }

        return b;
    }

}

输出:

try block
catch block
finally block
b>25, b = 35
85

说明:因为在return之前发生了除0异常,所以try中的return不会被执行到,而是接着执行捕获异常的catch 语句和最终的finally语句,此时两者对b的修改都影响了最终的返回值,这时return b;就起到作用了。当然如果你这里将return b改为return 300什么的,最后返回的就是300,这毋庸置疑。

例5:如果catch中有return语句呢?当然只有在异常的情况下才有可能会执行,那么是在finally之前就返回吗?

public class FinallyTest5 {

    public static void main(String[] args) {

        System.out.println(test5());
    }

    public static int test5() {
        int b = 20;

        try {
            System.out.println("try block");
            
            b = b /0;

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
            return b += 15;
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b += 50;
        }

        //return b;
    }

}

输出:

try block
catch block
finally block
b>25, b = 35
35

说明: 当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。发生异常后,catch中的return语句先执行,确定了返回值后再去执行finally块,执行完了catch再返回,finally里对b的改变对返回值无影响,原因同前面一样。

总结为一句话:

finally块的语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。

三、值传递与引用传递

比较好的博文:博客博客2
推荐大家多看下博客2。
先总结一句:java中都是值传递。
不用特别纠结于什么是值传递什么是引用传递,大多数人一般认为java内的基础类型数据传递都是值传递. java中实例对象的传递是引用传递,其实这是错误的,java中只有值传递,因为都是传递的副本,基本类型是传递value的副本,引用类型是传递地址的副本。本质上都是值传递。

博客2中举了一个很好的例子:
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙,而是钥匙打开的房子。

记住三种情形,就可以了:

  1. 情形一:给函数传的参数是基本类型,那么改变参数值不会影响到外面的参数,因为传递的是副本;
  2. 情形二:给函数传的参数是引用类型,如一个map,此时如果改变map中的key,会影响到原来的map,因为相当于“砸了房子里的电视”;
  3. 情形三:情形二中如果对map重新赋值,比如 map = new Map<>();此时不会影响到原来的map,因为这个“钥匙”是个副本,只是将它的引用指向了一个新的地址,但没有改变原来钥匙的指向。

错误的理解:

  1. 错误理解1:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
  2. 错误理解2:Java是引用传递。
  3. 错误理解3:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值