Java之Exception

转载 2013年12月04日 20:37:33

原文出处:http://blog.csdn.net/zhangerqing

Exception这个东西,程序中必须会有的,尽管我们很不乐意看到它,可是从另一个角度考虑,有异常则说明程序有问题,有助于我们及时改正。有的时候程序出错的原因有很多,比如不合法的输入、类型、空指针甚至内存不足,如果光从软件来看,我们只知道它出问题了,并不清楚问题出在哪儿,给软件排错是个很头疼的事情,因为可能出问题的地方太多了,语法上的问题还好点儿,毕竟能从视觉上看出来,有些逻辑上的问题才是致命的,我们必须从全局出发也许才能找到问题的根源!基于这些,我们需要借助于异常机制。记得刚学习写程序的时候,老师总说在有可能出错的地方,别忘了加入异常处理块,当时我就想,既然是自己写的东西,难道还不知道会不会出错?当时在一段时间内这个问题困扰着我,殊不知程序哪有那么简单,后来自己写得多了才清楚了,异常很重要!本章系Java之美[从菜鸟到高手演变]系列之Exception,通过本章的学习,我们可以基本较为深入的理解Java中异常处理机制。

 一、简介

Java为我们提供了非常完美的异常处理机制,使得我们可以更加专心的去写程序,有的时候遇到需要添加异常处理块的地方,像eclipse会自动提示你,感觉很幸福!我们看看异常处理的一些类的结构组成:

从根部开始分为两大类:Error和Exception。Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。Exception是程序本身可以处理的异常,这种异常分两大类:非运行时异常(发生在编译阶段,又称checkException)和运行时异常(发生在程序运行过程中,又叫uncheckException)。非运行时异常一般就是指一些没有遵守Java语言规范的代码,容易看的出来,并且容易解决的异常,运行时异常是那些在程序运行过程中产生的异常,具有不确定性,如空指针异常等,造成空指针的原因很多,所以运行时异常具有不确定性,往往难以排查,还有就是程序中存在的逻辑错误,光从一段代码中看不出问题,需要纵观全局才能发现的错误,也会造成运行时异常,这就要求我们在写程序时多多注意,尽量处理去处理异常,当异常发生时,希望程序能朝理想的方面运行!

二、异常的类型

一方面我们可以将异常分为受控异常和不受控异常,其实一般来讲,受控异常就是非运行时异常,不受控异常就是运行时异常和Error。另一方面,我们直接将异常分为非运行时异常和运行时异常。

三、异常处理的过程

使用try/catch/finally语句块安装异常处理程序,每个try块中包含可能出现异常的语句,每个catch块中包含处理异常的程序,

[java] view plaincopy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.         String filename = "d:\\test.txt";  
  5.         try {  
  6.             FileReader reader = new FileReader(filename);  
  7.             Scanner in = new Scanner(reader);  
  8.             String input = in.next();  
  9.             int value = Integer.parseInt(input);  
  10.             System.out.println(value);  
  11.         } catch (FileNotFoundException e) {  
  12.             e.printStackTrace();  
  13.         } finally {  
  14.             System.out.println("this is finally block!");  
  15.         }  
  16.     }  
  17. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.         String filename = "d:\\test.txt";  
  5.         try {  
  6.             FileReader reader = new FileReader(filename);  
  7.             Scanner in = new Scanner(reader);  
  8.             String input = in.next();  
  9.             int value = Integer.parseInt(input);  
  10.             System.out.println(value);  
  11.         } catch (FileNotFoundException e) {  
  12.             e.printStackTrace();  
  13.         } finally {  
  14.             System.out.println("this is finally block!");  
  15.         }  
  16.     }  
  17. }  

如果d盘根目录下没有test.txt的话,该程序抛出异常:

this is finally block!
java.io.FileNotFoundException: d:\test.txt (系统找不到指定的文件。)
 at java.io.FileInputStream.open(Native Method)
 at java.io.FileInputStream.<init>(FileInputStream.java:106)
 at java.io.FileInputStream.<init>(FileInputStream.java:66)
 at java.io.FileReader.<init>(FileReader.java:41)
 at Test.main(Test.java:10)

