Java中的异常报告和处理机制 && Java中的异常栈轨迹和异常链

泄露秘密 提交于 2020-04-07 04:15:29

参考文献:

https://www.cnblogs.com/zhangdaicong/p/6127984.html

 

为了应对运行期间可能出现的错误,提高程序的的稳健性,Java中定义了强大的异常处理机制。

Java的异常机制在增强程序稳健性的同时(围绕异常声明,在编译期间就具有严格的异常制度),增强了业务代码的逻辑性和连贯性(通过try-catch,避免了C等语言下的业务逻辑代码和异常检测代码严重混合)。

异常声明:Java要求能抛出异常的方法必须加上异常声明(该些异常未被处理,且非Java中定义的标准异常)。

Example:

1 package demo;
 2 import static net.mindview.util.Print.*;
 3 
 4 public class TestException {
 5     TestException(int i) throws ExceptionA,ExceptionB{
 6         switch(i)
 7         {
 8         case 1 : throw new ExceptionA();
 9         case 2 : throw new ExceptionB();
10         default : {int a[]={0,}; a[1]=1;};
11         }
12     }
13 }
14 class ExceptionA extends Exception{};
15 class ExceptionB extends Exception{};
1 package demo;
 2 
 3 import net.mindview.*;
 4 import net.mindview.util.PackageTest;
 5 
 6 import java.util.Arrays;
 7 import java.util.Random;
 8 
 9 import static net.mindview.util.Print.*;
10 
11 import static net.mindview.util.Range.*;
12 
13 import trypackage.*;
14 
15 public class printtry {
16 
17     /**
18      * @param args
19      */
20     public static void main(String[] args) {
21         for(int i=0;i<=3;i++)
22         {
23             try{
24                 print("Test:"+Integer.toString(i));
25                 TestException testException=new TestException(i);
26                 }
27                 catch(ExceptionA e)
28                 {
29                     print("Catch the exception from:A cathcing block");
30                     e.printStackTrace();
31                 }
32                 catch(ExceptionB e)
33                 {
34                     print("Catch the exception from:B catching block");
35                     e.printStackTrace();
36                 }
37                 catch(Exception e)
38                 {
39                     print("Catch the exception from:others catching block ");
40                     e.printStackTrace();
41     
42                 }
43                 finally
44                 {
45                     print("The finally block must be executed!");
46                 }
47         }
48         
49 
50     }
51 
52 }

运行结果:

从Example 中可以发现:

1、异常声明极其重要性

异常声明:即用户必须将函数可能抛出的所有自定义异常(即非标准异常),添加到函数声明中。

TestException(int i) throws ExceptionA,ExceptionB 方法(为TestException类的构造方法),由于其函数体中会抛出ExceptionA和ExceptionB两类用户自定义的异常,故必须在其函数声明中添加异常声明:throws ExceptionA,ExceptionB,否则Java无法让该文件编译通过。

你可能会注意到:default : {int a[]={0,}; a[1]=1;};该语句会产生明显的数组访问下标越界异常,但是却未将其添加到函数的异常声明中。这是因为此异常属于Java中自定义的非标准异常,其会自动的被逐层上报给main()函数(若上报的过程中未被try-catch处理)。

Java为什么要求异常声明?

这正是Java相比于C等语言的独特之处,Java的异常声明给编译器提供了一个非常重要的信息,即:该方法会产生哪些异常

一旦编译器可以确定方法体会产生的异常,那么当该方法被置于try块中时,编译器就会强制的要求所有的异常都要能被catch处理

若以上的的程序改为:

+ View Code

该程序将无法被编译通过,因为编译器通过方法的异常声明TestException(int i) throws ExceptionA,ExceptionB已经得知:TestException会抛出ExceptionA和ExceptionB两类异常,而此处的catch模块只给出了catch(ExceptionA e),即ExceptionB被没有被处理。这也正是Java竭力设计出异常机制,以保证程序健壮性的原因。

注意:Java此处只是提供了一种检查机制(提醒机制),至于异常能否被处理或者能否被妥善的处理,完全由程序员所决定。程序员也许为了编译让的通过会写出:

+ View Code

此处虽然catch了ExceptionB,并且保证了程序能够顺利的编译通过,但是程序员却对此异常什么也没做!

看上去好像并没有起到处理异常的作用? 确实如此,程序员未给出任何的处理办法,一旦相应的异常产生,无应对措施!

但是这完全是由使用该方法的程序员所造成的,Java已经通过自己的检查机制提醒了程序员需要的异常处理,已经起到了增强程序健壮性的作用!

起码让程序知道了这里有一个必须处理的异常,至于是否处理,如何处理,已经不是编译能够控制的事情。

 

2、异常类的继承和处理机制。

下一个程序却能编译通过:为什么程序中未给出ExceptionB的catch处理,但是仍然能够编译通过。

+ View Code

这是因为添加了catch(Exception a)模块,使得所有的异常都能被catch。这是为什么呢?

因为Exception是所有异常类的基类,包括Java中的常规异常:

+ View Code

catch模块会对try中捕获到的异常对象(一次只可能产生一个异常对象),依次的进行匹配,一旦匹配成功即转入catch块中进行相应的处理。

在匹配的过程中,如果catch声明的为基类异常对象,那么其导出类的异常对象是可以被其正常捕获的,即自动实现了向上的转型

 

