关于异常我个人的理解:程序在运行过程中会因为很多原因导致程序停止,我们可以把预想到的错误用异常来表示,良好的try…catch会使我们的程序更加的健壮,所以优秀的代码离不开异常的处理
一、异常分类
1、Throwable:可抛出的,是异常的顶层父类,其他的异常或者错误都是Throwable的子类类型,只有是Throwable的体系类型,才可以使用异常的处理机制。
2、Error:错误,是Throwable的子类,用于描述那些无法捕获和处理的错误情况,属于非常严重的错误,程序会停止运行。例如StackOverflowError
3、Exception:异常,是Throwable的子类,用于描述那些可以捕获和处理的不正常情况,属于不太严重的错误。例如NullPointerException
4、RuntimeException:运行时异常,是Exception的一个特殊的子类,在编译阶段是不做检查的一个异常。
二、异常的处理格式和方法
try…catch异常处理的格式:
try{
//可能出现异常的代码
}catch(可能出现异常的类型1 标识符){
//针对该异常的处理方式1
}catch(可能出现异常的类型2 标识符){
//针对该异常的处理方式2
}finally{
//最终执行的代码块
}
常用方法:
1、Throwable中的构造方法
Throwable():创建一个没有任何参数的异常对象
Throwable(String mes):创建一个带有指定消息的异常对象
Throwable(Throwable cause):创建一个有原因的异常对象
2、常用成员方法
getCause():获取异常对象中的原因异常
getMessage():获取异常详细的消息字符串
toString():返回此throwable的简短描述
printStackTrace():打印异常的调用栈轨迹
public class XianYuTest(){
public static void main(String [] args){
Throwable th = new Throwable();
System.out.println(th);//java.lang.Throwable
Throwable th1 = new Throwable("咸鱼不是鱼");
System.out.println(th1);//java.lang.Throwable:咸鱼不是鱼
Throwable th2 = new Throwable(th1 );
System.out.println(th2);//java.lang.Throwable:java.lang.Throwable:咸鱼不是鱼
//常用成员方法
System.out.println(th1.getMessage());//咸鱼不是鱼
System.out.println(th1.getCause());//java.lang.Throwable:咸鱼不是鱼
System.out.println(th1.toString());//java.lang.Throwable:咸鱼不是鱼
}
}
三、异常处理机制
##详细介绍try…catch(Contorller层是不用throws异常的)
1.try…catch语句
在try代码块中抛出异常之后,立即转到catch代码块执行或者退栈到上一层方法处寻找catch代码块。
2.finally语句:无论程序有没有异常,在任何情况下都必须执行的一段代码
由于异常会强制中断正常程序流程,这会使得某些不管在任何情况下都必须执行的步骤被忽略,从而影响程序的健壮性。使用finally语句,不管try代码块中是否出现了异常,都会执行finally代码块。
在某些情况下,把finally的操作放在try···catch语句的后面,这也能保证这个操作被执行。比如我们在做文件上传的时候,无论上传失败与否,最终还是要用finally来执行记录上传Log日志。
finally这种情况尽管在某些情况下是可行的,但不值得推荐,个人以为它有两个缺点:
@把与try代码块相关的操作孤立开来,使程序结构松散,可读性差。
@影响程序的健壮性。假如catch代码块继续抛出异常,就不会执行catch代码块之后的操作。
3、throws子句:声明可能会出现的异常
如果一个方法可能会抛出异常,但没有能力来处理这种异常,可以在方法声明处用throws子句来声明抛出异常。
一个方法可能会出现多种异常,throws子句允许声明抛出多个异常,中间用“,”隔开。
4、throw语句:抛出异常
throw语句用于在catch块中抛出自定义异常。有throw语句抛出的对象必须是java.lang.Throwable类或者其他子类的实例。
5、异常处理语句的语法规则
异常处理语句主要涉及到try、catch、finally、throw、throws关键字,要正确使用它们,就必须遵守以下必要的语法规则:
(1)try代码块不能脱离catch代码块或finally代码块而单独存在。try代码块后面至少有一个catch代码块或finally代码块。
(2)try代码块后面可以有零个或多个catch代码块,还可以有零个或至多一个finally代码块。如果catch代码块和finally代码块共存,finally代码块必须在catch代码块后面。
(3)try代码块后面可以只跟finally代码块。
(4)在try代码块中定义的变量的作用域为try代码块,在catch代码块和finally代码块中不能访问该变量。如果希望在catch代码块和finally代码块中访问try中用到的变量,则必须把变量定义在try代码块的外面(局部变量的作用域)。
(5)当try代码块后面有多个catch代码块时,Java虚拟机会把实际抛出的异常对象和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,而不会再执行其他的catch代码块。
(6)如果一个方法可能出现受检查异常,要么用try···catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。
6、异常流程的运行过程
异常流程有try···catch···finally语句来控制。如果程序中还包含了return和System.exit()语句,我们的程序就会变得更加复杂。
(1)finally语句不被执行的唯一情况就是先执行了用于终止程序的System.exit()方法。java.lang.System类的静态方法exit()用于终止当前的Java虚拟机进程,Java虚拟机所执行的Java程序也随之终止。另外,当catch语句中也抛出异常的情况下,在程序退栈寻找上一个方法的catch代码块之前,会先执行finally代码块。
(2)return语句用于退出本方法。在执行try或catch代码块中的return语句时,假如有finally代码块,会先执行finally代码块。
(3)finally代码块虽然在return语句之前就被执行(这里是指在return返回之前执行,如果return a+b;那么是先执行了a+b,再执行finally代码块,再返回),但finally代码块不能通过重新给变量赋值的方式改变return语句的返回值。
(4)建议不要在finally代码块中使用return语句,以为它会导致以下两种潜在的错误。
第一种错误是覆盖try或catch代码块的return语句。可以这样理解,在try或者catch中的return在把返回的结果赋给一个不知名的临时变量后,执行finally,如果没有finally里的return语句,接着回来将这个不知名变量的内容返回,如果在finally中出现了return语句,那么这个return语句没有被打断,给另一个不知名变量赋值之后,直接返回了,方法退栈,try或catch里的返回没有别执行,这样的结果就是finally中的return覆盖了try和catch中的return语句。
第二中错误是丢失异常。如果catch代码块中有throw语句抛出异常,由于先执行了finally代码块,又因为finally代码块中有return语句,所以方法退栈,catch代码块中的throw语句就没有被执行。
四、自定义异常
##自定义异常的步骤:
1、定义一个类,以Exception结尾,什么类型的异常尽量起一个英文名字,例如:TransactionException(交易异常)
2、让定义的这个类继承一个Exception或者RuntimeException
如果定义的是编译时异常就继承Exception
如果定义的是运行时异常就继承RuntimeException
3、构造方法不能被继承,需要进行手动的添加,调用父类中的构造
五、编译时异常和运行时异常
1、编译时异常:代码在编译阶段进行检测的异常,一旦代码中出现了编译时异常,那么在编译阶段是不给通过的,只能通过异常处理的手段进行处理(异常的捕获和处理,异常的声明(throws))
2、运行时异常:运行时异常在编译阶段是不做检查的,在编译阶段即使有了运行时异常,也不会编译失败,相当于在编译阶段是没有异常的,可以通过异常声明处理的手段进行处理,运行时异常 我个人感觉异常声明是没有意义的
3、继承体系:
编译时异常就是Exception和Exception的子类类型(除了RuntimeException)
运行时异常就是RuntimeException和RuntimeException的子类类型