16-异常

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;将函数结束了

注:一旦将问题解决掉,程序可以继续向下执行

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);
        }

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


猜你喜欢

转载自blog.csdn.net/ruyu00/article/details/79920301