JAVA异常处理

  2017-7-11 


JAVA异常处理的设计思路

java的设计思想里是尽量在代码运行前就手动检查代码(try、throw等工具)所有可能错误,也就是检查异常

(区分编译错误,那个由程序编译时自动检查,是不一样的东西)

但是有些异常是没法在运行前检查的,如RuntimeException(比如其子类异常:除0错,越界错),所以没办法,才是非检查异常(且这些错误全部都检查的话程序也太臃肿了)(Error也属于非检查异常)非检查异常属于JVM会自动默认直接抛出,不处理的

不过RuntimeException也可以自己手动检查异常,一切语句都可以自己设置检查运行时错误(一切语句运行起来的时候都是运行时),而其它检查异常就不能随便乱抛了,得满足该异常条件

0

Error和Exception的区别

Error:由 JVM 所侦测到的无法预期的错误,由于这是属于 JVM 层次的严重错误,导致 JVM 无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息(如系统崩溃,内存不足,堆栈溢出等)

Exception可以在程序运行中进行捕获并处理,继续执行


JAVA异常操作分两个环节:抛出异常和处理异常

抛出异常

抛出异常有两种方式:

代码设计者手动抛出异常

使用throw Throwable对象来手动抛出异常

throw new RuntimeException();

JVM自动捕获到异常并抛出

将需要JVM自动捕获抛出异常的代码写在try块里

处理(抛出的)异常

异常链

无论是手动抛出,还是自动抛出,抛出的【异常对象都会自动抛向方法调用者】,当方法调用者即为主程序且不catch处理,那么程序就终止,并打印异常信息;若被处理,则按try catch处理异常

举个例子,方法B抛出异常,如果是主程序直接调用该方法B,则直接抛出,程序终止,打印异常信息(假定未catch处理);如果是方法A调用抛出异常的方法B,那该异常就抛给A,接着A向上抛,如果有try catch则直接按照对应方式处理,没有就程序终止,打印异常信息

抛出的检查异常要么用throws声明,要么用try catch捕获,非检查异常(ErrorRuntimeException)不用非检查异常本质是所有方法都默认throws RuntimeException了,Error同)

(以下代码也包含抛出异常部分)

异常抛出后,可以直接向上抛(如果还能)不处理(但要throws声明);也可以用catch处理。(如果是JVM自动抛出的异常则不能用throws声明)

throws声明:只标识该方法可能抛出的异常列表,不处理

throws声明方式

class test
{
    void method() throws Exception  
    {
        throw new Exception();  //手动抛出异常
    }
}

异常链(还有上层调用者,则继续向上抛出):

class newTest
{
    void fatherMethod() throws Exception //异常对象通过异常链传递到该方法后,该方法的父类调用者已是主程序,且无catch处理,则程序终止,打印异常信息
    {
        method();
    }
    void method() throws Exception  
    {
        throw new Exception();
    }
}

上层调用者也可以是catch方式

class newTest
{
    void fatherMethod()
    {
        try{
            method();
        }catch (Exception e){
            System.out.println("father knows it");
        }
    }
    void method() throws Exception
    {
        throw new Exception();
    }

}

catch:处理抛出的异常

自动捕获的异常只能通过try catch来处理

void testMethod()
{
    try{
       method();  //try代码块,在该代码块里由JVM自动抛出异常
    }catch (Exception e){  //catch捕获try代码块中抛出的异常对象
       System.out.println("i know it"); //如果捕获到异常对象,处理代码
    }    
}

catch也可以同时捕获多个异常写多个catch即可

JVM自动捕获异常后再手动抛出封装异常

try catch结合手动throw

有时我们会从 catch 中手动抛出一个异常,目的是为了改变异常的类型(比如抛出一个我自己定义的异常MyException)。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节

class MyException extends Exception //自定义异常
{
    String name;
    Exception e;
    MyException(String name)
    {
        this.name = name;
    }
    void initCause(Exception e)  //传入导致错误的异常对象
    {
        this.e = e;
    }
    //other functton
}

class test
{
    void testMethod() throws MyException
    {
        try{
			//doSth
        }catch(RuntimeException e)   //这里捕获到了RuntimeException后,手动抛出我自己封装好的Exception类
        {
            MyException ex = new MyException("xxx is wrong!");
            ex.initCause(e);
            throw ex;
        }
    }
}

自定义异常

见上面JVM自动捕获异常后再手动抛出封装异常部分的例子

MORE

原理1:JVM如何捕获到异常并抛出?

①当一个方法中发生异常时,这个方法会创建该异常的一个异常对象(即try块,自动捕获异常)

②也可以手动自己创建一个异常对象(throw)

将异常对象交给JVM的过程就叫抛出异常

原理2:JVM是如何处理异常的?

JVM 会顺着调用栈去查找看是否有可以处理异常的代码(catch)(也就构成了前面所说的异常链)

如果有,JVM将该异常对象传递给它,由处理代码块进行处理;

如果到了栈底还没有(即前面所说的上层调用者已是主程序),JVM 就会将该异常转交给默认的异常处理器,该默认异常处理器打印出异常信息并终止应用程序。

NoClassDefFoundError 和 ClassNotFoundException

NoClassDefFoundError 是Error,发生在加载某类时在内存中找不到该类的定义

ClassNotFoundException是检查异常,使用反射的时候,通过传入的类路径参数没有找到该类

异常不要拿来做流程控制

finally,总有意想不到的惊喜等着你。。。

异常就是单纯处理异常的

关于return 和 finally

return和finally执行顺序问题:Java finally语句到底是在return之前还是之后执行?

给出结论:

1.finally语句是在try的return语句执行之后,return返回之前执行(return的语句执行后不返回,再继续执行finally中的语句,finally执行完后再返回)

2.finally块中的return语句会覆盖try块中的return返回。

3.inally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值

以及一些finally语句执行问题的结论:

(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。

(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。


且听风吟