异常这一块知识点小而杂,整理一下便于以后查找。
1. Java异常Exception的结构分析
Throwable
Throwable是 Java 语言中所有错误或异常的超类。
Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。
Exception
Exception及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。
RuntimeException
RuntimeException代表编程错误。编译器不会检查RuntimeException异常,如果RuntimeException没有被捕获而直达main,那么程序在推出前将调用printStackTrace()方法。虽然Java编译器不会检查运行时异常,但是我们也可以通过throws进行声明抛出,也可以通过try-catch对它进行捕获处理。
Error
和Exception一样,Error也是Throwable的子类。表示编译时和系统错误。和RuntimeException一样,编译器也不会检查Error。
2.自定义异常
//: exceptions/LoggingExceptions.java // An exception that reports through a Logger. import com.sun.javafx.util.Logging; import java.util.logging.*; import java.io.*; //自定义异常类必须继承已有异常类 class LoggingException extends Exception { private static Logger logger = Logger.getLogger("LoggingException"); //可加入额外成员 private int x; //覆盖getMessage public String getMessage(){ return "Detail Message: "+x+" "+super.getMessage(); } //可以有自己的构造器 public LoggingException(String msg ,int i) { super(msg); this.x = i; //为了获取异常Sting,重载printStackTrace(),并将StringWriter传给PrintWriter构造器, // 调用StringWriter.toString()将获得异常String StringWriter trace = new StringWriter(); printStackTrace(new PrintWriter(trace)); //java.util.logging.Logger将输出发送到System.err logger.severe(trace.toString()); } } public class LoggingExceptions { public static void main(String[] args) { try { throw new LoggingException("throw LoggingException from main", 5); } catch(LoggingException e) { //通过System.err将错误发送给标准错误流,可以通过System.setErr(PrintStream sttream)重定向 System.err.println("Caught " + e); } } } /* Output: 十月 03, 2018 12:33:14 下午 LoggingException <init> 严重: LoggingException: Detail Message: 5 throw LoggingException from main at LoggingExceptions.main(LoggingExceptions.java:33) Caught LoggingException: Detail Message: 5 throw LoggingException from main *///:~
自定义异常只需继承已有的异常类,并可加入额外的成员和构造器,重写覆盖已有的方法便可打印不同的信息。System.err将错误发送给标准错误流,通常比System.out要好,如果把结果送到System.err,就不会随System.out一起被重定向(Systrm.setOut(PrintStream sttream)),当然System.err也可以单独重定向。java.util.logging.Logger将输出记录到日志中。
3.常用方法
String getMessage():获取详细信息
String getLocalizedMessage():获取本地语言表示的详细信息
String toString()返回对Throwable的简单描述
void printStackTrace()打印Throwable和Throwable的调用栈轨迹,输出到标准错误流
void printStackTrace(PrintStream) 打印Throwable和Throwable的调用栈轨迹,输出到PrintStream
void printStackTrace(java.io.PrintWriter) 打印Throwable和Throwable的调用栈轨迹,输出到PrintWriter
StackTraceElement getStackTrace()返回栈轨迹数组,0是栈顶元素(Throwable被创建和抛出之处)
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 *///:~
Throwable fillStackTrace() 在Throwable对象内部记录栈帧的当前状态
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()"); //f->g->main 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); //有关原来异常发生点的信息会丢失,剩下与新抛出点有关信息:h->main,丢了f的栈轨迹 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) *///:~
4.异常链
捕获一个异常后抛出另一个异常,并且希望把原是异常信息保存下来 ,这被称为异常链。所有Throwable的子类在构造器中都可以接受一个cause对象作为参数,这个cause就用来表示原始异常。Throwable子类中只有Error、Exception、RuntimeException三种基本异常类提供了带cause参数的构造器,其他不带cause参数构造器的异常,可以用initCause()方法。
class DynamicFieldsException extends Exception { DynamicFieldsException(){ super(); } //创建带有cause构造器 DynamicFieldsException(Throwable cause){ super(cause); } } public class DynamicFields { public static void main(String[] args) { int i = 3; //将try语句块放入循环中,便可回到异常抛出点,重新尝试调用有问题的方法 while(i > 0){ try{ if(i == 3){ DynamicFieldsException dfe = new DynamicFieldsException(); //通过initCause生成异常链 dfe.initCause(new NullPointerException()); throw dfe; }else if (i==2){ //通过构造器生成异常链 throw new DynamicFieldsException(new IllegalArgumentException()); }else{ //如果代码块中无异常抛出,也可写成try-finally System.out.println("before --i: "+i); } }catch(DynamicFieldsException e) { e.printStackTrace(System.out); } //不管有没有异常被捕获,finally中的语句都将在方法返回前被执行 finally{ System.out.println("after --i: "+--i); } } } } /* Output: DynamicFieldsException at DynamicFields.main(DynamicFields.java:23) Caused by: java.lang.NullPointerException at DynamicFields.main(DynamicFields.java:25) after --i: 2 DynamicFieldsException: java.lang.IllegalArgumentException at DynamicFields.main(DynamicFields.java:29) Caused by: java.lang.IllegalArgumentException ... 1 more after --i: 1 before --i: 1 after --i: 0 *///:~
5.异常限制
1.派生类构造器异常说明必须包含基类构造器异常说明,即子类构造器抛出的异常必须大于等于父类构造器。
2.在继承和覆盖中,某个方法的异常说明变小了,即子类方法的异常说明要小于等于父类或接口中方法的异常说明
class ClassException extends Exception {} class ClassException1 extends ClassException {} class ClassException2 extends ClassException {} class ClassException11 extends ClassException1 {} abstract class ClassFather { public ClassFather() throws ClassException1 {} public void event() throws ClassException {} public abstract void atBat() throws ClassException1, ClassException2; } class InterfaceException extends Exception {} class InterfaceException1 extends InterfaceException {} interface Interface { public void event() throws InterfaceException1; public void rainHard() throws InterfaceException1; } public class StormyInning extends ClassFather implements Interface { //子类构造器抛出的异常必须大于等于父类构造器,ClassException≥ClassException1,可以包含别的异常 public StormyInning() throws InterfaceException, ClassException {} public StormyInning(String s)throws ClassException1 {} public void rainHard() throws InterfaceException1 {} //子类方法的异常说明要小于等于父类或接口中方法的异常说明 public void atBat() throws ClassException11 {} //public void event() throws InterfaceException1,ClassException{} public void event(){}; } ///:~
6.main()作为一个方法也可以有异常说明
import java.io.*; public class MainException { // 异常信息传递到控制台,main中不必try-catch public static void main(String[] args) throws Exception { FileInputStream file = new FileInputStream("MainException.java"); file.close(); } } /* Output: Exception in thread "main" java.io.FileNotFoundException: MainException.java (系统找不到指定的文件。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileInputStream.<init>(FileInputStream.java:93) at MainException.main(MainException.java:7) *///:~
7.吞食包装
如果在捕获异常后不做任何处理,程序继续向下执行,则这个异常就会被吞食消失,可以通过包装成自定义异常继续向上抛出或者用getCause()方法将原始异常继续向上抛出。
import java.io.*; 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) { //捕获异常后包装成别的异常向上抛出 throw new RuntimeException(e); } } } class SomeOtherException extends Exception {} public class TurnOffChecking { public static void main(String[] args) { WrapCheckedException wce = new WrapCheckedException(); wce.throwRuntimeException(3); for(int i = 0; i < 4; i++) try { if(i < 3) wce.throwRuntimeException(i); else throw new SomeOtherException(); } catch(SomeOtherException e) { System.out.println("SomeOtherException: " + e); } catch(RuntimeException re) { try { //向上抛出原异常 throw re.getCause(); } catch(FileNotFoundException e) { System.out.println("FileNotFoundException: " + e); } catch(IOException e) { System.out.println("IOException: " + e); } catch(Throwable e) { System.out.println("Throwable: " + e); } } } } /* Output: FileNotFoundException: java.io.FileNotFoundException IOException: java.io.IOException Throwable: java.lang.RuntimeException: Where am I? SomeOtherException: SomeOtherException *///:~