Java面试基础——异常基础知识+可查异常、非可查异常的处理方式和区别+try...catch..finally用法和陷阱总结

发布于:2022-10-17 ⋅ 阅读:(529) ⋅ 点赞:(0)

工作日🐇🐇🐇
这两天在刷Java技能树,刚好刷到异常,也让我自己认识到很多的不足,这里来总结一下,感谢c站技能树,这里有很多知识可以学习😗⭐⭐⭐
以下结合本人经验+c站技能树知识做了一个汇总😼😼

在这里插入图片描述

异常基础知识

Exception分为两类,一类是:运行时异常(RuntimeException)另一类是编译时异常(书上写的可称非运行时异常或已检查异常,是因为在Exception类中的子类只有RuntimeException是运行时异常。)

在进行异常捕获的时候,一定要记住要先捕获小异常,再捕获大异常

RuntimeException:不可查异常

对于不可查异常,编译器不会强制要求处理的异常,而编译时异常(非运行时异常),编译器会强制要求进行处理。
就是你在写代码的时候(编译期间)他就会给你报错,这就是可查异常,编译器会警告你,否则就是不可查异常(下面的例子非常清楚了)

常见的运行时异常(虽然编译也能通过,但是还是需要我们处理避免出现这些异常。)

  • ArrayIndexoutofBoundsException 访问数组时使用无效索引值
  • NullPointException 尝试访问null对象成员,空指针异常
  • ClassCastException 类型转换异常
  • ArithmeticException 非法的算术运算,如以0作除数
  • NumberFormatException 数字转化格式异常
  • IllegalArgumentException 方法接收到非法参数
  • IndexOutOfBoundsException 下标超出范围异常

Checked:可查异常

除了上述的Runtime异常和其子类异常之外,其他的异常就是Checked异常

例子

ublic static void t1(){
        try {
            System.out.println("hi");
        }catch (IndexOutOfBoundsException e){
            e.printStackTrace();
        }
    }


    public static void t2(){
        try {
            System.out.println("hi");
        }catch (NullPointerException e){
            e.printStackTrace();
        }
    }

在这里插入图片描述

可以看到编译器没有报错,现在来试试可查异常

public static void t3(){
        try {
            System.out.println("hi");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

在这里插入图片描述

编译器提示说:在try里面压根就不会抛出什么异常,因为只有一个输出语句,因此试图捕获这个异常是错误的

总结

对上述简单实验得到总结:如果一个catch语句试图捕获一个类型为XXXException的异常,那么它对应的try语句必须有可能抛出XXXException或其子类异常

自动关闭的try语句(补充)

之前出国一篇文章讲述关闭资源的正确方式:传送门
这样关闭的方式是没问题,但是代码比较臃肿,代码可读性不高。Java7之后,Java新增了自动关闭资源的try语句,也就是try后面加一对圆括号,括号里可以声明、初始化一个或者多个资源,这个资源就是你最后要关闭的资源,比如文件流啊、数据库连接之类的
🐕‍🦺🐕‍🦺注意,这里可以帮忙关闭的前提,是括号里的资源是可关闭的,也就是实现了AutoCloseable接口或者Closeable接口,实现这两个接口就必须实现close方法,不然你在括号里写的东西都不能关闭,那就没什么意义了

try(var oos = new ObjectOutputStream()){
  ...
}catch(){
	...
}

放在圆括号里声明、初始化,这样try语句就会自动关闭它们
🤓🤓几乎全部的资源类都实现了上面的两个接口,所以不用太担心

try-catch-finally

一般例子为:

try {
    //可能出现异常的代码
}catch(异常类型1 变量名1) {
    //处理异常的方式1
}catch(异常类型2 变量名2) {
    //处理异常的方式2
}
....
finally {
    //一定会执行的代码
}

📣📣📣注意:一个try语句可以对应多个catch,并且遵循先捕获小异常,后捕获大异常

finally块陷阱

大家都知道,finally块总是会被执行,但是还是有一些小陷阱

finally执行规则(终止情况)

有一种情况他不会执行
也就是:System.exit(0);

try(){
	...
	System.exit(0);
}finally{
	...do something...
}

重要

程序运行到System.exit(0);之后,finally块不会再被执行
这个语句的作用:停止当前线程和所有其他当场死亡的线程
而finally块并不能让已经停止的线程继续执行

当System.exit(0)被调用时,虚拟机退出前要执行两项清理工作:

  • 执行系统中注册的所有关闭钩子(这个是安全的)
  • 如果程序调用了System.runFinalizerOnExit(true);那么JVM会对所有还未结束的对象调用Finalizer(这种方式非常危险,而且现在已经过时了)

finally块和方法返回值

经典案例一

public static void main(String[] args) {
        int a = test();
        System.out.println(a);  // 7
    }

    public static int test(){
        int count = 5;
        try {
            return count++;
        }finally {
            System.out.println("finally块被执行");
            return ++count;
        }
    }

最终输出的结果是7,这表明,finally块也被执行了
所以他根本就没有执行try语句里面的return语句

⭐⭐当Java程序执行try语句catch语句遇到了return语句,那么return语句会导致该方法立即结束,系统执行完return语句之后,并不会立即结束该方法,而是去寻找有没有finally语句块,如果没有,那么该方法终止,并且返回相应的值;如果有,那么系统就立即开始执行finally块——只有当finally块执行完成之后,系统才会跳回来根据return语句结束方法;如果finally语句块中使用了return语句导致方法结束,则finally块已经结束了方法,系统将不会跳回去执行try语句、catch语句里的任何代码

经典案例二

public static void main(String[] args) {
        int test2 = test2();
        System.out.println(test2);  //5
    }
public static int test2(){
        int count = 5;
        try {
            int a = 20 / 0;
        }finally {
            System.out.println("finally被执行");
            return count;
        }
    }

上述代码在try语句块必然会抛出异常RuntimeException异常,但是程序没有使用catch去捕获,那么在这种情况下,这个异常应该导致test2方法非正常中止,test2应该没有返回结果
但是实际情况确实,并没有抛出异常,而是输出了结果5,就像int a = 20 / 0;这句话没啥用的感觉
🍊🍊🍊总结:一定要理解finally块执行流程,当程序执行try、catch语句块时候,遇到throw语句时,throw语句会导致这个方法结束,这个是没问题的;但是系统并不会马上结束,他会先去寻找该异常 处理流程中是否包含finally语句,如果没有,则马上抛出异常,中止方法;如果有,那么立即执行finally语句块——只有当finally语句块执行完成后,系统才会跳回来抛出异常、中止程序。
当然,如果finally里包含return语句,那么程序就不会回去

本文含有隐藏内容,请 开通VIP 后查看