但是finally块中的语句却输出了,这个暂且不谈,先记着,在d盘下新建文件test.txt,并输入内容2232,再来观察下:

输出:

2322
this is finally block!

finally块中的语句依然输出,说明:不论程序有无异常,finally块中的语句都会执行。因此finally块中一般放一些关闭资源的语句。接下来我们继续做实验,我们将test.txt中的2322改成abc,看看结果:

this is finally block!
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test.main(Test.java:13)
该异常中的两处重点我已经标出来了,一处是红色的Exception in thread “main”,表明异常抛出的地方,另一处是java.lang.NumberFormatException: For input string: "abc",表明异常的类型,此处我们看看上面之前的那个结果,为什么没有抛出异常出现的地方,仔细观察源程序,我们发现,程序中我们并没有显式声明NumberFormatException,而FileNotFoundException是我们声明过的,此处我总结一下就是说:1、如果我在程序中声明了某个异常,则抛出异常的时候,不会显示出处,直接抛出。2、如果我没有在程序中声明,那么程序会同时抛出异常的出处。这是为什么?还有,当我没有显式声明的时候,系统会怎么办?这肯定是有一定的规律的,下面我们继续做实验:

[java] view plaincopy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.         String filename = "d:\\test.txt";  
  6.   
  7.         // 进行捕捉异常   
  8.         try {  
  9.             FileReader reader = new FileReader(filename);  
  10.             Scanner in = new Scanner(reader);  
  11.             String input = in.next();  
  12.             int value = Integer.parseInt(input);  
  13.             System.out.println(value);  
  14.         } catch (FileNotFoundException e) { // 捕捉FileNotFoundException  
  15.             e.printStackTrace();  
  16.         } catch (NumberFormatException e) { // NumberFormatException  
  17.             e.printStackTrace(); // 打印异常信息 就是形如:at java.lang.NumberFor...的信息  
  18.             System.out.println("I'm here!");  
  19.         } finally {  
  20.             System.out.println("this is finally block!");  
  21.         }  
  22.     }  
  23. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.         String filename = "d:\\test.txt";  
  6.   
  7.         // 进行捕捉异常  
  8.         try {  
  9.             FileReader reader = new FileReader(filename);  
  10.             Scanner in = new Scanner(reader);  
  11.             String input = in.next();  
  12.             int value = Integer.parseInt(input);  
  13.             System.out.println(value);  
  14.         } catch (FileNotFoundException e) { // 捕捉FileNotFoundException  
  15.             e.printStackTrace();  
  16.         } catch (NumberFormatException e) { // NumberFormatException  
  17.             e.printStackTrace(); // 打印异常信息 就是形如:at java.lang.NumberFor...的信息  
  18.             System.out.println("I'm here!");  
  19.         } finally {  
  20.             System.out.println("this is finally block!");  
  21.         }  
  22.     }  
  23. }  

我加了一个catch块,转么捕获NumberFormatException,则程序输出:

java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test.main(Test.java:14)
I'm here!
this is finally block!

没有输出异常抛出的地方。继续改代码:

