12.2.1 异常参数
标准异常类有两个构造器,一个是默认构造器,另一个是接受字符串的构造器,如throw new NullPointerException("t=null");
12.4.1 异常与日志记录
e.printStackTrace(),默认形式,将方法调用序列打印到标准错误流,而e.printStackTrace(System.out)则打印到控制台
12.6 捕获所有异常
异常类型的基类Exception,应该放在异常处理程序的末尾,以防抢在其他异常处理程序之前,先把异常捕获了
e.printStackTrace()
e.printStackTrace(PrintStream)
e.printStackTrace(java.io.PrintWriter)
打印Throwable,和Throwable的调用栈轨迹,第一个输出到标准错误流,后两种输出到指定地点
以下演示Exception从Object继承来的方法
//: exceptions/ExceptionMethods.java
// Demonstrating the Exception Methods.
import static net.mindview.util.Print.*;
public class ExceptionMethods {
public static void main(String[] args) {
try {
throw new Exception("My Exception");
} catch(Exception e) {
print("Caught Exception");
print("getMessage():" + e.getMessage());
print("getLocalizedMessage():" +
e.getLocalizedMessage());
print("toString():" + e);
print("printStackTrace():");
e.printStackTrace(System.out);
}
}
} /* Output:
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
at ExceptionMethods.main(ExceptionMethods.java:8)
*///:~
12.6.2 重新抛出异常
重新抛出异常会把异常抛给上一级异常处理程序,同一个try块后的catch子句将被忽略。此外异常对象的所有信息都将得以保持,所以高一级环境中捕获此异常的处理程序将得到此异常的所有信息。fillInStackTrace()更新抛出栈信息
//: exceptions/Rethrowing.java
// Demonstrating fillInStackTrace()
public class Rethrowing {
public static void f() throws Exception {
System.out.println("originating the exception in f()");
throw new Exception("thrown from f()");
}
public static void g() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside g(),e.printStackTrace()");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside h(),e.printStackTrace()");
e.printStackTrace(System.out);
throw (Exception)e.fillInStackTrace();
}
}
public static void main(String[] args) {
try {
g();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
try {
h();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
}
} /* Output:
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.h(Rethrowing.java:20)
at Rethrowing.main(Rethrowing.java:35)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.h(Rethrowing.java:24)
at Rethrowing.main(Rethrowing.java:35)
*///:~
12.6.3 异常链
在设计模式中有一个叫做责任链模式,该模式是将多个对象链接成一条链,客户端的请求沿着这条链传递直到被接收、处理。同样Java异常机制也提供了这样一条链:异常链。
我们知道每遇到一个异常信息,我们都需要进行try…catch,一个还好,如果出现多个异常呢?分类处理肯定会比较麻烦,那就一个Exception解决所有的异常吧。这样确实是可以,但是这样处理势必会导致后面的维护难度增加。最好的办法就是将这些异常信息封装,然后捕获我们的封装类即可。
诚然在应用程序中,我们有时候不仅仅只需要封装异常,更需要传递。怎么传递?throws!!binge,正确!!但是如果仅仅只用throws抛出异常,那么你的封装类,怎么办??
我们有两种方式处理异常,一是throws抛出交给上级处理,二是try…catch做具体处理。但是这个与上面有什么关联呢?try…catch的catch块我们可以不需要做任何处理,仅仅只用throw这个关键字将我们封装异常信息主动抛出来。然后在通过关键字throws继续抛出该方法异常。它的上层也可以做这样的处理,以此类推就会产生一条由异常构成的异常链。
通过使用异常链,我们可以提高代码的可理解性、系统的可维护性和友好性。
同理,我们有时候在捕获一个异常后抛出另一个异常信息,并且希望将原始的异常信息也保持起来,这个时候也需要使用异常链。
在异常链的使用中,throw抛出的是一个新的异常信息,这样势必会导致原有的异常信息丢失,如何保持?在Throwable及其子类中的构造器中都可以接受一个cause参数,该参数保存了原有的异常信息,通过getCause()就可以获取该原始异常信息。
语法:
public void test() throws XxxException{
try {
//do something:可能抛出异常信息的代码块
} catch (Exception e) {
throw new XxxException(e);
}
}
复制代码
示例:
复制代码
public class Test {
public void f() throws MyException{
try {
FileReader reader = new FileReader("G:\\myfile\\struts.txt");
Scanner in = new Scanner(reader);
System.out.println(in.next());
} catch (FileNotFoundException e) {
//e 保存异常信息
throw new MyException("文件没有找到--01",e);
}
}
public void g() throws MyException{
try {
f();
} catch (MyException e) {
//e 保存异常信息
throw new MyException("文件没有找到--02",e);
}
}
public static void main(String[] args) {
Test t = new Test();
try {
t.g();
} catch (MyException e) {
e.printStackTrace();
}
}
}
复制代码
运行结果:
复制代码
com.test9.MyException: 文件没有找到--02
at com.test9.Test.g(Test.java:31)
at com.test9.Test.main(Test.java:38)
Caused by: com.test9.MyException: 文件没有找到--01
at com.test9.Test.f(Test.java:22)
at com.test9.Test.g(Test.java:28)
... 1 more
Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系统找不到指定的路径。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at java.io.FileReader.<init>(FileReader.java:41)
at com.test9.Test.f(Test.java:17)
... 2 more
复制代码
如果在程序中,去掉e,也就是:throw new MyException("文件没有找到--02");
那么异常信息就保存不了,运行结果如下:
com.test9.MyException: 文件没有找到--02
at com.test9.Test.g(Test.java:31)
at com.test9.Test.main(Test.java:38)
12.7 java标准异常
Throwable这个java类可以被用来表示任何可以作为异常被抛出的类,他的直接子类有两个,Error用来表示编译时或系统错误,Exception是可以被抛出的基本类型,在java库中,用户方法和运行时故障都可能抛出Exception异常
只能在代码中忽略RuntimeException及其子类的异常,其他类型的异常处理都是由编译器强制实施的,究其原因,RuntimeException代表的是编程错误:
1.无法预料的错误,如null异常
2.作为程序员,应该在代码中检查的错误,如数组越界异常
12.8 使用 finally进行清理
12.8.1 何时用到finally语句,即当要把除内存之外的所有资源恢复到他们的初始状态时,如已经打开的文件或网路连接,在屏幕上画的图形,甚至可以是外部世界的某个开关
甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在调到更高层的异常处理程序之前,执行finally子句()
若当前层的异常处理机制没有catch子句,那么该异常将留到上一层的异常处理机制中执行,会被上一层的catch子句捕获,不过或在当前层的finally子句之后(AlwaysFinally.java)
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
*///:~
//: 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 构造器
“这种通用的清理惯用法在构造器不抛出任何异常时也应该运用,其基本规则是:在创建要清理的对象之后,立即进入一个try-finally语句块”(Demo)Cleanup.java
//: 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 异常匹配
最近原则
若把捕获异常基类的catch子句放到所有catch子句的最前面,那么子类的异常将全部被屏蔽,编译器将向你报告错误
12.12 把“被检查时异常”转换为“检查时异常”(Demo)
方案一:直接把被检查的异常包装进RuntimeException,
try{ ...to do something useful}
catch(Idonknowwhattowiththischeckedexception e){
throw new RuntimeException(e);
}
//: exceptions/TurnOffChecking.java
// "Turning off" Checked exceptions.
import java.io.*;
import static net.mindview.util.Print.*;
class WrapCheckedException {
void throwRuntimeException(int type) {
try {
switch(type) {
case 0: throw new FileNotFoundException();
case 1: throw new IOException();
case 2: throw new RuntimeException("Where am I?");
default: return;
}
} catch(Exception e) { // Adapt to unchecked:
throw new RuntimeException(e);
}
}
}
class SomeOtherException extends Exception {}
public class TurnOffChecking {
public static void main(String[] args) {
WrapCheckedException wce = new WrapCheckedException();
// You can call throwRuntimeException() without a try
// block, and let RuntimeExceptions leave the method:
wce.throwRuntimeException(3);
// Or you can choose to catch exceptions:
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
print("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
throw re.getCause();
} catch(FileNotFoundException e) {
print("FileNotFoundException: " + e);
} catch(IOException e) {
print("IOException: " + e);
} catch(Throwable e) {
print("Throwable: " + e);
}
}
}
} /* Output:
FileNotFoundException: java.io.FileNotFoundException
IOException: java.io.IOException
Throwable: java.lang.RuntimeException: Where am I?
SomeOtherException: SomeOtherException
*///:~
另一种方案即是创建自己RuntimeException的子类