Thinking in Java:第十二章 通过异常处理错误

      java的基本理念是结构不佳的代码不能运行。

12.1 基本概念


12.2 基本异常

       异常情形是指阻止当前方法或作用域继续执行的问题。普通的问题是指在当前的环境下能得到足够的信息,总能处理这个错误;而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。

12.2.1 异常参数

        所有标准异常都有两个构造器:一个是默认构造器,一个是接受字符串作为参数。

12.3 捕获异常

        监控区域(try),异常处理程序(catch);每个catch子句看起来就像是接收一个且仅接收一个特殊类型的参数的方法。

       java支持终止模型,一旦异常被抛出,就表明异常已无法挽回,也不能回来继续执行;可以将try模块放入while循环中,这样就不断进入try块,直到得到满意的结果。

12.4 创建自定义异常

         要自己定义异常,必须从已有的异常类继承,最好是选择意思相近的异常类继承。

12.5 异常说明

         异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,所以方法定义可能看起来像这样:

        void f() throws Toobig,Toosmall,DivZero {}

代码必须与异常说明保持一致。如果方法里的代码产生了异常却没有进行处理,编译器会发现这个问题并提醒你:要么处理这个异常,要么就在异常说明中表明词方法将产生异常。

12.6 捕获所有异常

     throwable类介绍:

类 java.lang.Throwable

java.lang.Object
   |
   +----java.lang.Throwable

public class  Throwable extends  Object implements  Serializable 下列类的父类:
ErrorException

Throwable 类是 Java 语言中所有错误和异常类的父类。 仅当对象是该类的实例 (或它子类的一个实例)时被 Java Virtual Machine 抛出或被 Java throw 语句抛出。 相似地,只有这个类或它的一个子类能作为 catch 子句的参数。

Throwable 类包括了线程创建时该线程执行堆栈的缩影。 它也包括一个消息字符串,能够给出关于错误的更多信息。

这里是一个捕获异常的例程:

     try {
         int a[] = new int[2];
         a[4];
     } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("exception: " + e.getMessage());
         e.printStackTrace();
     }
 

构造子索引
Throwable()
构造一个无详细信息的新的  Throwable 。
Throwable(String)
用指定的详细信息创建一新的  Throwable 。

方法索引
fillInStackTrace()
填写执行堆栈跟踪信息。
getLocalizedMessage()
生成该  Throwable 的本地化描述。
getMessage()
返回该 throwable 对象的详细信息。
printStackTrace()
把该  Throwable 和它的跟踪情况打印到标准错误流。
printStackTrace(PrintStream)
把该  Throwable 和它的跟踪情况打印到指定打印流。
printStackTrace(PrintWriter)
把该  Throwable 和它的跟踪情况打印到指定打印机。
toString()
返回该 throwable 对象的简短描述。

构造子

Throwable

 public Throwable()
构造一个无详细信息的新的  Throwable 。 将自动填写堆栈跟踪信息。

Throwable

 public Throwable(String message)
用指定的详细信息创建一新的  Throwable 。 将自动填写堆栈跟踪信息。
参数:
message - 详细消息。

方法

getMessage

 public String getMessage()
返回该 throwable 对象的详细信息。
返回值:
该  Throwable 的详细信息,如果该  Throwable 没有详细的信息,则返回  null 。

getLocalizedMessage

 public String getLocalizedMessage()
生成该  Throwable 的本地化描述。 子类可能会覆盖该方法以便产生一个特定于本地的消息。 对于未覆盖该方法的子类,缺省地返回调用  getMessage() 的结果。  

toString

 public String toString()
返回该 throwable 对象的简短描述。  
返回值:
表示该  Throwable 的字符串。
覆盖:
类  Object 中的  toString

printStackTrace

 public void printStackTrace()
把该  Throwable 和它的跟踪情况打印到标准错误流。  
参见:
err

printStackTrace

 public void printStackTrace(PrintStream s)
把该  Throwable 和它的跟踪情况打印到指定打印流。

printStackTrace

 public void printStackTrace(PrintWriter s)
把该  Throwable 和它的跟踪情况打印到指定打印机。