[java] view plaincopy
  1. public class Test2 {  
  2.       
  3.     public void open(){  
  4.         String filename = "d:\\test.txt";  
  5.         try {  
  6.             FileReader reader = new FileReader(filename);  
  7.             Scanner in = new Scanner(reader);  
  8.             String input = in.next();  
  9.             int value = Integer.parseInt(input);  
  10.             System.out.println(value);  
  11.         } catch (FileNotFoundException e) {  
  12.             e.printStackTrace();  
  13.             System.out.println("this is test2 block!");  
  14.         }   
  15.     }  
  16. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Test2 {  
  2.       
  3.     public void open(){  
  4.         String filename = "d:\\test.txt";  
  5.         try {  
  6.             FileReader reader = new FileReader(filename);  
  7.             Scanner in = new Scanner(reader);  
  8.             String input = in.next();  
  9.             int value = Integer.parseInt(input);  
  10.             System.out.println(value);  
  11.         } catch (FileNotFoundException e) {  
  12.             e.printStackTrace();  
  13.             System.out.println("this is test2 block!");  
  14.         }   
  15.     }  
  16. }  
[java] view plaincopy
  1. public class Test3 {  
  2.       
  3.     public void carry() {  
  4.         Test2 t2 = new Test2();  
  5.         try {  
  6.             t2.open();  
  7.         } catch (Exception e) {  
  8.             e.printStackTrace();  
  9.             System.out.println("this is test3 block!");  
  10.         }  
  11.     }  
  12. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Test3 {  
  2.       
  3.     public void carry() {  
  4.         Test2 t2 = new Test2();  
  5.         try {  
  6.             t2.open();  
  7.         } catch (Exception e) {  
  8.             e.printStackTrace();  
  9.             System.out.println("this is test3 block!");  
  10.         }  
  11.     }  
  12. }  
[java] view plaincopy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         Test3 t3 = new Test3();  
  6.   
  7.         t3.carry();  
  8.     }  
  9.       
  10. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         Test3 t3 = new Test3();  
  6.   
  7.         t3.carry();  
  8.     }  
  9.       
  10. }  

思路是:Test2类中处理业务,Test3类调用Test2类的open方法,最后在Test类中调用Test3类的carry方法,但是,我将异常抛在Test3中,看看异常输出的结果:

java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test2.open(Test2.java:13)
 at Test3.carry(Test3.java:6)
 at Test.main(Test.java:7)
this is test3 block!

首先,抛出的异常没有地方信息了,其次输出了:this is test3 block!,说明该异常是从Test3类中的carry方法抛出的,当我们把Test3类中的异常捕获语句注释掉的时候,异常如下:

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
 at java.lang.Integer.parseInt(Integer.java:447)
 at java.lang.Integer.parseInt(Integer.java:497)
 at Test2.open(Test2.java:13)
 at Test3.carry(Test3.java:6)
 at Test.main(Test.java:7)

看到此处,我想读者朋友们应该有一定的感觉了,说了这么多,就是想说明一点,当程序处理不了异常的时候会怎么办?是这样的:当前方法如果声明了相应的异常处理器,如上面的程序如果加了catch(NumberFormatException e),则直接抛出,但是如果没有声明,则会找到它的调用者,如果调用者也没有做相应的处理,则会一直往前找,直到找到main方法,最后抛出异常,所以上面的现象不难解释!此处我们简单总结下异常处理的过程:1、在可能出错的方法加上try/catch块语句,来调用异常处理器。2、当异常发生时,直接跳到相应的异常处理器catch中,如果有则抛出异常,执行该catch块中的语句,如果没有,则找到它的调用者,直到main方法。3、如果有finally块,则执行finally块中的语句。

注意:

1、一个try可对应多个catch。2、有try必须至少有一个catch或者finally(此处经网友actt001指正,多谢!)。3、finally块不是必须的,可有可无。4、一般情况下,当异常发生时,会执行catch块中的语句,特殊情况:当main方法中抛出异常时,如果程序声明了该异常处理器,则执行相应的catch块中的语句,如果程序没有声明相应的异常处理器,则不执行catch块中的语句,直接抛出异常!那么,这个异常来源于哪儿?既然main中有try/catch语句(虽然不是对应的异常处理器),为什么没有抛出,说明main方法中的try/catch块根本就没有捕捉到异常,那么系统怎么处理?其实是这样的,这种情况下,异常被直接丢给JVM,而JVM的处理方式就是:直接中断你的程序!就是这么简单。

四、常见异常

NullPointerException 空指针

空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等

ClassNotFoundException  找不到类

找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

ClassCastException   类型转换

ArithmeticException   算数条件

算术条件异常。譬如:整数除零等。

ArrayIndexOutOfBoundsException  数组越界

数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

 

这块内容我们会不断更新,请读者朋友们在阅读的同时,不断提出自己遇到的有意义的异常,不断充实博文,欢迎读者积极补充!

 五、异常和错误

