Java异常处理框架

java中的异常,一个是编译时的检查异常,一般编译器要求程序代码捕获此类异常,如try{}catch{};一个是程序运行时的“运行时异常”及“错误”。运营时异常的处理方式是竭力保证数据的一致性,如事务的处理;错误的处理方式是保证数据的一致性的同时,还需要保证整个应用系统不崩溃。

What Is an Exception?

The term exception is shorthand for the phrase “exceptional event.”
在这里插入图片描述
The call stack.

The runtime system searches the call stack for a method that contains a block of code that can handle the exception. This block of code is called an exception handler.
在这里插入图片描述
Searching the call stack for the exception handler.

The Catch or Specify Requirement

The Three Kinds of Exceptions

The first kind of exception is the checked exception.
Checked exceptions are subject to the Catch or Specify Requirement. All exceptions are checked exceptions, except for those indicated by Error, RuntimeException, and their subclasses. 如 IOException、SQLException。
检查异常CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行try catch,该类异常一般包括几个方面:

  1. 试图在文件尾部读取数据
  2. 试图打开一个错误格式的URL
  3. 试图根据给定的字符串查找class 对象,而这个字符串表示的类并不存在。
    The second kind of exception is the error.
    Errors are not subject to the Catch or Specify Requirement. Errors are those exceptions indicated by Error and its subclasses.
    应用程序不会抛出该类异常,如果出现了该类异常,剩下的只能尽力保证程序安全终止了。
    The third kind of exception is the runtime exception.
    Runtime exceptions are not subject to the Catch or Specify Requirement. Runtime exceptions are those indicated by RuntimeException and its subclasses. 如NullPointerException,ClassCastException。
    Errors and runtime exceptions are collectively known as unchecked exceptions.(错误和运行时异常共同叫做未检查异常)

Catching and Handling Exceptions

Then, the try-with-resources statement, introduced in Java SE 7, is explained. The try-with-resources statement is particularly suited to situations that use Closeable resources, such as streams.

The try Block

try {
    code
}
catch and finally blocks . . .

The segment in the example labeled code contains one or more legal lines of code that could throw an exception. (The catch and finally blocks are explained in the next two subsections.)

The catch Blocks

try {

} catch (ExceptionType name) {

} catch (ExceptionType name) {

}

Each catch block is an exception handler that handles the type of exception indicated by its argument. The argument type, ExceptionType, declares the type of exception that the handler can handle and must be the name of a class that inherits from the Throwable class. The handler can refer to the exception with name.

Catching More Than One Type of Exception with One Exception Handler

In Java SE 7 and later, a single catch block can handle more than one type of exception. This feature can reduce code duplication and lessen the temptation to catch an overly broad exception.
In the catch clause, specify the types of exceptions that block can handle, and separate each exception type with a vertical bar (|):

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block.

The finally Block

The finally block always executes when the try block exits.

Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise(同样), if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

Important: The finally block is a key tool for preventing resource leaks. When closing a file or otherwise recovering resources, place the code in a finally block to ensure that resource is always recovered.

Consider using the try-with-resources statement in these situations, which automatically releases system resources when no longer needed. The The try-with-resources Statementsection has more information.

The try-with-resources Statement

The try-with-resources statement is a try statement that declares one or more resources.

The following example uses a try-with-resources statement to automatically close a java.sql.Statement object:

public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) {
        ResultSet rs = stmt.executeQuery(query);

        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");

            System.out.println(coffeeName + ", " + supplierID + ", " + 
                               price + ", " + sales + ", " + total);
        }
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    }
}

The resource java.sql.Statement used in this example is part of the JDBC 4.1 and later API.

Note: A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.

Putting It All Together

try语句块,表示要尝试运行代码,try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象。
catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理,catch语句带一个Throwable类型的参数, 表示可捕获异常类型。当try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配,若匹配,则执行catch块中代码,并将catch块参数指向所抛的异常对象。catch语句可以有多个,用来匹配多个中的一个异常,一旦匹配上后,就不再尝试匹配别的catch块了。 通过异常对象可以获取异常发生时完整的JVM堆栈信息,以及异常信息和异常发生的原因等。
finally语句块是紧跟catch语句后的语句块,这个语句块总是会在方法返回前执行, 而不管是否try语句块是否发生异常。并且这个语句块总是在方法返回前执行。目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。