Exception类为所有异常类的基类,故编译器认为ExceptionB对象能被catch,所以没有产生编译错误。

重要: catch(Exception e){}又被称为捕获全部异常对象,常常用于放在最后一个catch中,保证所有意料之外的异常对象都能被捕获。

+ View Code

catch(Exception e)虽然能捕获所有异常对象,但是其处理方法却只能有一种,无针对性,对任何意料之外的异常都要适用。所以其处理方法一般为将捕获的异常对象的信息通过e.printStackTrace()方法输出到控制台,以提醒程序员何种意料之外的异常发生了。

机制:对于同一个try块,Java不允许catch(基类)出现在catch(相应导出类)之前,以覆盖了更好的异常处理方法。

 

(二:)

Java中允许对异常进行再次抛出,以提交给上一层进行处理,最为明显的例子为Java的常规异常。

常规异常:有Java所定义的异常,不需要异常声明,在未被try-catch的情况下,会被默认上报到main()方法。

Example:

public class TestException {

    TestException(int i) throws ExceptionA,ExceptionB{

        int a[]={0,}; a[1]=1;

    }

}

当从main()方法中调用TestException类的构造函数的时候,会得到以下的异常提示,从中可以看出异常的冒泡机制(这是一个栈信息)。

异常的冒泡上传机制:当一个异常对象产生了以后,其会按照调用层次(一般是方法的调用层次)进行冒泡,直到被try-catch处理,或上报至main()方法,有编译器进行提示。

Example:

firstThrow()提示错误的原因为, firstThrow()的函数声明中包括了MyException的异常声明,而secondThrow()中调用了firstThrow()却未对可能抛出的异常对象提供任何处理方案,这是编译器所不能允许发生的事情。(这也是为了保证底层异常对象在冒泡过程中,能得到合适的处理和重视!)

注意此截图中,MyEclipse提供了两种方式帮助fix这个程序这两种方式常用的异常应对手段

1、本方法不给予处理,将异常上报给上一层。

1 public class TestExceptionChain {
 2     void firstThrow() throws MyException
 3     {
 4         print("Oringinally creat a MyException and throw it out");
 5         throw new MyException();
 6         
 7     }
 8     void secondThrow() throws MyException
 9     {
10         firstThrow();
11     }
12     TestExceptionChain() throws MyException{
13         secondThrow();
14     }
15 }

复制代码

 

1 public static void main(String[] args) {
 2         try{
 3          
 4             TestExceptionChain testExceptionChain=new TestExceptionChain();
 5         }
 6         catch(MyException e)
 7         {
 8             e.printStackTrace();
 9             print("Catch a my exception!");
10         }
11 
12     }

控制台的输出为:

从异常栈的记录信息可以发现,与代码相对应的异常抛出机制和次序:

firstThrow()产生MyException对象->异常冒泡至调用其的secondThrow()->异常冒泡至调用secondThrow()的TestExceptionChain的构造方法->冒泡至printtry的main()方法。

注意到:异常对象一直被抛出,直至在printtry的mian()方法中被try-catch捕获!

2、try-catch方式,捕捉上报的异常,而后进行相应处理或抛出另一异常。

2、1捕获异常后,进行相应处理。

Example:

1 public class TestExceptionChain {
 2     void firstThrow() throws MyException
 3     {
 4         print("Oringinally creat a MyException and throw it out");
 5         throw new MyException();
 6     }
 7     void secondThrow()
 8     {
 9         try
10         {
11         firstThrow();
12         }
13         catch (MyException e)
14         {
15             print("I have just caught a MyExcepton,but i want to do nothing for it");
16             e.printStackTrace();
17         }
18     }
19     TestExceptionChain(){
20         secondThrow();
21     }

从图中可以发现,异常在secondThrow() 中被try-catch模块捕获,并执行了相应的处理操作,所以其函数声明中无需添加异常声明,异常不会被上报

故mian()方法被改写成了以下的代码:

1 TestExceptionChain testExceptionChain=new TestExceptionChain();

注意此处异常栈的信息,表示的是异常产生的层次信息,并非异常信息的上报层次,因为其已经在secondThorow()中被捕获处理。

2.2 捕获异常后,抛出另一个异常。

Example:

1 public class TestExceptionChain {
 2     void firstThrow() throws MyException
 3     {
 4         print("Oringinally creat a MyException and throw it out");
 5         throw new MyException();
 6     }
 7     void secondThrow() throws YouException
 8     {
 9         try
10         {
11         firstThrow();
12         }
13         catch (MyException e)
14         {
15             print("I have just caught a MyExcepton,but i want to create a YouException and throw it out");
16             e.printStackTrace();
17             throw new YouException();
18         }
19     }
20     TestExceptionChain() throws YouException{
21         secondThrow();
22     }
23 }
24 class MyException extends Exception{}
25 class YouException extends Exception{}

从异常栈信息中可以发现,新抛出的YouException对象是从secondThrow()中开始的。

*Java中还提供了fillInStackTrace()方法,用于对捕获的异常的栈信息进行重写。

Example:

1 try{
 2          
 3             TestExceptionChain testExceptionChain=new TestExceptionChain();
 4         }
 5         catch(YouException e)
 6         {
 7             print("Catch a YouException!");
 8             e.fillInStackTrace();
 9             e.printStackTrace();
10             
11         }

 由于使用了fillInstack()方法,关于YouException的异常信息被重写,从其被从写处重新记录!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!