异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是JVM(Java虚拟机)通知你的一种方式,通过这种方式,JVM让你知道,你已经犯了个错误,现在有一个机会来修改它。Java中使用异常类来表示异常,不同的异常类代表了不同的异常。但是在Java中所有的异常都有一个基类,叫做Exception。

错误:它指的是一个合理的应用程序不能截获的严重的问题,大多数都是反常的情况,错误是JVM的一个故障(虽然它可以是任何系统级的服务)。所以,错误是很难处理的,一般的开发人员是无法处理这些错误的,比如内存溢出。

六、Assert(断言)

assert是jdk1.4才开始支持的新功能,主要在开发和测试时开启,为保证性能,在程序正式发布后通常是关闭的。启用断言比较简单,在启动参数里设置-ea或者-enableassertions就可以了.

assert表达式有两种情况:

1)assert exp1 此时的exp1为一个boolean类型的表达式

当其值为true时,运行通过,如果为false,则会抛出一个相应的AssertionError,注意它可以被catch到。

2)assert exp1 : exp2 此时的exp1同上,而exp2可以为基本类型或一个Object对象,当exp1的值为true时,同上,且exp2不会被运算;而当exp1的值为false时,将会抛出AssertionError,同时将exp2的结果作为AssertionError构造器中的参数,当使用catch该错误时,可利用getMessage()方法打印出exp2的结果。

使用断言应该注意:断言只是用来调试程序的工具,不要作为程序的一部分,或者有人用断言来代替try/catch,这些都是不对的,1、这和断言的作用相违背,2、断言在程序发布后,是会被关闭的,如果将它作为程序的一部分,那么当断言被关闭后,程序必然会出问题。3、有更好的方法,如try/catch,为什么还用断言。所以,最好不要讲断言作为程序的一部分,从心里上你可以把它当做可有可无就行了。

七、常见问题

1、finally和return问题

我们平时说:finally中的内容不论程序有无异常,都会被执行,那么如果我们的程序在try和catch块中return了,finally中的还会执行吗?读者可以先猜猜看,分析一下,接下来我们做实验:

[java] view plaincopy
  1. public class FinallyTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         boolean file = open();  
  5.         System.out.println("this is main return value:" + file);  
  6.     }  
  7.   
  8.     public static boolean open() {  
  9.         String filename = "d:\\test.txtp";  
  10.         try {  
  11.             FileReader reader = new FileReader(filename);  
  12.             Scanner in = new Scanner(reader);  
  13.             String input = in.next();  
  14.             int value = Integer.parseInt(input);  
  15.             System.out.println(value);  
  16.             return true;  
  17.   
  18.         } catch (FileNotFoundException e) {  
  19.             System.out.println("this is catch_for_filenot... block!");  
  20.             return false;  
  21.         } finally {  
  22.             System.out.println("this is finally block!");  
  23.         }  
  24.     }  
  25. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class FinallyTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         boolean file = open();  
  5.         System.out.println("this is main return value:" + file);  
  6.     }  
  7.   
  8.     public static boolean open() {  
  9.         String filename = "d:\\test.txtp";  
  10.         try {  
  11.             FileReader reader = new FileReader(filename);  
  12.             Scanner in = new Scanner(reader);  
  13.             String input = in.next();  
  14.             int value = Integer.parseInt(input);  
  15.             System.out.println(value);  
  16.             return true;  
  17.   
  18.         } catch (FileNotFoundException e) {  
  19.             System.out.println("this is catch_for_filenot... block!");  
  20.             return false;  
  21.         } finally {  
  22.             System.out.println("this is finally block!");  
  23.         }  
  24.     }  
  25. }  

故意把filename写错,造出异常,输出为下:

this is catch_for_filenot... block!
this is finally block!
this is main return value:false

从这儿看出来,程序先输出catch块中的,后又去执行finally块中的,虽然在catch中已经返回了,最后执行mian方法中的,而且输出false,说明catch块中的也成功返回了。所以,面对疑问,我们可以很肯定的回答,即使有return语句,finally块也一定会被执行!

