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
下列类的父类:
- Error, Exception
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(); }
构造子索引
方法索引
-
fillInStackTrace()
- 填写执行堆栈跟踪信息。 getLocalizedMessage()
-
生成该
Throwable
的本地化描述。
getMessage()
- 返回该 throwable 对象的详细信息。 printStackTrace()
-
把该
Throwable
和它的跟踪情况打印到标准错误流。
printStackTrace(PrintStream)
-
把该
Throwable
和它的跟踪情况打印到指定打印流。
printStackTrace(PrintWriter)
-
把该
Throwable
和它的跟踪情况打印到指定打印机。
toString()
- 返回该 throwable 对象的简短描述。
构造子
public Throwable()
-
构造一个无详细信息的新的
Throwable
。 将自动填写堆栈跟踪信息。
public Throwable(String message)
-
用指定的详细信息创建一新的
Throwable
。 将自动填写堆栈跟踪信息。 -
-
参数:
- message - 详细消息。
方法
public String getMessage()
- 返回该 throwable 对象的详细信息。
-
-
返回值:
-
该
Throwable
的详细信息,如果该Throwable
没有详细的信息,则返回null
。
-
该
public String getLocalizedMessage()
-
生成该
Throwable
的本地化描述。 子类可能会覆盖该方法以便产生一个特定于本地的消息。 对于未覆盖该方法的子类,缺省地返回调用getMessage()
的结果。
public String toString()
public void printStackTrace()
-
把该
Throwable
和它的跟踪情况打印到标准错误流。 -
-
参见:
- err
public void printStackTrace(PrintStream s)
-
把该
Throwable
和它的跟踪情况打印到指定打印流。
public void printStackTrace(PrintWriter s)
-
把该
Throwable
和它的跟踪情况打印到指定打印机。
public native Throwable fillInStackTrace()
-
填写执行堆栈跟踪信息。 该方法在应用程序重新抛出错误或异常时有用。例如:
try { a = b / c; } catch(ArithmeticThrowable e) { a = Number.MAX_VALUE; throw e.fillInStackTrace(); }
-
-
返回值:
-
该
Throwable
对象。
参见:
- printStackTrace
-
该
java.lang
Class Exception
java.lang.Objectjava.lang.Throwable
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:
-
Error
, Serialized 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 |
---|
fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
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 toThrowable.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 theThrowable.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 theThrowable.getMessage()
method). -
cause
- the cause (which is saved for later retrieval by theThrowable.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 theThrowable.getCause()
method). (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
Since:
- 1.4
-
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 异常使用指南
- 在恰当的级别处理异常。(知道该如何处理的情况才捕获异常)
- 解决问题并且重新调用产生异常的方法。
- 进行少许修补,然后绕开异常发生的地方继续执行;
- 用别的数据进行计算,以代替方法预计会返回的值;
- 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层;
- 把当前运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层;
- 终止程序;
- 进行简化。(入股欧尼的异常模式使问题变得复杂,那用起来会非常痛苦也很烦人)
- 让类库和程序更安全。(这既是为调式做短期投资,也是在为程序的健壮性做长期投资)