1.异常简介
所有异常都继承于Throwable类,其下有两大子类:
a.Error类:错误,一般编程人员不太接触,如虚拟机错误、线程死锁。硬伤:使程序崩溃。
b.Exception类:异常,编码、环境、用户输入等问题,其子类主要有:
检查异常需要手动添加捕获和处理语句。
2.try-catch以及try-catch-finally
try块:负责捕获异常,一旦try中发现异常,抛出异常的方法会终止执行,程序的控制权将被移交给catch块中的异常处理程序。【try语句块不可以独立存在,必须与 catch 或者 finally 块同存】
catch块:如何处理?比如发出警告:提示、检查配置、网络连接,记录错误等。执行完catch块之后程序跳出catch块,继续执行后面的代码。
finally:最终执行的代码,用于关闭和释放资源等。
终止执行,交由异常处理程序(抛出提醒或记录日志等),异常代码块外代码正常执行:
如果有很多种类型的异常,可以使用多个catch块去解决:
编写catch块的注意事项:多个catch块处理的异常类,要按照先catch子类后catch父类的处理方式,因为针对父类的异常处理程序对于子类也是适用的,会【就近处理】异常(由上自下):
注意点:假设try中有两条语句,第一条语句出现错误转到catch块,那么执行完catch块后try中的第二条语句是不会执行了的:
3.通过案例学习try…catch…final
package firstTry;
public class test {
public static void main(String[] args) {
test smallTest = new test();
int result = smallTest.test();
System.out.println("test()方法执行完毕,返回值为:" + result);
}
public int test() {
int divider = 10;
int result = 100;
try {
while(divider > -1) {
divider--;
result = result + 100 / divider;
}
return result;
}catch(Exception e) {
e.printStackTrace();
System.out.println("循环抛出异常了!!!");
return -1;
}
}
}
运行结果:
printStackTrace()方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。
package firstTry;
public class test {
public static void main(String[] args) {
test smallTest = new test();
int result = smallTest.test();
System.out.println("test()方法执行完毕,返回值为:" + result);
}
public int test() {
int divider = 10;
int result = 100;
try {
while(divider > -1) {
divider--;
result = result + 100 / divider;
}
return result;
}catch(Exception e) {
e.printStackTrace();
System.out.println("循环抛出异常了!!!");
}finally {
System.out.println("finally运行完毕,result:" + result);
}
return 1111;
}
}
运行结果为:
最后讨论一下有return情况下try…catch…finally的执行顺序:
有return的情况下try catch finally的执行顺序(最有说服力的总结)
4.Java中的异常抛出以及自定义异常
throws-声明将要抛出何种类型的异常(声明)
public void 方法名(参数列表)
throws 异常列表{
//调用会抛出异常的方法或者:
throw new Exception();
}
throw写在方法体里面,表明的是具体的抛出异常这个动作。
如果某个方法调用到了会抛出异常的方法,那么必须调用try-catch语句去捕获这个异常,或者添加throw声明,来将异常调用给更上一层的方法进行处理。
自定义异常类:当实际应用中需要用到Java中没有的异常,就需要自定义异常。
自定义异常需要继承于Java类库中意思相近的异常,或者直接继承Exception。
如:
5.Java中的异常链
只有Error,Exception,RuntimeException提供了带cause参数的构造器,其他的所有异常类只能通过initCause()来设置cause。
所有Throwable的子类构造器中都可以接受一个cause对象作为参数。cause是异常原由,代表着原始异常。既可以在当前位置创建并抛出行的异常,也可以通过cause追踪到异常最初发生的位置。
异常链是一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为新异常的一个属性(比如cause)。这个想法是指一个方法应该抛出定义在相同的抽象层次上的异常,但不会丢弃更低层次的信息。
把捕获的异常包装成新的异常,在新异常里添加原始的异常,并将新异常抛出,它们就像是链式反应一样,一个导致(cause)另一个,这个想法是指一个方法应该抛出定义在相同的抽象层次上的异常,(将所有捕获到的异常包装为新的异常类,即定义在相同的抽象层次上抛出)但不会丢弃更低层次的信息。
下面来看一个例子:
package demo;
public class DrunkException extends Exception{
public DrunkException() { //无参构造函数
}
public DrunkException(String message) { //有参构造函数
super(message);
}
}
package demo;
public class ChainTest {
public static void main(String[] args) {
ChainTest ct = new ChainTest();
try {
ct.Test2();
}catch (Exception e) {
e.printStackTrace();
}
}
public void Test1() throws DrunkException{
throw new DrunkException("喝车别开酒");
}
public void Test2()/*throws RuntimeException这句写不写都行*/ {
try{
Test1();
}catch( DrunkException e){
RuntimeException newEtc = new RuntimeException("司机一滴酒,亲人两行泪");
newEtc.initCause(e);
throw newEtc;
}
}
}
输出结果:
几点解释:
a.为什么throws RuntimeException这句写不写都行:在Java的异常类体系中,Error和RuntimeException是非检查型异常,其他的都是检查型异常。所有方法都可以在不声明throws的情况下抛出RuntimeException及其子类 不可以在不声明的情况下抛出非RuntimeException。
b. initCause(e)方法的作用是什么:
nitCause()这个方法就是对异常来进行包装的,目的就是为了出了问题的时候能够追根究底。因为一个项目,越往底层,可能抛出的异常类型会用很多,如果你在上层想要处理这些异常,你就需要挨个的写很多catch语句块来捕捉异常,这样是很麻烦的。如果我们对底层抛出的异常捕获后,抛出一个新的统一的异常,会避免这个问题。但是直接抛出一个新的异常,会让最原始的异常信息丢失,这样不利于排查问题。而包装成一个新异常后,调用initCause()方法,再将其抛出给更上一层的调用者处理,它的作用你相当于保存了原始的异常信息。其实这相当于形成一个异常链,可以很清楚地显示出原始的异常出处。否则可能会丢失原始的异常信息。
Test2()方法简便写法:
package demo;
public class ChainTest {
public static void main(String[] args) {
ChainTest ct = new ChainTest();
try {
ct.Test2();
}catch (Exception e) {
e.printStackTrace();
}
}
public void Test1() throws DrunkException{
throw new DrunkException("喝车别开酒");
}
public void Test2() /*throws RuntimeException这句写不写都行*/ {
try{
Test1();
}catch( DrunkException e){
RuntimeException newEtc = new RuntimeException(e);
//newEtc.initCause(e);
throw newEtc;
}
}
}
运行结果:
6.经验总结:
a.处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
b.在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
c.对于不确定的代码,也可以加上try-catch,处理潜在的异常
d.尽量去处理异常,切记只是简单的调用printStackTrace()去打印
e.具体如何处理异常,要根据不同的业务需求和异常类型去决定
f.尽量添加finally语句块去释放占用的资源