2、尽量不要将catch和finally一起使用。

像我上面演示程序那样,try/catch/finally一起使用,在《Big Java》一书中提到,不建议这样做,因为会影响程序的可读性,最好的做法是:用try/catch嵌套,catch用来捕获异常,finally用来关闭资源,修改如下:

[java] view plaincopy
  1. public class FinallyTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         boolean file = open();  
  6.         System.out.println("this is main return value:" + file);  
  7.     }  
  8.   
  9.     public static boolean open() {  
  10.           
  11.         String filename = "d:\\test.txtp";  
  12.         try {  
  13.             try {  
  14.                 FileReader reader = new FileReader(filename);  
  15.                 Scanner in = new Scanner(reader);  
  16.                 String input = in.next();  
  17.                 int value = Integer.parseInt(input);  
  18.                 System.out.println(value);  
  19.                 return true;  
  20.   
  21.             } finally {  
  22.                 // 一些关闭资源的操作   
  23.                 System.out.println("this is finally block!");  
  24.             }  
  25.   
  26.         } catch (FileNotFoundException e) {  
  27.             System.out.println("this is catch_for_filenot... block!");  
  28.             return false;  
  29.         }  
  30.     }  
  31. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class FinallyTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         boolean file = open();  
  6.         System.out.println("this is main return value:" + file);  
  7.     }  
  8.   
  9.     public static boolean open() {  
  10.           
  11.         String filename = "d:\\test.txtp";  
  12.         try {  
  13.             try {  
  14.                 FileReader reader = new FileReader(filename);  
  15.                 Scanner in = new Scanner(reader);  
  16.                 String input = in.next();  
  17.                 int value = Integer.parseInt(input);  
  18.                 System.out.println(value);  
  19.                 return true;  
  20.   
  21.             } finally {  
  22.                 // 一些关闭资源的操作  
  23.                 System.out.println("this is finally block!");  
  24.             }  
  25.   
  26.         } catch (FileNotFoundException e) {  
  27.             System.out.println("this is catch_for_filenot... block!");  
  28.             return false;  
  29.         }  
  30.     }  
  31. }  

3、自定义异常

毕竟系统自带的异常处理器并不能满足所有需求,因为对于我们开发人员来说,抛出的异常越细致,我们越容易找到问题,总不能所有的问题都抛出Exception吧?太笼统了。在实际的开发中,我们可以根据自己的需要,进行自定义异常处理器。

[java] view plaincopy
  1. /** 
  2.  * 自定义异常处理器,继承Exception或者RuntimeException,依情况而定. 
  3.  * @author erqing 
  4.  * 
  5.  */  
  6. public class NameNotSupportException extends RuntimeException {  
  7.   
  8.     private static final long serialVersionUID = 7295869280641332966L;  
  9.   
  10.     public NameNotSupportException() {  
  11.     }  
  12.   
  13.     public NameNotSupportException(String message) {  
  14.         super(message);  
  15.     }  
  16. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 自定义异常处理器,继承Exception或者RuntimeException,依情况而定. 
  3.  * @author erqing 
  4.  * 
  5.  */  
  6. public class NameNotSupportException extends RuntimeException {  
  7.   
  8.     private static final long serialVersionUID = 7295869280641332966L;  
  9.   
  10.     public NameNotSupportException() {  
  11.     }  
  12.   
  13.     public NameNotSupportException(String message) {  
  14.         super(message);  
  15.     }  
  16. }  
[java] view plaincopy
  1. public class DefineTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         String name = "egg";  
  5.         if(!"erqing".equals(name)){  
  6.             throw new NameNotSupportException("erqing");  
  7.         }else{  
  8.             System.out.println("name is OK!");  
  9.         }  
  10.     }  
  11. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class DefineTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         String name = "egg";  
  5.         if(!"erqing".equals(name)){  
  6.             throw new NameNotSupportException("erqing");  
  7.         }else{  
  8.             System.out.println("name is OK!");  
  9.         }  
  10.     }  
  11. }  
