异常这一块知识点小而杂,整理一下便于以后查找。
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
*///:~