Specifying the Exceptions Thrown by a Method

public void writeList() throws IOException, IndexOutOfBoundsException {

Remember that IndexOutOfBoundsException is an unchecked exception; including it in the throws clause is not mandatory. You could just write the following.

public void writeList() throws IOException {

How to Throw Exceptions

The throw Statement

   if (size == 0) {
        throw new EmptyStackException();
    }

For now, all you need to remember is that you can throw only objects that inherit from the java.lang.Throwable class.

Throwable Class and Its Subclasses

As you can see, Throwable has two direct descendants: Error and Exception.

  1. getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
  2. getMessage():返回异常的消息信息。
  3. printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。
    在这里插入图片描述
    The Throwable class.

Chained Exceptions

The following example shows how to use a chained exception.

try {

} catch (IOException e) {
    throw new SampleException("Other IOException", e);
}

In this example, when an IOException is caught, a new SampleException exception is created with the original cause attached and the chain of exceptions is thrown up to the next higher level exception handler.

Accessing Stack Trace Information

Now let’s suppose that the higher-level exception handler wants to dump the stack trace in its own format.


Definition: A stack trace provides information on the execution history of the current thread and lists the names of the classes and methods that were called at the point when the exception occurred. A stack trace is a useful debugging tool that you’ll normally take advantage of when an exception has been thrown.


The following code shows how to call the getStackTrace method on the exception object.

catch (Exception cause) {
    StackTraceElement elements[] = cause.getStackTrace();
    for (int i = 0, n = elements.length; i < n; i++) {       
        System.err.println(elements[i].getFileName()
            + ":" + elements[i].getLineNumber() 
            + ">> "
            + elements[i].getMethodName() + "()");
    }
}

Creating Exception Classes

The next figure illustrates one possible class hierarchy for the exceptions thrown by the linked list.
在这里插入图片描述
Example exception class hierarchy.

Unchecked Exceptions — The Controversy(争论)

Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don’t want to be bothered with specifying the exceptions your methods can throw.
Here’s the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception. 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。

Advantages of Exceptions

Advantage 1: Separating Error-Handling Code from “Regular” Code
Note that exceptions don’t spare you the effort of doing the work of detecting, reporting, and handling errors, but they do help you organize the work more effectively.
Advantage 2: Propagating Errors Up the Call Stack(传播错误到堆栈的最顶层)
Advantage 3: Grouping and Differentiating Error Types

How to design a exception Framework

异常处理的一般原则

1、能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。经常见到很多人写代码时throw new Exception(),导致系统日志中存在大量的异常,正确的异常处理方式是要先看能否处理成正常请求,比如登录时,验证码错误,这种就很清楚异常描述,就没必要throw new Exception(),而是要转化成正常的响应包体返回前端,只有实在没办法处理时,才抛出异常,并打印日志记录异常上下文数据,便于后续进行异常分析,这样能优雅的处理异常,也能使代码围绕核心业务逻辑来展开,而不是被一堆异常处理代码干扰,增强了代码的可读性
2、对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。
3、对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格, 将优雅的异常信息反馈给用户。

异常的转译与异常链

异常转译的原理

所谓的异常转译就是将一种异常转换另一种新的异常,也许这种新的异常更能准确表达程序发生异常。
在Java中有个概念就是异常原因,异常原因导致当前抛出异常的那个异常对象, 几乎所有带异常原因的异常构造方法都使用Throwable类型做参数,这也就为异常的转译提供了直接的支持,
因为任何形式的异常和错误都是Throwable的子类。比如将SQLException转换为另外一个新的异常DAOException,可以这么写:
先自定义一个异常DAOException:

	public class DAOException extends RuntimeException {  
	/(省略了部分代码)  
	  public DAOException(String message, Throwable cause) {  
	      super(message, cause);  
	  }  

比如有一个SQLException类型的异常对象e,要转换为DAOException,可以这么写:

DAOException daoEx = new DAOException ( "SQL异常", e);

异常转译是针对所有继承Throwable超类的类而言的,从编程的语法角度讲,其子类之间都可以相互转换。但是,从合理性和系统设计角度考虑,可将异常分为三类:Error、Exception、RuntimeException,笔者认为,
合理的转译关系图应该如图 2:
在这里插入图片描述
图 2 异常转译

为什么要这么做呢?笔者认为,异常的处理存在着一套哲学思想:对于一个应用系统来说, 系统所发生的任何异常或者错误对操作用户来说都是系统"运行时"异常,都是这个应用系统内部的异常。 这也是异常转译和应用系统异常框架设计的指导原则。在系统中大量处理非检查异常的负面影响很多,最重要的一个方面就是代码可读性降低,程序编写复杂,异常处理的代码也很苍白无力。因此,很有必要将这些检查异常Exception和错误Error转换为RuntimeException异常,让程序员根据情况来决定是否捕获和处理所发生的异常。
图中的三条线标识转换的方向,分三种情况:
①:Error到Exception:将错误转换为异常,并继续抛出。例如Spring WEB框架中,
将org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,将捕获的错误转译为一个NestedServletException异常。这样做的目的是为了最大限度挽回因错误发生带来的负面影响。因为一个Error常常是很严重的错误,可能会引起系统挂起。
②:Exception到RuntimeException:将检查异常转换为RuntimeException可以让程序代码变得更优雅,让开发人员集中经理设计更合理的程序代码,反过来也增加了系统发生异常的可能性。
③:Error到RuntimeException:目的还是一样的。把所有的异常和错误转译为不检查异常,这样可以让代码更为简洁,还有利于对错误和异常信息的统一处理。

设计一个高效合理的异常处理框架

对于一个应用系统来说,发生所有异常在用户看来都是应用系统内部的异常。因此应该设计一套应用系统的异常框架,以处理系统运行过程中的所有异常。基于这种观点,可以设计一个应用系统的异常比如叫做AppException。并且对用户来说, 这些异常都是运行应用系统运行时发生的,因此AppException应该继承RuntimeException,这样系统中所有的其他异常都转译为AppException,当异常发生的时候,前端接收到AppExcetpion并做统一的处理。 画出异常处理框架如图 3 :
在这里插入图片描述
图 3 一个应用系统的异常处理框架

在这个设计图中,AppRuntimeException是系统异常的基类,对外只抛出这个异常, 这个异常可以由前端(客户端)接收处理,当异常发生时,客户端的相关组件捕获并处理这些异常, 将"友好"的信息展示给客户。
在AppRuntimeException下层,有各种各样的异常和错误,最终都转译为AppRuntimeException, AppRuntimeException下面还可以设计一些别的子类异常,比如AppDAOException、OtherException等, 这些都根据实际需要灵活处理。 在往下就是如何将捕获的原始异常比如SQLException、HibernateException转换为更高级一点AppDAOException。
有关异常框架设计这方面公认比较好的就是Spring,Spring中的所有异常都可以用org.springframework.core.NestedRuntimeException来表示,并且该基类继承的是RuntimeException。
Spring框架很庞大,因此设计了很多NestedRuntimeException的子类,还有异常转换的工具,这些都是非常优秀的设计思想。

Java异常处理总结

回顾全文,总结一下Java异常处理的要点:

  1. 异常是程序运行过程过程出现的错误,在Java中用类来描述,用对象来表示具体的异常。 Java将其区分为Error与Exception,Error是程序无力处理的错误,Exception是程序可以处理的错误。 异常处理是为了程序的健壮性。
  2. Java异常类来自于Java API定义和用户扩展。通过继承Java API异常类可以实现异常的转译。
  3. 异常能处理就处理,不能处理就抛出,最终没有处理的异常JVM会进行处理。
  4. 异常可以传播,也可以相互转译,但应该根据需要选择合理的异常转译的方向。
  5. 对于一个应用系统,设计一套良好的异常处理体系很重要。这一点在系统设计的时候就应该考虑到。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值