fillInStackTrace

 public native Throwable fillInStackTrace()
填写执行堆栈跟踪信息。 该方法在应用程序重新抛出错误或异常时有用。例如:
     try {
         a = b / c;
     } catch(ArithmeticThrowable e) {
         a = Number.MAX_VALUE;
         throw e.fillInStackTrace();
     }
 
返回值:
该  Throwable 对象。
参见:
printStackTrace
java.lang.Exception类介绍

java.lang 
Class Exception

java.lang.Object
  extended by java.lang.Throwable
      extended by java.lang.Exception
All Implemented Interfaces:
Serializable
public class Exception
   
   
    
    extends 
    
    Throwable
   
   

The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.

Since:
JDK1.0
See Also:
ErrorSerialized Form

Constructor Summary
Exception() 
          Constructs a new exception with null as its detail message.
Exception(String message) 
          Constructs a new exception with the specified detail message.
Exception(String message, Throwable cause) 
          Constructs a new exception with the specified detail message and cause.
Exception(Throwable cause) 
          Constructs a new exception with the specified cause and a detail message of (cause==null ? null : cause.toString()) (which typically contains the class and detail message of cause).
 
Method Summary
 
Methods inherited from class java.lang.Throwable
fillInStackTracegetCausegetLocalizedMessagegetMessagegetStackTraceinitCauseprintStackTraceprintStackTraceprintStackTracesetStackTracetoString
 
Methods inherited from class java.lang.Object
cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait
 

Constructor Detail

Exception

public Exception()
Constructs a new exception with  null as its detail message. The cause is not initialized, and may subsequently be initialized by a call to Throwable.initCause(java.lang.Throwable).


Exception

public Exception(String message)
Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently be initialized by a call to Throwable.initCause(java.lang.Throwable).

Parameters:
message - the detail message. The detail message is saved for later retrieval by the  Throwable.getMessage() method.

Exception

public Exception(String message,
                 Throwable cause)
Constructs a new exception with the specified detail message and cause.

Note that the detail message associated with cause is not automatically incorporated in this exception's detail message.

Parameters:
message - the detail message (which is saved for later retrieval by the  Throwable.getMessage() method).
cause - the cause (which is saved for later retrieval by the  Throwable.getCause() method). (A  null value is permitted, and indicates that the cause is nonexistent or unknown.)
Since:
1.4

Exception

public Exception(Throwable cause)
Constructs a new exception with the specified cause and a detail message of  (cause==null ? null : cause.toString()) (which typically contains the class and detail message of  cause). This constructor is useful for exceptions that are little more than wrappers for other throwables (for example, PrivilegedActionException).

Parameters:
cause - the cause (which is saved for later retrieval by the  Throwable.getCause() method). (A  null value is permitted, and indicates that the cause is nonexistent or unknown.)
Since:
1.4
12.6.1 栈轨迹

            printStackTrace()方法所提供的信息可以通过getStackTrace()方法直接访问。

//: exceptions/WhoCalled.java
// Programmatic access to stack trace information.

public class WhoCalled {
  static void f() {
    // Generate an exception to fill in the stack trace
    try {
      throw new Exception();
    } catch (Exception e) {
      for(StackTraceElement ste : e.getStackTrace())
        System.out.println(ste.getMethodName());
    }
  }
  static void g() { f(); }
  static void h() { g(); }
  public static void main(String[] args) {
    f();
    System.out.println("--------------------------------");
    g();
    System.out.println("--------------------------------");
    h();
  }
} /* Output:
f
main
--------------------------------
f
g
main
--------------------------------
f
g
h
main
*///:~
12.6.2 重新抛出异常
         重抛异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。

        如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息。此时,可以使用fillinStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的。

12.6.3 异常链

         常常会想要在捕获一个异常后抛出另一个异常,并且希望把原来异常的信息保存下来,这被称为异常链;

          所有Throwable的子类在构造器中都可以接受一个cause对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到以后藏最初发生的位置。

        在Throwable的子类中,只有三种基本类型的异常类提供了带cause参数的构造器,他们是Error(用于java虚拟机报告系统错误)、Exception、RuntimeException。如果要把其他类型的异常连接起来,应该使用initCause()方法而不是构造器。

