`

异常理解

 
阅读更多

1.java异常层次结构       

        在 Java 程序设计语言中,使用一种异常处理的错误捕获机制。当程序运行过程中发生一些异常情况,程序有可能被中断、或导致错误的结果出现。

在这种情况下,程序不会返回任何值,而是抛出封装了错误信息的对象。

图 1. Java 异常体系结构

异常分为两大类,程序员只能抛出Exception异常对象,Error对象是Java系统在内部发生错误或者资源耗尽时才抛出的。

图 6. 异常层次类图样例

其中,BussinessException 属于基本业务操作异常,所有业务操作异常都继承于该类。例如,通常 UI 层或 Web 层是由系统最终用户执行业务操作驱动,因此最好抛出业务类异常。ServiceException 一般属于中间服务层异常,该层操作引起的异常一般包装成基本 ServiceException。DAOException 属于数据访问相关的基本异常。

对于多层系统,每一层都有该层的基本异常。在层与层之间的信息传递与方法调用时候,一旦在某层发生异常,传递到上一层的时候,一般包装成该层异常,直至与用户最接近的 UI 层,从而转化成用户友好的错误信息。

异常转译就是将一种异常转换为另一种异常。异常转译针对所有继承 Throwable 超类的异常类而言的。如下图 7 中代码所示展示了异常转译的一个例子:

图 7. 异常转译代码样例

图 7. 异常转译代码样例

 

 

2.java异常处理的分类

 

2.1)可检测异常

      可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,例如:sqlExecption 这个异常就是一个检测异常。你连接 JDBC 时,不捕捉这个异常,编译器就通不过,不允许编译。

2.2)非检测异常

       有两个主要类定义非检测异常:RuntimeException 和 Error。

      非检测异常不遵循处理或声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已解决了这样一个异常。例如:一个数组为 3 个长度,当你使用下标为3时,就会产生数组下标越界异常。这个异常 JVM 不会进行检测,要靠程序员来判断。Error 子类属于非检测异常,因为无法预知它们的产生时间。若 Java 应用程序内存不足,则随时可能出现 OutOfMemoryError;起因一般不是应用程序的特殊调用,而是 JVM 自身的问题。另外,Error 一般表示应用程序无法解决的严重问题。RuntimeException 类也属于非检测异常,因为普通 JVM 操作引发的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在 Java 应用程序中会频繁出现。因此,它们不受编译器检查与处理或声明规则的限制。

 

3.Java 异常的处理

        在 Java 应用程序中,对异常的处理有两种方式:处理异常和声明异常。

 

3.1)处理异常:try、catch 和 finally

       若要捕获异常,则必须在代码中添加异常处理器块。这种 Java 结构可能包含 3 个部分,都有 Java 关键字。下面的例子中使用了 try-catch-finally 代码结构。

   

1 import java.io.*;
2 public class EchoInputTryCatchFinally {
3      public static void main(String args[]){
4          System.out.println("Enter text to echo:");
5          InputStreamReader isr = new InputStreamReader(System.in);
6          BufferedReader inputReader = new BufferedReader(isr);
7          try{
8              String inputLine = inputReader.readLine();
9              System.out.println("Read:" + inputLine);     
10          }
11          catch(IOException exc){
12              System.out.println("Exception encountered: " + exc);
13          }
14          finally{
15             System.out.println("End. ");
16      }
17 }
18}

 其中:

 

  • try 块:将一个或者多个语句放入 try 时,则表示这些语句可能抛出异常。编译器知道可能要发生异常,于是用一个特殊结构评估块内所有语句。
  • catch 块:当问题出现时,一种选择是定义代码块来处理问题,catch 块的目的便在于此。catch 块是 try 块所产生异常的接收者。基本原理是:一旦生成异常,则 try 块的执行中止,JVM 将查找相应的 JVM。
  • finally 块:还可以定义 finally 块,无论运行 try 块代码的结果如何,该块里面的代码一定运行。在常见的所有环境中,finally 块都将运行。无论 try 块是否运行完,无论是否产生异常,也无论是否在 catch 块中得到处理,finally 块都将执行。

try-catch-finally 规则:

  1. 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
  2. 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
  3. catch 块与相应的异常类的类型相关。
  4. 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。
  5. 可嵌套 try-catch-finally 结构。
  6. 在 try-catch-finally 结构中,可重新抛出异常。
  7. 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击

 

3.2)声明异常

      若要声明异常,则必须将其添加到方法签名块的结束位置。下面是一个实例:

public void errorProneMethod(int input) throws java.io.IOException {
    //Code for the method,including one or more method
    //calls that may produce an IOException
}

这样,声明的异常将传给方法调用者,而且也通知了编译器:该方法的任何调用者必须遵守处理或声明规则。声明异常的规则如下:

  • 必须声明方法可抛出的任何可检测异常(checked exception)。
  • 非检测性异常(unchecked exception)不是必须的,可声明,也可不声明。
  • 调用方法必须遵循任何可检测异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

4.java异常处理注意事项

 

  1、重写的方法不能抛出与被重写的方法不同的异常。

  2、Catch语句块中异常的父类不能放在最前面。子类应该放在最前面。

        例如:catch(Exception e){

                  }catch(IOException e){

                  }错误,因为父类Exception 已经捕获,所以IOException会报错。

        我们应该这样写:

                 catch(IOException e){

                 }catch(Exception e){

                 }

   3、抛出什么类型的异常(为可恢复的条件使用检查型异常,为编程错误使用运行时异常

           当抛出异常时,如果希望调用者能够拦截并且恢复程序的执行,应该抛出checked exception(即可恢复情况使用checked exception)。为了让调用者能够恢复程序的执行,checked exception对象往往需要提供一些方法使得调用者可以获得一些有助于恢复的信息。所以,换位思考是个好主意,想一想如果调用者拦截了你抛出的异常,他能够恢复程序的执行,你就应该抛出checked exception,否则你就应该抛出RuntimeException.

对于程序错误(比如引用为空,方法的前提条件不满足等不可恢复情况)应该抛出RuntimeException。
   4、如何传递底层抛出的异常
        异常对象的传递机制是从异常抛出的地方往调用链的反方向传递。如果我们catch到了一个从底层抛出的异常,我们如何处理呢。如果我们能够处理该异常,我们应该处理该异常。如果我们不能处理该异常,需要继续传递时,我们需要考虑我们调用者能否理解这个底层传递过来的异常的含义。有时候调用者并不理解底层的异常,甚至都不知道有底层这个抽象设计存在,需要我们将异常进行转换,抛出调用者能够理解的异常对象。
      将特定的异常转换为更通用异常时一种错误做法。一般而言,这将取消异常起初抛出时产生的上下文,在将异常传到系统的其他位置时,将更难处理。见下例:
  1. 1   try{
    2     // Error-prone code
    3   }
    4   catch(IOException e){
    5      String msg = "If you didn ’ t have a problem before,you do now!";
    6      throw new Exception(msg);
    7   }

    因为没有原始异常的信息,所以处理器块无法确定问题的起因,也不知道如何更正问题。

 

   6、throw和throws的区别

       throws是用来声明一个方法可能抛出的所有异常信息

throw则是指抛出的一个具体的异常类型。
通常在一个方法(类)的声明处通过throws声明方法(类)可能抛出的异常信息,而在方法(类)内部通过throw声明一个具体的异常信息。
throws通常不用显式的捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法;
throw则需要用户自己捕获相关的异常,而后在对其进行相关包装,最后在将包装后的异常信息抛

两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。    

另:throw还可以有相当于return的作用,终止程序!

  

  7、自定义异常

  

    TestMyException.java继承Exception,构造方法调用父类方法存储错误信息 

public class TestMyException extends Exception{
	
	
	/**
	 * 自定义异常
	 */
	private static final long serialVersionUID = 1L;

	public TestMyException(String message){//构造器记录错误信息异常exception.getMessage;
		super(message);
	}	
	
	public TestMyException(Throwable t){//异常类的父类,泛型
		super(t);
	}
	
	public TestMyException(Throwable t , String message){
		super(message,t);
	}
}

   

   

DefineMyException.java
public class DefineMyException {


public static void test() throws TestMyException{
int x = 0;
if(x == 0){
throw new TestMyException("除数不能为0");
}
}

/**
* @param args
*/
public static void main(String[] args){
try {
test();
} catch (TestMyException e) {
System.out.println(e.getMessage());
}
}

}

 

 

参考链接:

1、http://www.oschina.net/translate/10-java-exception-and-error-interview-questions-answers-programming

2、http://www.ibm.com/developerworks/cn/java/j-lo-exception-misdirection/

3、http://www.ibm.com/developerworks/cn/java/j-jtp05254/

4、http://www.ibm.com/developerworks/cn/java/j-lo-exceptionframework/

 

 

补充一个经典链接:http://blog.csdn.net/hguisu/article/details/6155636

主要搞定这两个demo的区别:

demo1:

package exception;

public class TestException {
	    public TestException() {  
	    }  
	    //一般不在finally return值
	    boolean testEx() throws Exception {  
	        boolean ret = true;  
	        try {  
	            ret = testEx1();  
	        } catch (Exception e) {  
	            System.out.println("testEx, catch exception");  
	            ret = false;  
	            throw e;  
	        } finally {  
	            System.out.println("testEx, finally; return value=" + ret);  
	        } 
	        return ret;
	    }  
	  
	    boolean testEx1() throws Exception {  
	        boolean ret = true;  
	        try {  
	            ret = testEx2();  
	            if (!ret) {  
	                return false;  
	            }  
	            System.out.println("testEx1, at the end of try");  
	            return ret;  
	        } catch (Exception e) {  
	            System.out.println("testEx1, catch exception");  
	            ret = false;  
	            throw e;  
	        } finally {  
	            System.out.println("testEx1, finally; return value=" + ret);  
	            /*return ret; */ 
	        }  
	    }  
	  
	    boolean testEx2() throws Exception {  
	        boolean ret = true;  
	        try {  
	            int b = 12;  
	            int c;  
	            for (int i = 2; i >= -2; i--) {  
	                c = b / i;  
	                System.out.println("i=" + i);  
	            }  
	            return true;  
	        } catch (Exception e) {  
	            System.out.println("testEx2, catch exception");  
	            ret = false;  
	            throw e;  //抛出异常,但跑到finally有return值,调用者方法的方法者(即textEx1)会直接忽略异常
	        } finally {  
	            System.out.println("testEx2, finally; return value=" + ret);  
	            /*return ret; */ 
	        }  
	    }  
	  
	    public static void main(String[] args) {  
	        TestException testException1 = new TestException();  
	        try {  
	            testException1.testEx();  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	        }  
	    }  	  
}

 demo1输出:

  

i=2
i=1
java.lang.ArithmeticException: / by zero
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false
	at exception.TestException.testEx2(TestException.java:46)
	at exception.TestException.testEx1(TestException.java:24)
	at exception.TestException.testEx(TestException.java:10)
	at exception.TestException.main(TestException.java:63)

 

 

 

   demo2:   

 

package exception;

public class TestException {
	    public TestException() {  
	    }  
	    //一般不再finally return值
	    boolean testEx() throws Exception {  
	        boolean ret = true;  
	        try {  
	            ret = testEx1();  
	        } catch (Exception e) {  
	            System.out.println("testEx, catch exception");  
	            ret = false;  
	            throw e;  
	        } finally {  
	            System.out.println("testEx, finally; return value=" + ret);  
	            return ret;  
	        }  
	    }  
	  
	    boolean testEx1() throws Exception {  
	        boolean ret = true;  
	        try {  
	            ret = testEx2();  
	            if (!ret) {  
	                return false;  
	            }  
	            System.out.println("testEx1, at the end of try");  
	            return ret;  
	        } catch (Exception e) {  
	            System.out.println("testEx1, catch exception");  
	            ret = false;  
	            throw e;  
	        } finally {  
	            System.out.println("testEx1, finally; return value=" + ret);  
	            return ret;  
	        }  
	    }  
	  
	    boolean testEx2() throws Exception {  
	        boolean ret = true;  
	        try {  
	            int b = 12;  
	            int c;  
	            for (int i = 2; i >= -2; i--) {  
	                c = b / i;  
	                System.out.println("i=" + i);  
	            }  
	            return true;  
	        } catch (Exception e) {  
	            System.out.println("testEx2, catch exception");  
	            ret = false;  
	            throw e;  //抛出异常,但跑到finally有return值,调用者方法的方法者(即textEx1)会直接忽略异常
	        } finally {  
	            System.out.println("testEx2, finally; return value=" + ret);  
	            return ret;  
	        }  
	    }  
	  
	    public static void main(String[] args) {  
	        TestException testException1 = new TestException();  
	        try {  
	            testException1.testEx();  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	        }  
	    }  	  
}

   demo2输出:

   

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics