程序设计中异常处理框架的好坏直接影响到整个项目的代码质量以及后期的维护成本。
异常:程序在运行时出现的问题。这个问题按照面向对象思想进行描述,并封装成了对象。这些问题产生的原因、名称、描述等多个属性信息存在,将这些异常按照面向对象的思想对问题进行对象封装,这样就方便于操作问题以及处理问题。
异常出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系。
Throwable:可抛出的。
Error:错误,一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。Java应用程序本身无法恢复的严重错误,应用程序不需要捕获、处理这些严重错误。当程序发生这种严重错误时,通常的做法是通知用户并中止程序的执行。 Error是无法处理的异常,比如OutOfMemoryError ,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常
Exception:异常,可以有针对性的处理方式,异常可分为运行时异常(RuntimeException)和检查时异常(CheckedException)两种。
无论是错误还是异常,它们都有具体的子类体现每一个问题,它们的子类都有一个共性,就是都以父类名才作为子类的后缀名。
这个体系中的所有类和对象都具备一个独有的特点;
就是可抛性,可抛性的体现:就是这个体系中的类和对象都可以被throws和throw两个关键字所操作。
在开发时,如果定义功能时,发现该功能会出现一些问题,应该将问题在定义功能时标示出来,这样调用者就可以在使用这个功能的时候,预先给出处理方式。如何标示呢?通过throws关键字完成,格式:throws 异常类名,异常类名...
这样标示后,调用者,在使用该功能时,就必须要处理,否则编译失败。
处理方式有两种:1、捕捉;2、抛出。
对于捕捉:java有针对性的语句块进行处理。
try {
需要被检测的代码;
} catch ( 异常类 变量名 ){
异常处理代码;
} finally {
一定会执行的代码;
}
异常处理原则:
功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样的处理有针对性,抛几个就处理几个。
特殊情况:try对应多个catch时,如果有父类的catch语句块,一定要放在下面。
引发异常时,会按顺序查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句,其后 catch 语句被忽略
在捕获异常的时候,应按照“从小到大”的顺序捕获异常,即先子类后父类
Java异常在try/catch块后加入finally块,可以确保无论是否发生异常 finally块中的代码总能被执行
try…catch…finally异常处理结构中,try语句块是必须的, catch和finally语句块为可选,但两者至少出现一个语句块
先子类异常,后父类异常。无论是否发生异常,finally块总被执行
throw 和throws关键字的区别:
throw用于抛出异常对象,后面跟的是异常对象,throw用在函数内。
throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在函数上。
通常情况:函数内容如果有throw,抛出异常对象,并没有进行处理,那么函数上一定要声明,否则编译失败。但是也有特殊情况。
异常分两种:
1:编译时被检查的异常,只要是Exception及其子类都是编译时被检测的异常CheckedException:检查时异常,又称为非运行时异常,这样的异常必须在编程时进行处理,否则就会编译不通过。例如我们在前面的学习过程中,经常在编译的时候发生类找不到的情况,这就是一个典型的检查时异常。
2:运行时异常,其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。
RuntimeException:运行时异常,即程序运行时抛出的异常,这种异常在写代码时不进行处理,Java源文件也能编译通过。
Try{
//可能抛出异常的语句块
}catch(SomeException1 e){ // SomeException1特指某些异常
//当捕获到SomeException1类型的异常时执行的语句块
} catch(异常类 变量){
//当捕获到SomeException2类型的异常时执行的语句块
System.exit(0);//退出jvm。
}finally{
//无论是否发生异常都会执行的代码,通常用于关闭(释放)资源。
}
编译时被检查的异常和运行时异常的区别:
编译被检查的异常在函数内被抛出,函数必须要声明,否编译失败。
声明的原因:是需要调用者对该异常进行处理。
运行时异常如果在函数内被抛出,在函数上不需要声明。
不声明的原因:不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以,不让调用处理的,直接让程序停止,由调用者对代码进行修正。
定义异常处理时,什么时候定义try,什么时候定义throws呢?
功能内部如果出现异常,如果内部可以处理,就用try;
如果功能内部处理不了,就必须声明出来,让调用者处理。
Throw
Throw用在方法内,用于抛出具体异常类的对象
1,抛给自己
运行时异常 : 1.无视异常:2.使用try-catch包裹throw:3,:throws抛出但不是必须的
检查时异常:1.必须自己try catch解决2.throws抛出这个异常
2,抛给方法调用者
Throws
throws用于声明方法可能抛出的异常,其后为异常类,可以有多个,异常类之间用英文逗号间隔。
如果throws抛出检查时异常类,此时方法调用者只能有两种处理方式:①、使用try-catch处理异常;②、继续上抛该异常类
如果throws抛出的是运行时异常类,则方法调用者有三种方式来处理该异常:①、无视该异常;②、使用try-catch处理异常; ③、继续上抛该异常类
自定义异常:当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。
对于角标是整数不存在,可以用角标越界表示,
对于负数为角标的情况,准备用负数角标异常来表示。
负数角标这种异常在java中并没有定义过。
那就按照java异常的创建思想,面向对象,将负数角标进行自定义描述。并封装成对象。
这种自定义的问题描述成为自定义异常。
注意:如果让一个类称为异常类,必须要继承异常体系,因为只有称为异常体系的子类才有资格具备可抛性。
对于除法运算,0作为除数是不可以的。java中对这种问题用ArithmeticException类进行描述。对于这个功能,在我们项目中,除数除了不可以为0外,还不可以为负数。可是负数的部分java并没有针对描述。所以我们就需要自定义这个异常。
自定义异常的步骤:
1:定义一个子类继承Exception或RuntimeException,让该类具备可抛性。
2:通过throw 或者throws进行操作。
异常的转换思想:
当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。
try catch finally的几种结合方式:
try ~ ~ catch
try ~ ~ finally
try ~ ~ catch ~ ~ finally
这种情况,如果出现异常,并不处理,但是资源一定关闭,所以try finally集合只为关闭资源。
记住:finally很有用,主要用户关闭资源。无论是否发生异常,资源都必须进行关闭。
System.exit(0); //退出jvm,只有这种情况finally不执行。
子父类进行覆盖的特点:
1:当子类覆盖父类的方法时,如果父类的方法抛出了异常,那么子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类,不能抛出其他异常。
2:如果父类抛出了多个异常,那么子类在覆盖时只能抛出父类的异常的子集。
注意:
如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws。
如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。
常见异常:
1、脚标越界异常(IndexOutOfBoundsException)包括数组、字符串;空指针异常(NullPointerException)
2、类型转换异常:ClassCastException
3、没有这个元素异常:NullPointerException
4、不支持操作异常; 异常要尽量避免,如果避免不了,需要预先给出处理方式。比如家庭备药,比如灭火器。