什么是异常?
异常是在程序中导致程序中断运行的一种指令流。
异常发生的逻辑:
- 程序在JVM虚拟机中编译,发现编译错误后,将错误作为对象返回。
- 如果程序员不修改程序,那么错误对象就会类似于return一样传递到main程序入口,继而传回JVM中。
- 当JVM收到错误对象时,就会中断程序,并将异常信息显示到控制台。
异常体系结构
异常指的是Exception , Exception类, 在Java中存在一个父类Throwable(可能的抛出)
Throwable存在两个子类:
- 1.Error:表示的是错误,是JVM发出的错误操作,只能尽量避免,无法用代码处理。
- 2.Exception:一般表示所有程序中的错误,所以一般在程序中将进行try…catch的处理。
- 简单来说,error是指程序员无法通过代码来修改的错误,只能避免。比如硬件内存过小导致溢出错误等。而异常是指程序员能够通过代码修改从而实现正常功能的。
- 受检异常在编写代码时b会受到IDE的检查,如果异常了代码就会有下波浪线飘红。
- 非受检异常在编写时不会接受IDE的检查,只有在程序运行时才发生问题继而报错。
- 但两种异常的处理方式是一样的。
处理异常
处理异常的思想是控制异常发生逻辑的第二步,不要让错误对象返回到JVM中。
如果要想对异常进行处理,则必须采用标准的处理格式,处理格式语法如下:
//try后面跟多个catch,则可以处理多个异常。
try{
// 有可能发生异常的代码段
}catch(异常类型1 对象名1){
// 异常的处理操作
}catch(异常类型2 对象名2){
// 异常的处理操作
} ...
finally{
//必然执行的异常统一处理出口
//无论是否发生异常,finally必然执行
}
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(“亲,必须输入数字哦”); }
- 格式2,作为了解,不怎么用
```java
try{
}
catch(ArithmeticException|InputMismatchException e){
System.out.println("输入有误");
}
- 格式3,常用 ```java try{
} //因为ArithmeticException异常的父类的父类, // InputMismatchExceptio异常的父类,都是RuntimeException异常, // 所以可以用RuntimeException作为catch的匹配条件 //ArithmeticException异常和InputMismatchExceptio异常都会属于RuntimeException异常的多态 catch(RuntimeException e){ //多态 System.out.println(“输入有误”);
- 根据格式3的思想,在实际工作中,我们多使用异常的根类Exception作为catch的匹配条件,保证程序发生异常时不奔溃,但缺点是解决异常时没有针对性,只有广泛性。格式如下:
```java
try{
}
catch(Exception e){
System.out.println("输入有误");
finally
在进行异常的处理之后,在异常的处理格式中还有一个finally语句,那么此语句将作为异常的统一出口,不管是否产生了异常,最终都要执行此段代码。
finally面试题
在xxx的情况下,finally的程序块是否会执行?
答:除了一些不可抗力因素,如电脑断电关机,程序内存清空,软件被关闭 等情况,finally的程序块不会执行。其他情况一律会执行。
下面看个例子
原理分析对比
基本数据类型储存在栈,修改其值是直接在栈内修改为a=20,但是返回值是栈内存中的备份值,是10。
因为对象是引用地址类型,在栈内存储的是对象的地址,对象的属性和行为储存在堆内存中。同样的,return返回值是栈内存中的备份值,即对象的内存地址0x123。当修改对象值时p.age=20,实际是修改了堆内存的age值。栈内存中的地址值没有变,仍然是0x123。但是地址对应的对象的age属性的值变成了28。
关键:
- 返回值都是备份值。
- 基本数据类型的修改值是修改栈中的值,引用数据类型的修改值是修改堆内存的对象属性值
finally面试题总结
- 会不会执行finally的代码,看程序的catch模块中有没有System.exit()语句。有则出现异常时不会执行finally的程序块。
- 在try中return了一个值a,在finally中修改该值为b。那么系统最终return的值是多少?
- 判断数据类型,如果是基本数据类型,那么最终returnd的值为a。如果是引用数据类型(对象等),那么最终returnd的值为b.
异常处理常见面试题
- try-catch-finally 中哪个部分可以省略?
答: catch和finally可以省略其中一个 , catch和finally不能同时省略
注意:格式上允许省略catch块, 但是发生异常时就不会捕获异常了,我们在开发中也不会这样去写代码.
- try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
答:finally中的代码会执行
详解:
执行流程:
- 先计算返回值, 并将返回值存储起来, 等待返回
- 执行finally代码块
- 将之前存储的返回值, 返回出去;
需注意:
- 返回值是在finally运算之前就确定了,并且缓存了,不管finally对该值做任何的改变,返回的值都不会改变
- finally代码中不建议包含return,因为程序会在上述的流程中提前退出,也就是说返回的值不是try或 catch中的值
- 如果在try或catch中停止了JVM,则finally不会执行.例如停电- -, 或通过如下代码退出 JVM:System.exit(0);
什么时候应该选择try…catch来处理异常?什么时候选择throws把异常抛出去?
- 如果是因为传参导致异常,应该通过throws将异常抛出去。
- 如果是方法出现异常,应该用try…catch来处理异常。
throws与throw的区别:
throws抛出系统的异常,throw用于抛出自定义异常。
if(age<0||age>180){
RuntimeException e = new RuntimeException("年龄不合理"); //自定义异常
throw e;
}
else {
this.age = age;
}
抛出异常抛到哪里:抛到出现异常的函数的调用处。