《Java编程思想》笔记12-通过异常处理错误
异常处理的两种基本模型
- 终止模型,Java 和 C++ 支持。假设错误非常关键,程序无法返回到异常发生的地方继续执行。
- 恢复模型,异常处理程序的工作是修正错误,然后重新调用出问题的方法,并认为第二次能成功。
如果 Java 想要实现类似恢复的行为,可以在遇见错误时不要抛出,而是调用方法来修正该错误。或者把 try 块放在 while 循环里,这样可以不断的进入 try 块中,直到得到满意的结果。
异常说明(throws)
可以声明方法抛出异常,实际并不抛出。好处是为异常占个位置,以后就可以抛出这种异常而不用修改已有代码。在定义抽象基类和接口时这种能力很重要,这样子类或接口实现就可以抛出这些预先声明的异常。
栈轨迹
printStackTrace() 方法所提供的信息可以通过 getStackTrace() 方法来直接访问。这个方法将返回一个由栈轨迹中的元素所构成的数组。
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; // 把当前对象重新抛出,printStackTrace() 方法显示的将是原来异常抛出点的调用栈信息,而非重新抛出点的信息。
}
}
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);
for(StackTraceElement ste : e.getStackTrace()) // 栈轨迹
System.out.println("栈轨迹:"+ste);
}
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 exceptions.Rethrowing.f(Rethrowing.java:8)
at exceptions.Rethrowing.g(Rethrowing.java:12)
at exceptions.Rethrowing.main(Rethrowing.java:30)
main: printStackTrace()
java.lang.Exception: thrown from f()
at exceptions.Rethrowing.f(Rethrowing.java:8)
at exceptions.Rethrowing.g(Rethrowing.java:12)
at exceptions.Rethrowing.main(Rethrowing.java:30)
栈轨迹:exceptions.Rethrowing.f(Rethrowing.java:8)
栈轨迹:exceptions.Rethrowing.g(Rethrowing.java:12)
栈轨迹:exceptions.Rethrowing.main(Rethrowing.java:30)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
at exceptions.Rethrowing.f(Rethrowing.java:8)
at exceptions.Rethrowing.h(Rethrowing.java:21)
at exceptions.Rethrowing.main(Rethrowing.java:38)
main: printStackTrace()
java.lang.Exception: thrown from f()
at exceptions.Rethrowing.h(Rethrowing.java:25)
at exceptions.Rethrowing.main(Rethrowing.java:38)
*///:~
异常链
想要在捕获一个异常后抛出另一个异常,并且把原始的异常的信息保存下来,这被称为异常链。
所有 Throwable 的子类在构造器中都可以接受一个 cause 对象作为参数。这个 cause 就用来表示原始异常,这样通过把原始异常传递给新的异常,就使得即使在当前位置创建并抛出新的异常,也能通过这个异常链追踪到异常最初发生的位置。在 Throwable 的子类中,只有三种基本的异常类提供了带 cause 参数的构造器。它们是 Error,Exception 和 RuntimeException。如果要把其他类型的异常链接起来,应该使用 initCause()
方法而不是构造器。
class DynamicFieldsException extends Exception {}
public class DynamicFields {
public void setField(String id, Object value) throws DynamicFieldsException {
if(value == null) {
DynamicFieldsException dfe = new DynamicFieldsException();
dfe.initCause(new NullPointerException());
throw dfe;
}
}
public static void main(String[] args) {
DynamicFields df = new DynamicFields();
try {
df.setField("number3", null);
} catch(DynamicFieldsException e) {
e.printStackTrace(System.out);
}
}
} /* Output:
exceptions.DynamicFieldsException
at exceptions.DynamicFields.setField(DynamicFields.java:18)
at exceptions.DynamicFields.main(DynamicFields.java:27)
Caused by: java.lang.NullPointerException
at exceptions.DynamicFields.setField(DynamicFields.java:19)
... 1 more
*///:~
缺陷:异常丢失
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(); // 丢失异常 VeryImportantException
} finally {
// try {
lm.dispose(); // 把这句也加上 try 块,就不会丢失了
// }catch(Exception e) {
// System.out.println(e);
// }
}
} catch(Exception e) {
System.out.println(e);
}
}
} /* Output:
A trivial exception
*///:~
另一种丢失方式,从 finally 字句中返回。
// 没有任何输出
public class ExceptionSilencer {
public static void main(String[] args) {
try {
throw new RuntimeException();
} finally {
return;
}
}
}
构造器
子类的构造器可以抛出任何异常,但 throws 必须包含父类构造器的异常说明。且不能捕获父类构造器抛出的异常(有隐式 super(),super()必须在子类构造器的第一行,所以无法捕获)。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com
文章标题:《Java编程思想》笔记12-通过异常处理错误
文章字数:1.1k
本文作者:Bin
发布时间:2018-03-28, 21:18:45
最后更新:2019-08-06, 00:46:42
原始链接:http://coolview.github.io/2018/03/28/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B012-%E9%80%9A%E8%BF%87%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。