第十二章 通过异常处理错误(中)
12.4 创建自定义异常
我们不必拘泥于Java中已有的异常类型,Java提供的异常体系不可能预见所有的错误,所以可以自己定义异常类来表示程序中可能会遇到的特定问题:要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承,建立新的异常类最简单的方法就是让编译器为你产生默认构造器,所以这几乎不用写多少代码:
class SimpleException extends Exception { } public class InheritingException { public void f() throws SimpleException { System.out.println("Throw SimpleException from f()"); throw new SimpleException(); } public static void main(String[] args) { InheritingException sed = new InheritingException(); try { sed.f(); } catch (SimpleException e) { System.out.println("Caught it!"); } } }
输出结果为:
Throw SimpleException from f()
Caught it!
编译器创建了默认构造器,他将自动调用基类的默认构造器。上面的例子的结果被打印在控制台上,但是我们也许想通过写入System.err而将错误发送给标准错误流,通常这比把错误信息输出到System.out要好,因为System.out也许会被重定向,如果发送到System.errr他就不会随着System.out一起被重定向,这样更容易被用户注意。
也可以为异常类定义一个接受字符串参数的构造器:
package com.example.demo.exceptions; class MyException extends Exception { public MyException() {} public MyException(String msg) {super(msg);} } public class FullConstructors { public static void f() throws MyException { System.out.println("Throwing MyException from f()"); throw new MyException(); } public static void g() throws MyException { System.out.println("Throwing MyException from g()"); throw new MyException("Originated in g()"); } public static void main(String[] args) { try { f(); } catch (MyException e) { e.printStackTrace(System.out); } try { g(); } catch (MyException e) { e.printStackTrace(System.out); } } }
输出结果为:
Throwing MyException from f()
com.example.demo.exceptions.MyException
at com.example.demo.exceptions.FullConstructors.f(FullConstructors.java:11)
at com.example.demo.exceptions.FullConstructors.main(FullConstructors.java:19)
Throwing MyException from g()
com.example.demo.exceptions.MyException: Originated in g()
at com.example.demo.exceptions.FullConstructors.g(FullConstructors.java:15)
at com.example.demo.exceptions.FullConstructors.main(FullConstructors.java:24)
12.4.1 异常与记录日志
你可能还想使用java.util.logging工具将输出记录到日志中:
package com.example.demo.exceptions; import java.util.logging.*; import java.io.*; class LoggingException extends Exception { private static Logger logger = Logger.getLogger("LoggingException"); public LoggingException() { StringWriter trace = new StringWriter(); printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } } public class LoggingExceptions { public static void main(String[] args) { try { throw new LoggingException(); } catch (LoggingException e) { System.err.println("Caught " + e); } try { throw new LoggingException(); } catch (LoggingException e) { System.err.println("Caught " + e); } } }
输出结果为:
10月 05, 2019 7:36:51 下午 com.example.demo.exceptions.LoggingException <init>
严重: com.example.demo.exceptions.LoggingException
at com.example.demo.exceptions.LoggingExceptions.main(LoggingExceptions.java:18)
Caught com.example.demo.exceptions.LoggingException
10月 05, 2019 7:36:51 下午 com.example.demo.exceptions.LoggingException <init>
严重: com.example.demo.exceptions.LoggingException
at com.example.demo.exceptions.LoggingExceptions.main(LoggingExceptions.java:23)
Caught com.example.demo.exceptions.LoggingException
静态的Logger.getLogger方法创建了一个String参数相关联的Logger对象,这个Logger对象会将其输出发送到System.err,向Logger写入的最简单方式就是直接电泳与日志记录消息的级别相关联的方法,这里用的是severe()。为了产生日志记录消息,我们需要使用重载的printStackTrace()方法,他接受一个java.io.StringWriter对象传递给这个PrintWriter的构造器,那么通过调用toString()方法,就可以将输出抽取为一个String。
12.5 异常说明
Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员,这是种优雅的做法,他使得调用者能确切知道写什么样的代码可以捕获所有潜在的异常,当然如果提供了源代码,客户端程序员可以在源代码中查找throw语句来获知相关信息,然而程序库通常并不与源代码一起发布,为了预防这样的问题,Java提供了相应的语法,使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理,这就是异常说明。它属于方法声明的一部分,紧跟在形式参数列表之后。
异常说明使用了副驾的关键字throws,后面接一个所有潜在异常类型的列表,所以方法定义可能看起来就像是这样:
void f() throws TooBig, TooSmall, DivZero { //... }
但是要这样子写:
void f() { //... }
就表示这个方法不会抛出任何异常,除了从RuntimeException继承的异常。
12.6 捕获所有异常
可以只写一个异常处理程序来捕获所有类型的异常,通过捕获异常类型的、基类Exception,就可以做到这一点,事实上还有其他的基类,但Exception是同编程活动相关的基类:
catch(Exception e) {
System.out.println(“Caught an exception”);
}
这将捕获所有异常,所以最好把它放在处理程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了。因为Exception是与编程有关的所有异常类的基类,所以它不会含有太多具体的信息,不过可以调用它从基类Throwable继承的方法:
String getMessage()
String getLocalizedMessage()
用来获取详细信息,或用本地语言表示的详细信息。
12.6.1 栈轨迹
printStackTrace()方法所提供的信息可以通过getStackTrace方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。元素0是栈顶元素,并且是调用序列中的最后一个方法调用,数组中的最后一个元素和栈底是调用序列中的第一个方法调用,下面是一个简单的演示示例:
public class WhoCalled { static void f() { 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(); } }
输出结果为:
f
main
--------------------
f
g
main
--------------------
f
g
h
main
12.6.2 重新抛出异常
又是希望把刚捕获的异常重新抛出,尤其是在使用Exception捕获所有异常的时候,既然已经得到了对当前异常对象的引用,可以直接把它重新抛出:
catch(Exception e) {
System.out.println(“An exception was thrown”);
}
重抛异常会把异常抛给上一级环境的异常处理程序,同一个try块的后续catch子句将被忽略,此外,异常对象的所有信息都得到保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有信息。
如果只是把当前异常对象重新抛出,那么printStackTrace方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息,要想更新这个信息,可以调用fillInStackTrace方法,浙江返回一个Throwable对象,它是通过把当前调用栈信息填入原来的那个异常对象而建立的,像这样:
package com.example.demo.exceptions; 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); } } }
输出结果为:
originating the exception in f()
Inside g().e.printStackTrace()
java.lang.Exception: thrown from f()
at com.example.demo.exceptions.Rethrowing.f(Rethrowing.java:6)
at com.example.demo.exceptions.Rethrowing.g(Rethrowing.java:10)
at com.example.demo.exceptions.Rethrowing.main(Rethrowing.java:28)
main:printStackTrace()
java.lang.Exception: thrown from f()
at com.example.demo.exceptions.Rethrowing.f(Rethrowing.java:6)
at com.example.demo.exceptions.Rethrowing.g(Rethrowing.java:10)
at com.example.demo.exceptions.Rethrowing.main(Rethrowing.java:28)
originating the exception in f()
Inside h().e.printStackTrace()
java.lang.Exception: thrown from f()
at com.example.demo.exceptions.Rethrowing.f(Rethrowing.java:6)
at com.example.demo.exceptions.Rethrowing.h(Rethrowing.java:19)
at com.example.demo.exceptions.Rethrowing.main(Rethrowing.java:35)
main:printStackTrace()
java.lang.Exception: thrown from f()
at com.example.demo.exceptions.Rethrowing.h(Rethrowing.java:23)
at com.example.demo.exceptions.Rethrowing.main(Rethrowing.java:35)
原文地址:https://www.cnblogs.com/parable/p/11625638.html