[java] view plaincopy
  1. Exception in thread "main" NameNotSupportException: erqing  
  2.     at DefineTest.main(DefineTest.java:7)  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Exception in thread "main" NameNotSupportException: erqing  
  2.     at DefineTest.main(DefineTest.java:7)  

五、自定义异常

      Java确实给我们提供了非常多的异常,但是异常体系是不可能预见所有的希望加以报告的错误,所以Java允许我们自定义异常来表现程序中可能会遇到的特定问题,总之就是一句话:我们不必拘泥于Java中已有的异常类型。

      Java自定义异常的使用要经历如下四个步骤:

      1、定义一个类继承Throwable或其子类。

      2、添加构造方法(当然也可以不用添加,使用默认构造方法)。

      3、在某个方法类抛出该异常。

      4、捕捉该异常。

复制代码
/** 自定义异常 继承Exception类 **/
public class MyException extends Exception{
    public MyException(){
        
    }
    
    public MyException(String message){
        super(message);
    }
}

public class Test {
    public void display(int i) throws MyException{
        if(i == 0){
            throw new MyException("该值不能为0.......");
        }
        else{
            System.out.println( i / 2);
        }
    }
    
    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.display(0);
            System.out.println("---------------------");
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}
复制代码

      运行结果:

1111

六、异常链

      在设计模式中有一个叫做责任链模式,该模式是将多个对象链接成一条链,客户端的请求沿着这条链传递直到被接收、处理。同样Java异常机制也提供了这样一条链:异常链。

      我们知道每遇到一个异常信息,我们都需要进行try…catch,一个还好,如果出现多个异常呢?分类处理肯定会比较麻烦,那就一个Exception解决所有的异常吧。这样确实是可以,但是这样处理势必会导致后面的维护难度增加。最好的办法就是将这些异常信息封装,然后捕获我们的封装类即可。

      诚然在应用程序中,我们有时候不仅仅只需要封装异常,更需要传递。怎么传递?throws!!binge,正确!!但是如果仅仅只用throws抛出异常,那么你的封装类,怎么办??

      我们有两种方式处理异常,一是throws抛出交给上级处理,二是try…catch做具体处理。但是这个与上面有什么关联呢?try…catch的catch块我们可以不需要做任何处理,仅仅只用throw这个关键字将我们封装异常信息主动抛出来。然后在通过关键字throws继续抛出该方法异常。它的上层也可以做这样的处理,以此类推就会产生一条由异常构成的异常链。

      通过使用异常链,我们可以提高代码的可理解性、系统的可维护性和友好性。

      同理,我们有时候在捕获一个异常后抛出另一个异常信息,并且希望将原始的异常信息也保持起来,这个时候也需要使用异常链。

      在异常链的使用中,throw抛出的是一个新的异常信息,这样势必会导致原有的异常信息丢失,如何保持?在Throwable及其子类中的构造器中都可以接受一个cause参数,该参数保存了原有的异常信息,通过getCause()就可以获取该原始异常信息。

      语法:

复制代码
public void test() throws XxxException{
        try {
            //do something:可能抛出异常信息的代码块
        } catch (Exception e) {
            throw new XxxException(e);
        }
    }
复制代码

      示例:

复制代码
public class Test {
    public void f() throws MyException{
         try {
            FileReader reader = new FileReader("G:\\myfile\\struts.txt");  
             Scanner in = new Scanner(reader);  
             System.out.println(in.next());
        } catch (FileNotFoundException e) {
            //e 保存异常信息
            throw new MyException("文件没有找到--01",e);
        }  
    }
    
    public void g() throws MyException{
        try {
            f();
        } catch (MyException e) {
            //e 保存异常信息
            throw new MyException("文件没有找到--02",e);
        }
    }
    
