什么是异常?

异常是在程序中导致程序中断运行的一种指令流。

异常发生的逻辑:

  • 程序在JVM虚拟机中编译,发现编译错误后,将错误作为对象返回。
  • 如果程序员不修改程序,那么错误对象就会类似于return一样传递到main程序入口,继而传回JVM中。
  • 当JVM收到错误对象时,就会中断程序,并将异常信息显示到控制台。
    异常处理 - 图1异常体系结构

异常指的是Exception , Exception类, 在Java中存在一个父类Throwable(可能的抛出)
Throwable存在两个子类:

  • 1.Error:表示的是错误,是JVM发出的错误操作,只能尽量避免,无法用代码处理。
  • 2.Exception:一般表示所有程序中的错误,所以一般在程序中将进行try…catch的处理。
  • 简单来说,error是指程序员无法通过代码来修改的错误,只能避免。比如硬件内存过小导致溢出错误等。而异常是指程序员能够通过代码修改从而实现正常功能的。

异常处理 - 图2

  • 受检异常在编写代码时b会受到IDE的检查,如果异常了代码就会有下波浪线飘红。
  • 非受检异常在编写时不会接受IDE的检查,只有在程序运行时才发生问题继而报错。
  • 但两种异常的处理方式是一样的。

处理异常

处理异常的思想是控制异常发生逻辑的第二步,不要让错误对象返回到JVM中。

如果要想对异常进行处理,则必须采用标准的处理格式,处理格式语法如下:

  1. //try后面跟多个catch,则可以处理多个异常。
  2. try{
  3. // 有可能发生异常的代码段
  4. }catch(异常类型1 对象名1){
  5. // 异常的处理操作
  6. }catch(异常类型2 对象名2){
  7. // 异常的处理操作
  8. } ...
  9. finally{
  10. //必然执行的异常统一处理出口
  11. //无论是否发生异常,finally必然执行
  12. }

try+catch的处理流程

1、 一旦产生异常,则系统会自动产生一个异常类的实例化对象。

2、 那么,此时如果异常发生在try语句,则会自动找到匹配的catch语句执行,如果没有在try语句中,则会将异 常抛出.

3、 所有的catch根据方法的参数匹配异常类的实例化对象,如果匹配成功,则表示由此catch进行处理。

异常处理格式

  • 格式1: ```java try{

} catch (ArithmeticException e){ System.out.println(“亲,除数不能为0!”); } catch(InputMismatchException e){ System.out.println(“亲,必须输入数字哦”); }

  1. - 格式2,作为了解,不怎么用
  2. ```java
  3. try{
  4. }
  5. catch(ArithmeticException|InputMismatchException e){
  6. System.out.println("输入有误");
  7. }
  • 格式3,常用 ```java try{

} //因为ArithmeticException异常的父类的父类, // InputMismatchExceptio异常的父类,都是RuntimeException异常, // 所以可以用RuntimeException作为catch的匹配条件 //ArithmeticException异常和InputMismatchExceptio异常都会属于RuntimeException异常的多态 catch(RuntimeException e){ //多态 System.out.println(“输入有误”);

  1. - 根据格式3的思想,在实际工作中,我们多使用异常的根类Exception作为catch的匹配条件,保证程序发生异常时不奔溃,但缺点是解决异常时没有针对性,只有广泛性。格式如下:
  2. ```java
  3. try{
  4. }
  5. catch(Exception e){
  6. System.out.println("输入有误");

finally

在进行异常的处理之后,在异常的处理格式中还有一个finally语句,那么此语句将作为异常的统一出口,不管是否产生了异常,最终都要执行此段代码。

finally面试题

在xxx的情况下,finally的程序块是否会执行?

答:除了一些不可抗力因素,如电脑断电关机,程序内存清空,软件被关闭 等情况,finally的程序块不会执行。其他情况一律会执行。

下面看个例子

异常处理 - 图3

异常处理 - 图4

异常处理 - 图5

原理分析对比

基本数据类型储存在栈,修改其值是直接在栈内修改为a=20,但是返回值是栈内存中的备份值,是10。

异常处理 - 图6

因为对象是引用地址类型,在栈内存储的是对象的地址,对象的属性和行为储存在堆内存中。同样的,return返回值是栈内存中的备份值,即对象的内存地址0x123。当修改对象值时p.age=20,实际是修改了堆内存的age值。栈内存中的地址值没有变,仍然是0x123。但是地址对应的对象的age属性的值变成了28。

异常处理 - 图7

关键:

  • 返回值都是备份值。
  • 基本数据类型的修改值是修改栈中的值,引用数据类型的修改值是修改堆内存的对象属性值

异常处理 - 图8

finally面试题总结

  • 会不会执行finally的代码,看程序的catch模块中有没有System.exit()语句。有则出现异常时不会执行finally的程序块。
  • 在try中return了一个值a,在finally中修改该值为b。那么系统最终return的值是多少?
    • 判断数据类型,如果是基本数据类型,那么最终returnd的值为a。如果是引用数据类型(对象等),那么最终returnd的值为b.

异常处理常见面试题

  1. try-catch-finally 中哪个部分可以省略?

答: catch和finally可以省略其中一个 , catch和finally不能同时省略

注意:格式上允许省略catch块, 但是发生异常时就不会捕获异常了,我们在开发中也不会这样去写代码.

  1. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

答:finally中的代码会执行

详解:

执行流程:

  1. 先计算返回值, 并将返回值存储起来, 等待返回
  2. 执行finally代码块
  3. 将之前存储的返回值, 返回出去;

需注意:

  1. 返回值是在finally运算之前就确定了,并且缓存了,不管finally对该值做任何的改变,返回的值都不会改变
  2. finally代码中不建议包含return,因为程序会在上述的流程中提前退出,也就是说返回的值不是try或 catch中的值
  3. 如果在try或catch中停止了JVM,则finally不会执行.例如停电- -, 或通过如下代码退出 JVM:System.exit(0);

什么时候应该选择try…catch来处理异常?什么时候选择throws把异常抛出去?

  • 如果是因为传参导致异常,应该通过throws将异常抛出去。
  • 如果是方法出现异常,应该用try…catch来处理异常。

throws与throw的区别:

throws抛出系统的异常,throw用于抛出自定义异常。

  1. if(age<0||age>180){
  2. RuntimeException e = new RuntimeException("年龄不合理"); //自定义异常
  3. throw e;
  4. }
  5. else {
  6. this.age = age;
  7. }

抛出异常抛到哪里:抛到出现异常的函数的调用处。