1 什么是异常
-
宏观 : 程序在编译和运行时出现的问题(错误)
-
编译异常 :javac 语法问题
-
运行异常: java 逻辑问题 (空指针异常,数组下标越界异常)
-
-
微观: 编码时所谓的异常,实际上是异常对象。
运行时产生了一个异常,实际上就是产生了一个异常对象
-
错误(问题)代表的是一种情况,一种现象,一种表现 。 异常(对象)是对这种现象的描述
2 异常对象的特点
-
有异常对象,就一定会先有异常类
-
异常对象是一个可以被抛出的对象,所有的异常类都要继承一个Throwable父类
-
根据问题的严重程度,将异常(描述)分为两类
-
Error 所有继承Error父类的异常,属于Error异常,表示这个问题有些严重
一般无法再程序运行过程中解决,建议关闭程序,慢慢解决
栈内存溢出 StackOverFlowError
-
Exception 所有Exception父类的异常,属于Exception异常,表示这个问题不太严重
可以在程序运行时给与解决方案,使得程序可以继续执行
空指针异常 NullPointerException
-
-
无论Error还是Exception,都必须是可以抛出的,都继承Throwable
3 抛出异常(对象)
-
异常情况一般都是伴随着判断产生
public void t1(){
int age = scanner.nextInt();
if(age < 0 || age >= 200){
//这就是一种错误的年龄输入情况(您输入的年龄非法)
//此时在程序运行时产生了一个问题(异常情况)
}
}
-
出现问题后,如果解决方案明确,就直接在方法中使用代码解决 , 此时与异常无关
-
如果解决方案不明确(一般是在封装的方法中), 将可以对问题给与描述(产生异常对象),并将其抛给当前方法的调用者,让调用者来处理
3.1 JDK提供的异常
-
所谓的给与异常描述,就是创建异常对象
-
要想创建异常对象,需要先有异常类
-
JDK提供了很多异常描述(异常类)
-
NullPointerException
-
ArrayIndexOutOfBoundsException
-
StackOverflowError
-
FileNotFoundException
-
ClassNotFoundException
-
-
产生异常描述就是new对象
new NullPointerException()
-
使用throw关键字抛出异常对象,抛给当前方法的调用者
public void t1(){
String path = scanner.next();//输入一个文件地址
findFile(path);
}
public void findFile(String path){
//根据调用者传递的文件路径,帮调用者寻找文件
if(path ... ){
//没有找到对应的文件
throw new FileNotFoundException();
//无法执行,不可达的代码
}
}
注意
throw关键字执行的时候,会结束当前方法,为调用者返回一个错误描述(异常对象)
throw关键字的作用与return类似
3.2 自定义异常类
-
我们可以自定义异常,来描述出现的问题
-
自定义异常类 , 继承异常相关的父类(Throwable,Exception , Error)
一般异常的名字就体现了错误的情况,可以在配合注释进一步说明
-
/**
当年龄小于0或大于150时,抛出这样的异常
*/
class AgeOutOfBoundsException extends Exception{}
2.通过有参构造方法,为异常提供更为详细的描述信息(可选)
class AgeOutOfBoundsException extends Exception{
public AgeOutOfBoundsException(){}
public AgeOutOfBoundsException(String msg){
super(msg);
}
}
3.使用throw 抛出异常
public void t1(int age){
if(age <0 || age > 150){
throw AgeOutOfBoundsException("您提供的年龄是"+age+",但他超过了基本的年龄范围,基本年龄范围是[0,150]");
}
}
4 处理异常
-
我们调用其他工具类时,传递了一些信息。
-
工具类在使用我们提供的信息执行功能时出现了问题,不知道该如何处理
-
就会将问题形成一个异常对象描述,并抛给我们(此时我们是调用者)
-
这个时候我们应该如何处理异常呢?
-
异常处理的过程是:要么我们在当前的方法中处理,要么就继续向上抛,抛给我们的调用者处理
-
如果一直都没有方法处理,最终会抛给JVM。JVM会打印错误信息并停止程序
-
过程中如果一个方法无法处理这个异常
-
重新创建一个异常对象,手动throw抛给调用者
-
不做任何处理,会自动抛给调用者
-
-
-
我们如何处理异常?
-
在方法中,使用try{}包裹可能产生异常对象的代码
运行时一旦产生了异常对象,try就会对其进行捕获
会将异常对象交给对应的catch来处理
-
public void t1(int age){
if(age < 0 || age > 150){
throw new AgeOutOfBoundsException();
}
}
//----------------------------
public void t2(){
int age =scanner.nextInt();
try{
t1(age);
}
}
2.在try{}代码块后面跟着catch{}来处理指定的异常问题
在try的后面跟多个catch,来处理不同的异常情况
public void t2(){
int age =scanner.nextInt();
try{
t1(age);
。。。。
}catch(NullPointerException e){
//根据空指针异常情况来处理异常
}catch(ArrayIndexOutOfBoundsException e){
//根据数组下标越界异常情况来处理异常
}catch(AgeOutOfboundsException e){
//根据年龄越界异常情况处理异常
}
}
注意:如果多个catch处理的异常对象存在继承关系
要求对父类异常情况的处理必须在catch、列表的后面
try{
}catch(NullPointerException e){
}catch(Exception e){
}
3.在try和catch的后面,可以跟有finally{}块,作为try和catch的统一出口
如果有一段代码,在try中也要执行,在catch也要执行,就可以将其写在finally中。
try{
a
b
c
}catch(){
a1
a2
a3
dddd
}finally{
dddd
}
注意 try-catch-finally组合
-
try-catch-finally可以通过使用,按照上述的顺序
-
try的后面可以没有catch,但需要紧跟finally
try{
}finally{}
-
try-catch不能单独存在,catch必须跟在try的后面
-
一旦有finally,无论执行try,还是执行catch,finally中代码一定执行。哪怕try和catch中有return
try{
return 10 ;
}catch(){
return 20 ;
}finally{
System.out.println("--------------------------");
}
如果try,catch和finally中都有return,最终返回的finally中的值。
try{
return 10 ;
}catch(){
return 20 ;
}finally{
return ;
}
5 受检异常与非受检异常
-
所有继承RuntimeException的类,都称为非受检异常
在编码时,对可能产生异常的代码不必须提前做处理或声明,但可以做处理或声明
-
其余的异常类,都称为受检异常,要求在编码的时候:
-
如果方法能处理异常,必须对可能产生异常的代码提前做try-catch处理
-
如果方法不能处理异常,会抛给调用者,由调用者处理,就需要调用者提前try-catch处理,当前方法需要使用throws关键字声明(告诉调用者),当前方法有可能产生一个无法处理的受检异常,你的try-catch
-
FileNotFoundException就是一个受检异常
-
public void t1(){
t3();
try{
t2();
}catch(FileNotFoundException e){
}
}
public void t2()throws FileNotFoundException{
new FileInputStream("c:/z/1.txt");
}
public void t3(){
try{
//这可能会抛出一个FileNotFoundException 受检异常
}catch(FileNotFoundException e){}
}
注意
-
一个方法可以使用throws声明多个受检异常,使用逗号隔开
public void t1()throws Exception1 , Exception2 , ...{}