    public static void main(String[] args) {
        Test t = new Test();
        try {
            t.g();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}
复制代码

      运行结果:

复制代码
com.test9.MyException: 文件没有找到--02
    at com.test9.Test.g(Test.java:31)
    at com.test9.Test.main(Test.java:38)
Caused by: com.test9.MyException: 文件没有找到--01
    at com.test9.Test.f(Test.java:22)
    at com.test9.Test.g(Test.java:28)
    ... 1 more
Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系统找不到指定的路径。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:106)
    at java.io.FileInputStream.<init>(FileInputStream.java:66)
    at java.io.FileReader.<init>(FileReader.java:41)
    at com.test9.Test.f(Test.java:17)
    ... 2 more
复制代码

      如果在程序中,去掉e,也就是:throw new MyException("文件没有找到--02");

      那么异常信息就保存不了,运行结果如下:

com.test9.MyException: 文件没有找到--02
    at com.test9.Test.g(Test.java:31)
    at com.test9.Test.main(Test.java:38)

      PS:其实对于异常链鄙人使用的也不是很多,理解的不是很清楚,望各位指正!!!!

七、异常的使用误区

      首先我们先看如下示例:该实例能够反映java异常的不正确使用(其实这也是我刚刚学Java时写的代码)!!

复制代码
OutputStreamWriter out = null;
        java.sql.Connection conn = null;
        try {            //   ---------1
            Statement stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select *from user");
            while (rs.next()){
                out.println("name:" + rs.getString("name") + "sex:"
                        + rs.getString("sex"));
            }
            conn.close();         //------2
            out.close();
        } 
        catch (Exception ex){    //------3
            ex.printStackTrace();    //------4
        }
复制代码

        1、-----------1

      对于这个try…catch块,我想他的真正目的是捕获SQL的异常,但是这个try块是不是包含了太多的信息了。这是我们为了偷懒而养成的代码坏习惯。有些人喜欢将一大块的代码全部包含在一个try块里面,因为这样省事,反正有异常它就会抛出,而不愿意花时间来分析这个大代码块有那几块会产生异常,产生什么类型的异常,反正就是一篓子全部搞定。这就想我们出去旅游将所有的东西全部装进一个箱子里面,而不是分类来装,虽不知装进去容易,找出来难啊!!!所有对于一个异常块,我们应该仔细分清楚每块的抛出异常,因为一个大代码块有太多的地方会出现异常了。

     结论一:尽可能的减小try块!!!

      2、--------2

      在这里你发现了什么?异常改变了运行流程!!不错就是异常改变了程序运行流程。如果该程序发生了异常那么conn.close(); out.close();是不可能执行得到的,这样势必会导致资源不能释放掉。所以如果程序用到了文件、Socket、JDBC连接之类的资源,即使遇到了异常,我们也要确保能够正确释放占用的资源。这里finally就有用武之地了:不管是否出现了异常,finally总是有机会运行的,所以finally用于释放资源是再适合不过了。

      结论二:保证所有资源都被正确释放。充分运用finally关键词。 

       3、----------3

      对于这个代码我想大部分人都是这样处理的,(LZ也是尴尬)。使用这样代码的人都有这样一个心理,一个catch解决所有异常,这样是可以,但是不推荐!为什么!首先我们需要明白catch块所表示是它预期会出现何种异常,并且需要做何种处理,而使用Exception就表示他要处理所有的异常信息,但是这样做有什么意义呢?

      这里我们再来看看上面的程序实例,很显然它可能需要抛出两个异常信息,SQLException和IOException。所以一个catch处理两个截然不同的Exception明显的不合适。如果用两个catch,一个处理SQLException、一个处理IOException就好多了。所以:

      结论三:catch语句应当尽量指定具体的异常类型,而不应该指定涵盖范围太广的Exception类。 不要一个Exception试图处理所有可能出现的异常。 

      4、----------4

      这个就问题多多了,我敢保证几乎所有的人都这么使用过。这里涉及到了两个问题,一是,捕获了异常不做处理,二是异常信息不够明确。

      4.1、捕获异常不做处理,就是我们所谓的丢弃异常。我们都知道异常意味着程序出现了不可预期的问题,程序它希望我们能够做出处理来拯救它,但是你呢?一句ex.printStackTrace()搞定,这是多么的不负责任对程序的异常情况不理不顾。虽然这样在调试可能会有一定的帮助,但是调试阶段结束后呢?不是一句ex.printStackTrace()就可以搞定所有的事情的!

