原来的网址:
http://www.apkbus.com/forum.php?mod=viewthread&tid=15288&fromuid=3402
1. 前言
最近在封装淘宝的SDK,我想实现自定义异常,所以补下这里的知识。以前从来没有实现过自定义异常,发现这里还是很有意思的。
你的程序总有一天会崩溃掉,在崩溃掉的时候我们要知道它在哪,为了什么而崩溃掉,数据的保存或者丢失情况如何等问题。我们可以通过继承类java.lang.Throwable的子类:Exception来设计我们自己的Java异常。Exception类用于描述程序能够捕获的异常,如ClassNotFoundException。要注意的是自定义异常类之间也可以有继承关系,同时也需要为自定义异常类设计构造方法,以方便构造自定义异常对象。
2. 编写自定义异常类的模式
编写自定义异常类实际上是继承一个API标准异常类,用新定义的异常处理信息覆盖原有信息的过程。常用的编写自定义异常类的模式如下:
- publicclass CustomException extends Exception { //或者继承任何标准异常类
- public CustomException() {} //用来创建无参数对象
- public CustomException(String message){ //用来创建指定参数对象
- super(message); //调用超类构造器
- }
- }
当然也可选用Throwable作为超类。其中无参数构造器为创建缺省参数对象提供了方便。第二个构造器将在创建这个异常对象时提供描述这个异常信息的字符串,通过调用超类构造器向上传递给超类,对超类中的toString()方法中返回的原有信息进行覆盖。
来讨论一个具体例子。假设程序中需要验证用户输入的表示年龄的数据必须是正整数值。我们可以按照以上模式编写这个自定义异常类如下:
- publicclass NegativeAgeException extends Exception { //或者:extends Throwable
- public NegativeAgeException() {}
- public NegativeAgeException(String message){
- super(message);
- }
- }
下面是应用这个自定义异常类的例子:
//完整程序存在本书配套资源目录为Ch11中名为NegativeAgeExceptionTest.java
- try{
- String ageString =JOptionPane.showInputDialog("Enter your age: ");
- if (Integer.parseInt(ageString) < 0)
- throw newNegativeAgeException("Please enter a positive age");
- else
- JOptionPane.showMessageDialog(null,ageString, "Age", 1);
- }
- catch(NegativeAgeExceptione){
- System.out.println(e);
- }
或者,可以创建一个缺省对象,然后在catch中打印具体信息,如:
- throw new NegativeAgeException();
- catch(NegativeAgeException e) {
- System.out.println("Please enter apositive age");
将产生与第一个例子相同的效果。
3.自定义异常处理
无论是利用标准API异常类来处理特殊的异常,或者编写自定义的异常类来达到同样目的,问题的关键是:
1. 当这个异常发生时,如何及时捕获这个异常。
2. 捕获这个异常后,如何产生精确的异常处理信息。
毋庸置疑,我们不可能期待JVM自动抛出一个自定义异常,也不能够期待JVM会自动处理一个自定义异常。发现异常、抛出异常以及处理异常的工作必须靠编程人员在代码中利用异常处理机制自己完成。
一般情况下,发现和抛出一个自定义异常通过在try程序块中利用if和throw语句完成,即:
- try {
- ...
- if (someExceptionConditon == true) {
- throw new CustomException("A custom exception xxx occurred. Please
- check your entry...")
- ...
- }
- catch (CustomException e) {
- ...
- }
而打印异常处理信息可以在抛出时包括在构造器的参数中,或者包括在处理这个异常的catch中。
另外应该注意在自定义异常发生之前,有可能产生标准异常的情况。例如,在一个需要验证年龄必须是正整数值的程序中,利用自定义异常类,如NegativeAgeException,验证输入的年龄是否正整数,即:
- try {
- ...
- if (Integer.parseInt(ageString) < 0)
- throw NegativeAgeException("Please enter a positive age");
- else
- ...
- }
- catch (NumberFormatException e) {
- System.out.println(e);
- }
- catch (NegativeAgeException e) {
- System.out.println(e);
- }
- ...
注意在这个代码中,如果ageString是非法整数字符串,如“25ab”,系统将首先抛出NumberFormatException,而不会执行throw NegativeAgeException("Please enter a positive age")。所以应该在catch中加入对NumberFormatException的处理,如以上代码所示。
4.设计实例分析
这是个比较完整的自定义异常类的设计,其实是比较模板化的东西。
自定义异常步骤:
一:创建一个类继承于Throwable或其子类;
二:添加构造方法;
三:在一个方法中使用throw抛出异常
四:在另一个方法中捕获并处理异常
示例:
第一个类:
- /**
- * 继承Exception类
- * @author Song Shi Chao
- *
- */
- public class MyFirstException extends Exception {
- public MyFirstException() {
- super();
- }
- public MyFirstException(String msg) {
- super(msg);
- }
- public MyFirstException(String msg, Throwable cause) {
- super(msg, cause);
- }
- public MyFirstException(Throwable cause) {
- super(cause);
- }
- // 自定义异常类的主要作用是区分异常发生的位置,当用户遇到异常时,
- // 根据异常名就可以知道哪里有异常,根据异常提示信息进行修改。
- }
第二个类:
- /**
- * 继承Throwable 类
- * @author Song Shi Chao
- *
- */
- public class MySecondException extends Throwable {
- public MySecondException() {
- super();
- }
- public MySecondException(String msg) {
- super(msg);
- }
- public MySecondException(String msg, Throwable cause) {
- super(msg, cause);
- }
- public MySecondException(Throwable cause) {
- super(cause);
- }
- }
这里我想做下说明,为什么写了4个构造函数呢?刚开始的时候,我也不明白,后来看了看源码,就懂了。大家可以点进去看Exception后者Throwable 的源码,最底层的父类就是这4个构造函数,所以,我们自定义的类不过是重写父类的构造方法罢了?但是构造函数是不能重写的啊?其实,看上去像重写,其实不是重写,只不过是像罢了。
第三个类:
- /**
- * 在这个继承了Exception类的自定义异常类中,我们定义了如下变量:
- id:独立标示符,这个是用来进行标示类中什么地方出现了错误被捕捉到。
- classname:捕捉到这个错误的类的名字。
- method:捕捉到这个错误的方法的名字。
- message:用来描述整个事件的情况。
- previous:是MyException得一个实例,若在链表中它是第一个的话,那么它就是null。
- * @author Song Shi Chao
- *
- */
- public class MyThreeException extends Exception {
- private int id; // a unique id
- private String classname; // the name of the class
- private String method; // the name of the method
- private String message; // a detailed message
- private MyThreeException previous = null; // the exception which was caught
- private String separator = " "; // line separator
- public MyThreeException(int id, String classname, String method, String message,
- MyThreeException previous) {
- this.id = id;
- this.classname = classname;
- this.method = method;
- this.message = message;
- this.previous = previous;
- }
- public String traceBack() {
- return traceBack(" ");
- }
- public String traceBack(String sep) {
- this.separator = sep;
- int level = 0;
- MyThreeException e = this;
- String text = line("Calling sequence (top to bottom)");
- while (e != null) {
- level++;
- text += line("--level " + level + "--------------------------------------");
- text += line("Class/Method: " + e.classname + "/" + e.method);
- text += line("Id : " + e.id);
- text += line("Message : " + e.message);
- e = e.previous;
- }
- return text;
- }
- private String line(String s) {
- return s + separator;
- }
- }
测试类:
- public class TestMyException {
- public static void firstException() throws MyFirstException {
- throw new MyFirstException(""firstException()" method occurs an exception!");
- }
- public static void secondException() throws MySecondException {
- throw new MySecondException(""secondException()" method occurs an exception!");
- }
- public static void threeException() throws MyThreeException {
- throw new MyThreeException(3, ""threeException()" method occurs an exception!", "sss", "hahha", null);
- }
- public static void main(String[] args) {
- try {
- TestMyException.threeException();
- TestMyException.firstException();
- TestMyException.secondException();
- } catch (MyFirstException e1) {
- System.out.println("Exception: " + e1.getMessage());
- e1.printStackTrace();
- } catch (MySecondException e2) {
- System.out.println("Exception: " + e2.getMessage());
- e2.printStackTrace();
- } catch (MyThreeException e) {
- e.printStackTrace();
- }
- }
- /*
- //当一个try块后面跟着多个catch块时,如果发生的异常匹配第一个catch块的参数,便将异常处理权利交给第一个catch块。
- //如果发生的异常与第一个catch块不匹配,便看是否与第二个catch块匹配,依次下去,如果到最后依然无法匹配该异常,
- //便需要在方法声明中添加一条throw语句,将该异常抛出。
- //因此,在有多个catch块,而且每次处理的异常类型具有继承关系时,应该首先catch子类异常,再catch父类异常。
- //比如,如果MySecondException继承MyFirstException,那么最好将catch(MySecondException?e2)放在前面
- //把catch?(MyFirstException?e1)放在后面。
- */
- }