12.7 java标准异常

        属于运行时异常的类型有很多,它们会自动被java虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是RuntimeException类继承而来。

       只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的,究其原因,RuntimeException代表的是编程错误:

1)无法预料的错误,比如在控制范围之外的null引用;2)程序员应该在代码中进行检查的错误。

12.8 使用finally进行清理

          需要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句,这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上的图形,甚至可以是外部世界的某个开关。

12.8.3 缺憾:异常丢失

//: exceptions/LostMessage.java
// How an exception can be lost.

class VeryImportantException extends Exception {
  public String toString() {
    return "A very important exception!";
  }
}

class HoHumException extends Exception {
  public String toString() {
    return "A trivial exception";
  }
}

public class LostMessage {
  void f() throws VeryImportantException {
    throw new VeryImportantException();
  }
  void dispose() throws HoHumException {
    throw new HoHumException();
  }
  public static void main(String[] args) {
    try {
      LostMessage lm = new LostMessage();
      try {
        lm.f();
      } finally {
        lm.dispose();
      }
    } catch(Exception e) {
      System.out.println(e);
    }
  }
} /* Output:
A trivial exception
*///:~
     抛出的异常被finally子句里的异常所取代。

     另外一种更加简单的丢失异常的方式是从finally子句中返回:

//: exceptions/ExceptionSilencer.java

public class ExceptionSilencer {
  public static void main(String[] args) {
    try {
      throw new RuntimeException();
    } finally {
      // Using 'return' inside the finally block
      // will silence any thrown exception.
      return;
    }
  }
} ///:~
12.9 异常的限制

        派生类的构造器的异常说明必须包含基类构造器的异常说明;另外派生类的构造器不能捕获基类构造器抛出的异常。

       派生类方法可以不抛出任何异常,即使它是基类所定义的异常。编译器会强制你捕获这个类所抛出的异常,但是如果向上转型成基类型,那么编译器就会要求你捕获基类的异常。

12.10 构造器

 

//: exceptions/Cleanup.java
// Guaranteeing proper cleanup of a resource.

public class Cleanup {
  public static void main(String[] args) {
    try {
      InputFile in = new InputFile("Cleanup.java");
      try {
        String s;
        int i = 1;
        while((s = in.getLine()) != null)
          ; // Perform line-by-line processing here...
      } catch(Exception e) {
        System.out.println("Caught Exception in main");
        e.printStackTrace(System.out);
      } finally {
        in.dispose();
      }
    } catch(Exception e) {
      System.out.println("InputFile construction failed");
    }
  }
} /* Output:
dispose() successful
*///:~
12.11 异常匹配

       抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。查找的时候并不要求抛出的异常同处理程序锁申明的异常完全匹配。派生类的对象也可以匹配其基类的处理程序。

12.12 其他可选方式

        异常处理的一个重要原则是“只有在你知道如何处理的情况下才捕获异常”。实际上,异常处理的一个重要目标是把错误处理的代码同错误发生的地点相分离。

        “被检查的异常”使这个问题变得有点复杂,因为它们强制你在可能还没有准备好处理错误的时候被迫加上catch子句,这就导致了吞食则有害(harmful ifswallowed)的问题。

       使用异常的好处实际上是:

1)有一致的,使用异常来报告错误的模型;

2)不在于什么时候进行检查,而是一定要有类型检查。也就是说,必须强制程序使用正确的类型,至于这种强制施加于编译时还是运行时,那倒没关系。

12.13 异常使用指南

  1. 在恰当的级别处理异常。(知道该如何处理的情况才捕获异常)
  2. 解决问题并且重新调用产生异常的方法。
  3. 进行少许修补,然后绕开异常发生的地方继续执行;
  4. 用别的数据进行计算,以代替方法预计会返回的值;
  5. 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层;
  6. 把当前运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层;
  7. 终止程序;
  8. 进行简化。(入股欧尼的异常模式使问题变得复杂,那用起来会非常痛苦也很烦人)
  9. 让类库和程序更安全。(这既是为调式做短期投资,也是在为程序的健壮性做长期投资)




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值