一、总述:
异常会导致JVM非正常停止
面向对象语言中异常是一个类,产生异常就创建异常对象并抛出一个异常对象,java处理异常对象的方式是中断处理。异常不是指语法错误,语法错误编译不会通过,没有产生字节码文件,不能运行
java程序分为两个过程:编译过程、运行过程(加载器先加载,后再用解释运行器)
java的异常包含Error和Exception两种:
1、Error:java虚拟机JVM无法解决的严重问题,如:JVM系统内部错误、资源耗尽。解决方法改代码,不编写针对性的代码去解决Error。即此异常必须重写源代码
2、 Exception:因为编程错误或偶然的外在因素导致的一般性问题,可以使用针对性代码进行处理。即编译期异常,进行编译(写代码)java程序出现的问题,此异常可以人为处理不必重写源代码(其中包含RuntimeException运行异常)
如{
空指针;
试图读取不存在的文件
网络连接中断
数组脚标越界{
当出现数组索引越界异常此时,JVM会检测出程序会出现异常则JVM此时会做:
1.JVM根据异常产生的原因创建一个异常对象,
这个异常对象包含了异常产生的(内容,原因,位置):
ArrayIndexOutOfBoundsException(index);
2.在getElement方法中,没有异常的处理逻辑(try...catch),
则JVM会利用getElement方法把异常对象抛出给方法调用
者:main方法来处理异常
3.main方法接收到此异常对象,main方法也没有处理逻辑,
继续把异常对象抛给main方法调用者JVM处理
4.JVM收到异常对象则:把异常对象(内容,原因,位置)
以红色的爱字体打印在控制台,
同时JVM会终止当前正在执行的java程序-->中断处理
}
}
异常分类:编译异常,运行异常
异常的顶级父类:Throwable{ java.long.Throwable类是java语言中所有错误或异常的超类 }
顶级父类仅有两个子类:Error(不能人为处理),Exception(可以人为处理)
在Except中除了RuntimeException及其子类是运行时异常(非受检异常)外,其他均是编译异常(受检异常)。运行时异常被抛出可以不处理,即不捕获也不声明抛出。默认给虚拟机处理,终止程序,什么时候不抛出运行时异常了,再来执行程序。
Throwable类中定义了3个异常处理方法
String getMessage()返回此throwable的剪短描述
String toString()返回此throwable的详细信息字符串。
void printStackTrace()JVM定义异常对象,默认此方法,打印的异常信息是最全面的
throw关键字
作用:
可以使用throw关键字在指定的方法中抛出指定的异常
使用格式:
throw new xxxException("异常产生的原因")
注意:
1.throw关键字必须写在方法的内部
2.throw关键字后边new的对象必须是Exception或者Exception子类对象
3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
throw关键字后面创建的是RuntimeException或者是RuntimeException子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
throw关键字后面创建的是编译异常(写代码的时候报错),我们必须处理这个异常,要么throw,要么try...catch
定义一个方法,获取指定索引处的元素
参数:
int[] arr
int index
在后续的学习中必须对方法传递过来的参数进行合法性校验,若参数不合法,必须使用抛出异常的方式告知方法调用者,传递的参数有问题.
注意:
NullPointerException是一个空指针异常默认交给JVM处理
二、异常的体系结构
java.lang.Throwable
|----java.lang.Error:不编写针对性的代码去解决Error
|----java.lang.Exception:可以进行异常处理
|----运行时异常(非受检异常unchecked)--RuntimeException及其子类--执行java.exe命令时出现的异常
|----编译异常(受检异常checked)--执行javac.exe命令时,可能出现的异常
三、异常的处理:抓抛模型
1.异常处理的两个过程:
过程一:“抛”:程序在正常执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,一但抛出对象以
后,其后的代码就不再执行
关于异常对象的产生:
1.系统自动生成的异常对象
2.手动地生成一个异常对象,并抛出(throw)
过程二 :“抓”:可以理解为异常的处理方式:1)try...catch...finally 2)throws
2.异常处理方式详解:
①异常处理的方式一:try...catch...finally的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}...
}finally{
//一定会执行的代码
}
或
finally代码块
格式:
Try{
可能产生异常的代码
}Catch(定义一个异常的变量,用于接收try中抛出的异常对象){
异常的处理逻辑,异常对象出现后怎怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}
...
catch (异常类名 变量名){
}finally{
无论是否出现异常都会执行
}
或
Try...Catch异常处理,异常处理的第二种方法,自己处理异常
格式:
Try{
可能产生异常的代码
}Catch(定义一个异常的变量,用于接收try中抛出的异常对象){
异常的处理逻辑,异常对象出现后怎怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}
说明:
1.finally是可选的。finally中声明的是一定会被执行的代码,即使catch中又出现异常了,try或catch中有return语句等情况,如果finally有return语句,永远返回finally中的结果,避免这种情况。finally不能单独使用,必须和try一起使用。finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)。
2.使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应的异常类对象,根据此对象的类型,去catch中进行匹配,即try中可能会抛出多个异常对象,那么可以使用多个catch来处理这些异常对象
3.一旦try中的异常对象匹配到某一个catch时,就接入catch中进行处理,一旦处理完成就跳出当前的try...catch结构(在没有写finally的情况下),继续执行之后的代码
4.catch中的异常类型若没有子父类关系,则声明先后顺序互不影响;若有子父类关系,则要求子类一定声明在父类的前面,否则报错
5.catch中常用的方法:
{
String getMessage();--输出错误原因
printStackTrace();--打印堆栈信息
}
6.在try中定义的(声明的)变量,在出了try结构以后,就不能再被调用
7.try...catch...finally结构可以相互嵌套使用try...catch...finally处理编译时异常,使得程序编译时就不再报错,但在运行时可能报错。相当于使用try...catch...finally结构将一个编译时可能出现的异常,延迟到运行时出现开发中由于运行时异常较为常见,所以通常不针对运行时异常编写try...catch...finally;针对于编译时异常,一定要考虑异常处理
8开发中如何选择使用try...catch...finally还是使用throws:
A.如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try...catch...finally处理。
B.执行的方法a中,先后又调用了另外几个方法。这几个方法还是递进关系执行的,则建议这几个方法使用throws的方法进行处理,而执行的方法a使用try...catch...finally处理。
9.对于JVM来说其他物理连接,如数据库连接,输入输出流、Socket连接等资源是无法进行回收处理的。因此需要手动进行资源释放,此时资源的释放就需要写在finally中。
10.对于运行时异常不进行try...catch...finally处理
11.多个异常使用捕获处理:1.多个异常分别处理。2.多个异常一次捕获,多次处理。3.多个异常一次捕获一次处理
12一个try多个catch注意:
{
catch里面定义的异常变量,若有子父类关系,则子类的异常变量必须写在上边,否则报错
报错的原因是多态的产生使得子类变量没有被使用而显得多余
ArrayIndexOutOfBoundsException继承了IndexOutOfBoundsException
}
13.若try中产生了异常对象,那么就会执行catch中的异常处理逻辑,执行完毕catch中的逻辑处理,继续执行try...catch的后续代码;若try中没有异常,就不会执行catch中的异常逻辑处理,执行完try中的代码,继续执行try...catch的后续代码。
②异常处理的的方式二:throws + 异常类型
throw关键字:处理异常的第一种方法,交给别人处理
作用:
可以使用throw关键字在指定的方法中抛出指定的异常。当方法内部抛出异常对象的时候,必须处理此异常。可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者(自己不处理,给别人处理),最终交给JNM处理-->中断处理
使用格式:
使用格式:在方法声明时使用
修饰符 返回值类型 方法名(参数列表)throws AAAException,BBBException...{
throw new AAAException("产生原因");
throw new BBBException("产生原因");
...
throw new xxxException("异常产生的原因")
}
注意:
1.throw关键字必须写在方法的内部,而throws关键字必须写在方法声明处,throws后面声明的异常必须是Exception或者是Exception的子类
2.throw关键字后边new的对象必须是Exception或者Exception子类对象
3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
throw关键字后面创建的是RuntimeException或者是RuntimeException子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
throw关键字后面创建的是编译异常(写代码的时候报错),我们必须处理这个异常,要么throw,方法交给调用者处理,最终交给JVM;要么try...catch自己处理
4.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常,如果抛出的多个异常对象有父子关系,则直接声明父类异常即可。
5.调用了一个声明抛出异常的方法,我们必须处理声明的异常
要么继续使用throws声明抛出,方法交给调用者处理,最终交给JVM
要么try...catch自己处理
定义一个方法,获取指定索引处的元素
参数:
int[] arr
int index
以后(工作中)必须对方法传递过来的参数进行合法性校验。若参数不合法,必须使用抛出异常的方式告知方法调用者,传递的参数有问题.
如:{
果arr的值是null
则抛出空指针异常,告知方法调用者“传递的数组的值是null”
对传递过来的参数index进行合法性检验
若index范围不在数组索引范围内,就抛出索引越界异常,
告知方法的调用者“传递的索引超出了数组的使用范围”
}
注意:
NullPointerException是一个空指针异常默认交给JVM处理
1."throws + 异常类型"写在方法的声明处,指明方法执行时,可能会抛出的异常类型;一旦方法体体执行时出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会抛出,异常代码后续代码,就不再运行
2.try...catch...finally:真正地将异常给处理了;throws的方法只是将异常抛给了方法的调用者,并没有将异常处理掉
三、自定义异常类:
java提供的异常类,不够我们使用,我们需要自定义一些异常类
如何自定义异常类:
1.继承于现有的异常结构:RuntimeException(不用显式去处理)、Exception(必须用try...catch..finally处理一下)
2.提供全局常量serialVersionUID(序列号)唯一标识 MyException类
3.提供重载构造器
格式:
public class XXXException extends Exception / RuntimeException{
添加一个空参数的构造方法
添加一个异常信息的构造方法
}
注意:
1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类
2.自定义异常类继承于Exception或RuntimeException
继承Exception:则自定义的异常类就是一个编译期异常,若方法内部抛出了编译期异常,就必须处理这个编译期异常,要么throws,要么try...catch
继承RuntimeException:则自定义的异常类就是一个运行期异常,无需处理,交给虚拟机(中断处理)
throw与throws的区别:
throws VS throw
是声明异常 是抛出异常
与方法名同位置 在方法体内
异常处理的方式 生成一个异常对象
tip: 方法重写的规则之一:
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类。
四、子父类异常:
子父类异常:
1.若父类抛出多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常
2.父类方法没有抛出异常,子类重写父类该方法时也不可能抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
3.子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
注意:
父类异常时什么样,子类异常就什么样
小案例:
Objects类中的静态方法
public static <T> T requireNonNull(T obj){
if(obj==null){
throw new NullPointerException();
return obj;
}
}
自定义异常类一:
public class EcDef extends Exception {
static final long serialVersionUID = 序列号;
public EcDef() {
}
public EcDef(String message) {
super(message);
}
}
自定义异常二:
自定义异常类
public class RegisterException extends /*Exception*/RuntimeException{
添加一个空参数的构造方法
public RegisterException(){
super();
}
添加一个异常信息的构造方法
所有的异常类都会有一个带异常信息的构造方法,
方法内部会调用父类异常信息的构造方法,
让父类来处理异常信息
public RegisterException(String message){
super(message);
}
}
常见易混淆概念:
final、finally、finalize三者区别:分别解释三者意义,其中finalize是一个垃圾回收前必须执行的方法。try...catch...finally中finally的使用
类似:
throw和throws
Collection、Collections
Sting、StringBuffer、StringBuilder
ArrayList、linkList
HashMap、LinkedHashMap
重写、重载
结构不相似的:
抽象类、接口
==、equals()
sleep、wait()
以上是今日小节,不喜勿喷,感谢理解