      那么怎么改进呢?有四种选择:

      1、处理异常。对所发生的的异常进行一番处理,如修正错误、提醒。再次申明ex.printStackTrace()算不上已经“处理好了异常”.

      2、重新抛出异常。既然你认为你没有能力处理该异常,那么你就尽情向上抛吧!!!

      3、封装异常。这是LZ认为最好的处理方法,对异常信息进行分类,然后进行封装处理。

      4、不要捕获异常。

      4.2、异常信息不明确。我想对于这样的:java.io.FileNotFoundException: ………信息除了我们IT人没有几个人看得懂和想看吧!所以在出现异常后,我们最好能够提供一些文字信息,例如当前正在执行的类、方法和其他状态信息,包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。起码我公司是需要将异常信息所在的类、方法、何种异常都需要记录在日志文件中的。

      所以:

      结论四:既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。 不要做一个不负责的人。

      结论五:在异常处理模块中提供适量的错误原因信息,组织错误信息使其易于理解和阅读。

      对于异常还有以下几个注意地方:

     六、不要在finally块中处理返回值。

    七、不要在构造函数中抛出异常。

 

 

八、try…catch、throw、throws

      在这里主要是区分throw和throws。

      throws是方法抛出异常。在方法声明中,如果添加了throws子句,表示该方法即将抛出异常,异常的处理交由它的调用者,至于调用者任何处理则不是它的责任范围内的了。所以如果一个方法会有异常发生时,但是又不想处理或者没有能力处理,就使用throws吧!

     而throw是语句抛出异常。它不可以单独使用,要么与try…catch配套使用,要么与throws配套使用。

复制代码
//使用throws抛出异常
    public void f() throws MyException{
         try {
            FileReader reader = new FileReader("G:\\myfile\\struts.txt");  
             Scanner in = new Scanner(reader);  
             System.out.println(in.next());
        } catch (FileNotFoundException e) {
            throw new MyException("文件没有找到", e);    //throw
        }  
        
    }
复制代码

 

 

 

九、总结

      其实对于异常使用的优缺点现在确实存在很多的讨论。例如:http://www.cnblogs.com/mailingfeng/archive/2012/11/14/2769974.html。这篇博文对于是否需要使用异常进行了比较深刻的讨论。LZ实乃菜鸟一枚,不能理解异常深奥之处。但是有一点LZ可以肯定,那就是异常必定会影响系统的性能。

      异常使用指南(摘自:Think in java)

      应该在下列情况下使用异常。

      1、在恰当的级别处理问题(在知道该如何处理异常的情况下才捕获异常)。

      2、解决问题并且重新调用产生异常的方法。

      3、进行少许修补,然后绕过异常发生的地方继续执行。

      4、用别的数据进行计算,以代替方法预计会返回的值。

      5、把当前运行环境下能做的事情尽量做完。然后把相同(不同)的异常重新抛到更高层。

      6、终止程序。

      7、进行简化。

      8、让类库和程序更加安全。(这既是在为调试做短期投资,也是在为程序的健壮做长期投资)


相关文章推荐

java Exception

  • 2011-09-10 00:00
  • 96KB
  • 下载

Java中的异常 Exception

Java 异常基础 Exception Java中的异常 Exception   java.lang.Exception类是Java中所有异常的直接或间接父类。即Exception类是所有异常的根...

java开发exception部分

  • 2014-12-16 12:14
  • 343KB
  • 下载

Java中的Checked Exception——美丽世界中潜藏的恶魔?

Java中的Checked Exception——美丽世界中潜藏的恶魔?   在使用Java编写应用的时候,我们常常需要通过第三方类库来帮助我们完成所需要的功能。有时候这些类库所提供的很多API都通过...

nested exception is java.sql.SQLException: Data truncated for column 'PassWord' at row 72

Cause: java.sql.SQLException: Data truncated for column 'PassWord' at row 72

java exception

  • 2014-06-25 20:13
  • 32KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)