1、异常是在运行时期发生的不正常情况。java通过面向对象思想,用类的形式将问题封装成对象,用异常类对其进行描述,最终实现将正常流程代码和问题处理代码分离,提高阅读性
2、异常体系(Throwable):
(1)Error:错误,是由JVM抛出的严重性问题(已经影响到程序的执行),一般不可处理,直接修改程序
(2)Exception:异常,可以处理
注:无论是错误还是异常,他们都是问题,问题发生就应该可以抛出(具备可抛性),让调用者知道,并可以处理。所以形成了一个共性的父类Throwable(可抛出的)
说明:
(1)Error:不编写代码,JVM不正常,是错误,不是异常
(2)RuntimeException及其子类:不编写try...catch...处理代码,也无需throws声明,出现异常就让程序停掉(运行时异常RuntimeException较为常见)
(3)Exception的其他子类:需要编写处理代码,调用者调用时需要对throws声明的问题预先定义处理方式
3、出现异常时,不能return(return是函数正常结束后的返回值),要将问题封装成对象抛出。但抛出有个前提,就是对象必须具备可抛性
4、异常体系的特点:Throwable及其所有子类都具备可抛性。通过两个关键字来体现:throw、throws。凡是可以被throw、throws这两个关键字所操作的类和对象,都具备可抛性
5、异常的处理,需要明确两个问题:
(1)为什么要抛出
(2)什么时候抛出
6、处理异常的步骤:
(1)遇到识别的问题进行对象的封装
(2)将问题抛给调用者。如果调用者没有处理,就继续向上抛(谁调用就抛给谁),最后到JVM处停下
7、异常的发生可以结束函数,异常后面的语句都不会执行(如果try代码块的某条语句引起了异常,位于该语句之后的代码不会被执行)
8、异常一般都带消息,且消息可以自定义
9、自定义异常:
(1)如果让一个类成为异常类,必须要继承异常体系(要么继承Exception,要么继承RuntimeException)。因为只有成为异常体系的子类,才有资格具备可抛性,才可以被两个关键字(throw、throws)所操作
(2)自定义异常通常要提供两个构造器:空参的和带一个字符串参数String的构造器( XxxException(String msg) { super(msg); } )
注:异常本身没有多少信息,一般方法都是继承父类的。只有构造函数,但构造函数也访问父类
10、java编译器通常会先检查语法错误,之后才会检查基本的逻辑错误。如果看到编译提示的是异常的问题,eg:“未报告的异常xxx”,此时语法错误已经检查到最后了。即异常是最后一个报错误的
11、编译的问题:
(1)如果函数内发生了异常,eg: throw new XxxException(),就需要在函数上进行声明throws,否则编译失败
(2)调用了发生异常的函数(函数上声明了异常:throws XxxException),就必须要处理,不处理一样失败。处理的方式之一就是抛出,最后抛给JVM(也可以捕获)
注:声明用的是throws,在函数上用throws关键字对该类进行声明,以便于调用者对该问题预先定义处理方式
12、throws和throw的区别:
(1)throws使用在函数上
throw使用在函数内
(2)throws抛出的是异常类,可以抛出多个,用逗号隔开
throw抛出的是异常对象(new XxxException()),一次只能抛一个
说明:一个函数功能可以有多个异常,但只能引发一个。只要有一个异常被引发,这个函数的功能就结束了(出栈),跳转到上面的函数中找处理方式。所以throw new XxxException() 后面不要写其他语句
13、异常处理的捕获形式:
对异常进行针对性处理的方式,一般是try...catch...finally。try是检测(不需要检测的代码不要放在try中),catch是捕获(没catch没处理),finally是必须执行的代码
格式如下:
try {
//需要被检测异常的代码
} catch(异常类 变量) {
//处理异常的代码
} finally {
//一定会被执行的代码
}
注:若程序中显示的声明了某个异常,则抛出异常时不会显示出处。若程序中没有显示的声明某个异常,当抛出异常时,系统会显示异常的出处?
14、何时捕获(try),何时抛出(throws)?
在建立功能时发生了问题,问题如果处理的了,就try...catch...,如果处理不了,就抛出
15、方法声明中抛出(throws)的是什么类型的异常,catch( ... )中就接收什么类型的异常 -- 处理问题要有针对性
16、多catch情况:
(1)如果一个函数声明了多个异常,在调用该功能时,都要进行针对性处理,就会有一个try对应多个catch的情况
(2)多catch时,父类的catch放在最下面,否则编译失败。但一般抛出几个就处理几个,不要做多余的动作。如果真的出现了其他异常,就让程序停下,有问题,解决问题。预先处理等于隐藏问题
17、finally代码块:
(1)finally代码块的作用:通常用于关闭物理资源。凡是有资源要关闭的,都放在finally代码块里,如数据库连接、网络连接、磁盘文件等
(2)通常情况下,不要在finally代码块中使用return、throw等导致方法终止的语句。因为一旦在finally代码块中使用了return、throw等语句,会导致try和catch代码块中的return、throw语句失效
(3)无论异常是否发生,finally代码块中的内容都会被执行。除非遇到一种情况,就是在catch中出现System.exit(0);,即退出JVM。执行到System.exit(0);时,后面的所有代码都不会执行
class Test {
public int testFinal() {
int x = 1;
try {
return ++x;
} catch (Exception e) {
} finally {
++x;
}
return x;
}
public static void main(String[] args) {
Test test = new Test();
int result = test.testFinal();
System.out.println(result); //2
}
}
说明:
a). 先执行try中的return还是finally?
先执行try中return后面的++x,再执行finally,最后执行try中的return
b). 为什么返回结果是2不是3 ?
如果try语句中有return,代码执行如下:如果有返回值,就把返回值保存到局部变量中。执行完finally语句后,返回之前保存在局部变量表里的值
c). 何时返回值为3 ?
如果finally中也有return语句,返回值为3。因为当try和finally中都有return时,会忽略try中的return,而使用finally中的return结束函数
(4)如果try中发生的异常被catch捕获并处理,则try...catch...finall...之后的代码将继续执行。但如果catch中出现return;,finally代码块中的内容会被执行,但try...catch...finall...之后的代码不会执行,因为return;将函数结束了
注:一旦将问题解决掉,程序可以继续向下执行
(5)尽量不要将catch和finally一起使用,因为会影响程序的可读性。最好的做法是:用try/catch嵌套,catch用来捕获异常,finally用来关闭资源
try {
try {
} finally {
// 一些关闭资源的操作
}
} catch (XxxException e) {
}
18、try...catch...finally...代码块常见的组合:
(1)try...catch...finally...:常见组合体
(2)try...catch(多个)...:当没有必要资源需要释放时,可以不定义finally
(3)try...finally...:异常无法直接catch处理,留给调用者处理,但是资源需要关闭(此时需在函数上声明异常throws -- 没catch没处理)
注意:只有try代码块是必须的,catch、finally至少出现其中之一,也可同时出现。即 try代码块不能单独存在
19、异常处理的原则:
(1)函数内部如果抛出需要检测的异常(throw new XxxException()),那么函数上必须要用throws声明,否则必须在函数内用try...catch...捕获,否则编译失败
(2)如果调用到了声明异常的函数,要么try...catch...,要么throws,否则编译失败
(3)什么时候catch,什么时候throws?
功能内部可以解决,用catch;解决不了,用throws告诉调用者,由调用者解决
(4)一个功能如果抛出了多个异常,调用时必须对应多个catch进行针对性处理。内部有几个需要检测的异常,就抛几个异常。抛出几个,就catch几个
20、获取异常信息的方法(Throwable类中的方法):
(1)getMessage():返回此Throwable的详细消息字符串,即为异常构造函数中设置的值
(2)toString():异常对象建立自己独特的字符串表现形式,即 类名 : getLocalizedMessage()
注:在输出语句中打印对象,默认调用toString()方法。不论放入什么对象,输出语句输出的都是字符串
(3)printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。返回 异常名字+异常信息+异常位置,此方法是JVM默认的异常处理机制(打印异常的跟踪栈信息,并终止程序运行)。返回值类型是void,所以不能放在System.out.println(); 中
说明:要对捕获的异常进行适当的处理,而不是简单地将异常的跟踪栈信息打印出来(异常处理、重新抛出新异常、在合适的层处理新异常)
注:异常通常放在日志文件里,通过第三方工具来完成,eg:log4j ...
21、异常转换
在catch处理中,将熟悉的问题进行解决,将解决不了的问题进行封装并作为新的异常抛出,在方法上用throws声明新的异常,告知调用者详细情况。
捕获一个异常,然后接着抛出另一个异常,并把原始异常信息保存下来,是一种典型的链式处理(23种设计模式之一 -- 责任链模式),也被称为“异常链”
try{
computer.run();
//如果上面computer.run();发生异常,此句不执行
System.out.println(name+"讲课");
}catch (MaoYanException e){
System.out.println(e.toString());
test();
//异常转换
// throw new NoPlanException("课程进度无法完成"+e.getMessage());
NoPlanException noPlanException = new NoPlanException("课程进度无法完成");
//新的异常中包含原始异常的所有信息 -- 异常链
noPlanException.initCause(e);
throw noPlanException;
//有时也可直接将原始异常e传入 -- Throwable类有一个构造函数:Throwable(Throwable cause):cause代表详细消息
// throw new RuntimeException(e);
}
新的异常中包含原始异常的所有信息 -- 异常链
noPlanException.initCause(e);
throw noPlanException;
//有时也可直接将原始异常e传入 -- Throwable类有一个构造函数:Throwable(Throwable cause):cause代表详细消息
// throw new RuntimeException(e);
}
22、异常的注意事项:
(1)子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类(子类不能出现和父类不一样的问题,所以子类异常需要在父类异常的范围之内) -- 多态时编译看左边,运行看右边
(2)如果父类抛出多个异常,那么子类只能抛出父类异常的子集
总结:子类覆盖父类,只能抛出父类的异常或者子类或者子集,也可不抛
注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛。如果子类方法中使用到了抛出异常的方法,就只能try,否则报错
(3)throw new XxxException(); 语句后不要写任何语句,因为要跳转,下面的语句执行不到
(4)不要使用过于庞大的try代码块。把大块的try代码块分割成多个可能出现异常的程序段落,并把它们放在单独的try代码块中,从而分别捕获并处理异常
(5)不要过度使用异常。正常的业务逻辑判断不要使用异常机制,异常处理的嵌套通常没有必要使用超过两层(用逻辑判断合理规避,同时辅助try...catch...处理)
(6)catch捕获异常后,不要只写e.printStackTrace(),要提供一些文字信息,如:异常所在的类、方法、何种异常等,记录在日志文件中
(7)不要在构造函数中抛出异常
(8)throw不能单独使用,要么与try...catch...配套使用,要么与throws配套使用
23、何时使用异常:
(1)在恰当的级别处理问题 -- 在知道该如何处理异常的情况下才捕获异常
(2)解决问题并且重新调用产生异常的方法
(3)进行少许修补,然后绕过异常发生的地方继续执行
(4)用别的数据进行计算,以代替方法预计会返回的值
(5)把当前运行环境下能做的事情尽量做完,然后把新的异常抛到更高层
(6)终止程序
(7)进行简化
(8)让类库和程序更加安全
24、异常一共包含四部分信息:所属的线程、异常名称、异常信息、异常位置
25、总结:
(1)在函数上用throws声明异常,目的是让调用者处理问题。所以,调用throws声明的方法,要预先给出处理方式(编译时检测异常)
(2)运行时异常RuntimeException及其子类,不需要声明throws,也不需要try...catch...,出现问题时给JVM,程序直接挂掉
(3)方法内有问题(throw new XxxException()),在方法上用throws声明。调用时再处理try...catch...(调用时,可以部分try...catch...,部分再向外抛出:方法内throw new XxxException() + 方法上throws -- 异常转换)
(4)try代码块中异常后可以写其他语句,但异常发生时,引发异常的语句后面的语句不会再执行。throw new XxxException(); 后不要写其他语句,要跳转,下面的语句执行不到。异常被捕获并处理后,try...catch...finally...后面的语句可以被继续执行
26、异常转换Demo地址
https://pan.baidu.com/s/1e8QJcsFY_YT2MI-NH-PzPQ 密码:sbe2