`

异常理解

 
阅读更多

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

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    Java异常处理-自定义异常类及课后练习

    通过这些练习,可以加深对自定义异常理解和使用方法的掌握。 ### 三、小结与小悟 #### (1)小结:异常处理5个关键字 - `try`:包含可能抛出异常的代码块。 - `catch`:捕获并处理异常的代码块。 - `throw`:抛出...

    NCV5-日志异常技术红皮书_NC单据开发日志异常的红皮书_

    1. 日志异常理解:日志异常是指在系统运行过程中,通过日志记录的错误信息或不正常状态。这些异常可能源于代码错误、数据问题、配置失误或者系统资源不足等多种原因。识别日志异常的关键在于了解不同类型的错误代码...

    深入理解java异常处理机制

    ### 深入理解Java异常处理机制 #### 引言 异常处理机制是任何现代编程语言不可或缺的一部分,尤其是在像Java这样的面向对象的语言中更是如此。Java的异常处理机制旨在帮助开发者编写更健壮、更易于维护的代码。...

    异常定义分析,概念理解

    关于异常的定义和理解,各类异常的区分.等等

    java异常体系理解

    JDK1.7以上的异常体系,需要后续的不断总结和完善,学习jdk必备!

    MonteCarlo.rar_Monte Carlo_剔除异常值_异常值_异常样本剔除_蒙特卡洛 异常

    总之,蒙特卡洛方法为处理异常值提供了一种有力的工具,它允许我们在大量随机抽样中理解数据的内在结构,从而更准确地识别和剔除异常值。然而,这种方法也有其局限性,例如假设数据分布,因此在实际应用中应结合业务...

    深入理解LINUX内核(中文第三版)第四章 中断和异常

    在深入理解Linux内核第四章中,作者详细阐述了Linux内核中中断和异常的概念、分类、处理机制以及它们在系统运行中的重要性。本章不仅涉及了Intel微处理器手册中对中断和异常的分类方法,还探讨了Linux内核如何初始化...

    C# 捕获C/C++异常的例子

    总结来说,理解和处理C#与C++之间的异常转换是跨语言开发的关键技能。通过适当的设计和实现,我们可以确保C++的异常能够被C#有效地捕获和处理,从而增强程序的健壮性和稳定性。在实际项目中,确保正确处理各种异常...

    深入理解java异常(异常中的Error+异常中的Exception+检查异常+运行时异常+异常处理+throws关键字等)

    try 块中放置可能会发生异常的代码,当异常发生时,try 块抛出系统自动生成的异常对象,然后异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序,并执行 catch 语句。 throws 关键字用于在方法签名中声明...

    理解.NET中的异常

    总的来说,理解.NET中的异常机制对于编写健壮、易于维护的代码至关重要。正确地使用异常,结合异常的本质和.NET提供的异常类,可以提高代码质量,同时确保在遇到错误时能够及时恢复或提供有用的错误信息。

    C#异常处理总结及简单实例

    一、异常处理的理解? 异常处理是指程序在运行过程中,发生错误会导致程序退出,这种错误,就叫做异常。 因此处理这种错误,就称为异常处理。 二、异常处理如何操作? C# 异常处理时建立在四个关键词之上的:try、...

    变态心理学串讲PPT课件.pptx

    学科的历史发展揭示了对心理异常理解的逐步深化,从早期的迷信和误解到现在科学的诊断和治疗方法的形成。 在讨论心理正常与异常时,课件提到了正常心理活动的功能,包括适应环境、促进个体成长和发展、维护自我平衡...

    深入理解java异常处理机制Java开发Java经验技巧共

    这篇19页的PDF文档《深入理解java异常处理机制Java开发Java经验技巧共》可能涵盖了Java异常处理的基本概念、最佳实践以及高级用法。在Java中,异常处理通过五个关键字来实现:try、catch、finally、throw和throws。 ...

    易语言HOOK异常处理

    源码分析可能还包括理解如何在异常发生时正确恢复,或者记录异常信息以供后续分析。 在实际应用中,这种技术可以用于创建安全的程序,提高软件的稳定性,或者在调试阶段深入理解程序行为。对于易语言的开发者来说,...

    全面理解java中的异常处理机制

    Java中的异常处理机制是编程过程中不可或缺的一部分,它用于处理程序运行时出现的错误或不正常情况。...理解并熟练运用异常处理,不仅可以提高代码质量,还能帮助开发者更好地应对复杂编程环境中的各种挑战。

    易语言线程结构异常处理

    易语言是一种专为中国人设计的编程语言,它以简体中文作为编程语句,降低了编程的门槛,使得更多的人能够参与到编程中来。...同时,深入理解线程管理、异常处理和底层内存操作也是提升编程技能的重要步骤。

    c/vc++/MFC异常处理/结构化异常处理 浅析

    在编程领域,异常处理是确保程序健壮性与稳定性的关键技术。对于C、C++以及基于MFC(Microsoft Foundation Classes)的开发来说,异常处理更是不...理解并熟练运用这些异常处理机制,是每个专业程序员必备的技能之一。

    JAVA范例 四)异常处理---编译时异常、运行时异常

    本文将深入探讨"JAVA范例 四)异常处理---编译时异常、运行时异常"这个主题,结合标签"源码"和"工具",我们可以理解这与实际编程实践和可能使用的开发工具如Struts2框架相关。 首先,我们来看编译时异常。编译时...

    计算布格异常Fortran程序

    首先,我们要理解什么是自由空气异常(Free Air Anomaly)。自由空气异常是指地球表面某点的重力值与理想平坦地球在同一高度上的重力值之差。它反映了地壳下方密度分布的差异,但并未考虑地形对重力的影响。计算公式...

Global site tag (gtag.js) - Google Analytics