12.1 概念
12.2 基本异常
throw new NullPointerException("t = null");
- 可以抛出任意类型的 Throwable 对象,它是异常类型的根类
12.3 捕获异常
try {
} catch(Type1 id1) {
} catch(Type2 id2) {
}
终止与恢复
- 终止模型,认为错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行
- 恢复模型,认为异常处理的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功,希望异常被处理完后能够继续执行程序
12.4 创建自定义异常
class SimpleException extends Exception {} //自定义异常
public class InheritingExceptions {
public void f() throws SimpleException {
System.out.println("Throw SimpleException from f()");
throw new SimpleException(); //抛出异常
}
public static void main(String[] args) {
InheritingExceptions sed = new InheritingExceptions();
try {
sed.f();
} catch (SimpleException e) { //捕获异常
System.out.println("Caught it!");
}
}
}
/*
Throw SimpleException from f()
Caught it!
*/
e.printStackTrace(System.out); //打印从方法调用处到异常抛出处的方法调用序列
import java.util.logging.*;
import java.io.*;
//通过异常输出日志
class LoggingException extends Exception {
//与String相关的Logger对象,将其输出发送到System.err
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);
}
}
}
/*
九月 21, 2020 10:01:13 下午 learn.LoggingException <init>
严重: learn.LoggingException
at learn.LoggingExceptions.main(LoggingExceptions.java:16)
Caught learn.LoggingException
九月 21, 2020 10:01:13 下午 learn.LoggingException <init>
严重: learn.LoggingException
at learn.LoggingExceptions.main(LoggingExceptions.java:21)
Caught learn.LoggingException
*/
- 必须在异常处理程序中生成日志信息
12.5 异常说明
- 要告知客户端程序员某个方法可能抛出异常的类型,它属于方法声明的一部分
void f() throws TooBig,TooSmall,DivZero {}
- 说明中抛出的异常必须在方法中写出异常的处理,即try {} catch {}结构
12.6 捕获所有异常
catch(Exception e) {}
//可以调用它从基类 Throwable 中继承的方法
String getMessage();
String getLocalizedMessage()
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)
public class ExceptionMethods {
public static void main(String[] args) {
try {
throw new Exception("My Exception");
} catch(Exception e) {
System.out.println("Caught Exception");
System.out.println("getMessage():" + e.getMessage());
System.out.println("getLocalizedMessage():" + e.getLocalizedMessage());
System.out.println("toString():" + e);
System.out.println("printStackTrace():");
e.printStackTrace(System.out);
}
}
}
/*
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
at learn.ExceptionMethods.main(ExceptionMethods.java:6)
*/
public class WhoCalled {
static void f() {
try {
throw new Exception();
} catch (Exception e) {
//e.getStackTrace() 获取由栈轨迹元素构成的数组
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();
System.out.println("--------------------------------");
}
}
/*
f
main
--------------------------------
f
g
main
--------------------------------
f
g
h
main
--------------------------------
*/
catch(Exception e) {
throw e;
}
//重新抛出异常,把异常信息抛给上一级环境中的异常处理程序
package learn;
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();
throw (Exception)e.fillInStackTrace();
}
}
public static void h() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside h(),e.printStackTrace");
e.printStackTrace();
throw (Exception)e.fillInStackTrace();
}
}
public static void main(String[] args) {
try {
g();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace();
}
try {
h();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace();
}
}
}
/*
originating the exception in f()
Inside g(),e.printStackTrace
main: printStackTrace()
originating the exception in f()
Inside h(),e.printStackTrace
main: printStackTrace()
java.lang.Exception: thrown from f()
at learn.Rethrowing.f(Rethrowing.java:6)
at learn.Rethrowing.g(Rethrowing.java:10)
at learn.Rethrowing.main(Rethrowing.java:28)
java.lang.Exception: thrown from f()
at learn.Rethrowing.g(Rethrowing.java:14)
at learn.Rethrowing.main(Rethrowing.java:28)
java.lang.Exception: thrown from f()
at learn.Rethrowing.f(Rethrowing.java:6)
at learn.Rethrowing.h(Rethrowing.java:19)
at learn.Rethrowing.main(Rethrowing.java:34)
java.lang.Exception: thrown from f()
at learn.Rethrowing.h(Rethrowing.java:23)
at learn.Rethrowing.main(Rethrowing.java:34)
*/
- 只是抛出当前异常,printStackTrace将显示原来异常抛出点的调用栈信息,可以调用fillStackTrace方法更新异常信息为重新抛出点的信息
- 调用fillStackTrace的那一行就变成新的异常发生地了
- 异常链,在捕获一个异常后抛出另一个异常
Error(cause)
Exception(cause)
RuntimeException(cause)
initCause()
public Object setField(String id,Object value) throws MyException {
if(value == null) {
MyException me =
new MyException();
me.initCause(new YourException());
//通过initCause()把YourException异常对象插入
throw me;
}
}
catch(NoSuchFieldException e) {
throw new RuntimeException(e);
//使用RuntimeException()构造器把NoSuchFieldException异常转换成RuntimeException异常
}
12.7 Java标准异常
- RuntimeException 运行时异常不必手动抛出
- RuntimeException 没有被捕获而直接直达 main ,程序退出前会自动调用 printStackTrace()
12.8 使用 finally 进行清理
try {
} catch() {
} catch() {
} finally {
//这里的代码一定会执行,无论异常是否抛出
}
- 当要把除内存之外的资源恢复到初始状态的时候,就要用finally,如已打开的文件或绘制的图形
- 即使在 try { return; } 中有return 语句,也能执行 finally 的清理工作
- 在 finally 中抛出异常,可能会覆盖 try 中抛出的异常,从而无法在 catch 中捕获异常,这是 Java 的语言缺陷
12.9 异常的限制
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning {
public Inning() throws BaseballException {}
public void event() throws BaseballException {
//没有真的抛出异常
}
public abstract void atBat() throws Strike,Foul;
public void walk() {} //不抛出受检查的异常
}
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
interface Storm {
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}
public class StormyInning extends Inning implements Storm {
//构造器可以增加新的异常,但必须处理基类异常 BaseballException
public StormyInning()
throws RainedOut,BaseballException {}
public StormyInning(String s)
throws Foul,BaseballException {}
//普通方法必须按照基类抛出异常,不能添加
//void walk() throws PopFoul {}
//接口不能向基类中已存在的方法中添加异常
//public void event() throws RainedOut {}
//即使基类中有异常,你也可以不抛出异常
public void event() {}
//覆盖的方法可以抛出继承的异常
public void atBat() throws PopFoul {}
public static void main(String[] args) {
try {
//导出类
StormyInning si = new StormyInning();
si.atBat();
} catch(PopFoul e) {
System.out.println("Pop Foul");
} catch(RainedOut e) {
System.out.println("Rained Out");
} catch(BaseballException e) {
System.out.println("Baseball Exception");
}
try {
//基类
Inning i = new StormyInning();
i.atBat();
} catch(Strike e) {
System.out.println("Strike");
} catch(Foul e) {
System.out.println("Foul");
} catch(RainedOut e) {
System.out.println("RainedOut");
} catch(BaseballException e) {
System.out.println("Baseball Exception");
}
}
}
- 声明抛出的异常实际可以不抛出
- 接口中有和基类中相同的方法,则在导出类中该方法不能抛出新的异常
- 如果接口中抛出的异常不是来自基类,则没有限制添加新的异常
- 异常限制对构造器不起作用,派生类构造器的异常必须包含基类构造器的异常说明,派生类构造器不能捕获基类抛出的异常
- 派生类普通方法抛出了异常,基类中没有抛出,就会发生编译错误
- 覆盖后的方法可以不抛出任何异常
- main 中,如果处理派生类对象,则只要处理这个类声明的异常,如果声明为基类,则要求处理捕获基类的异常
12.10 构造器
import java.io.*;
public class InputFile {
private BufferedReader in;
public InputFile(String fname) {
try {
in = new BufferedReader(new FileReader(fname));
} //这里是打开文件错误,不需要关闭文件
catch (FileNotFoundException e) {
//throw e;
} //其他类型的错误则必须关闭文件
catch (Exception e) {
try {
in.close();
} catch (IOException e) {
}
throw e;
}
}
public String getLine() {
String s;
try {
s = in.readLine();
} catch (IOException e) {
//将读行错误异常转化为一个RuntimeException,表示一个编程错误
throw new RuntimeException("readLine() failed");
}
return s;
}
public void dispose() {
try {
in.close();
} catch(IOException e) {
throw new RuntimeException("in.close() failed");
}
}
}
12.11 异常匹配
- 抛出异常,会匹配最近的处理程序
- 派生类异常也可以匹配基类的处理程序
class BException extends Exception {}
class DException extends BException {}
public class Human {
public static void main(String[] args) {
try {
throw new DException();
} catch(DException e) {
System.out.println("DException");
} catch(Exception e) {
System.out.println("Exception");
}
try {
throw new DException();
} catch(BException e) {
System.out.println("BException");
}
}
}
/*
DException
BException
*/
12.12 其他可选方式
- 只有在你知道如何处理的时候才抛出异常
(1)受检查的异常:这种在编译时被强制检查的异常称为"受检查的异常"。即在方法的声明中声明的异常。
(2)不受检查的异常:在方法的声明中没有声明,但在方法的运行过程中发生的各种异常被称为"不被检查的异常"。这种异常是错误,会被自动捕获。
为了保证程序健壮性 - 必须要有一致的,使用异常来报告错误的模型
- 必须要有类型检查,不管是在编译期间还是运行期间
public static void main(String[] args) throws Exception {}
//在main中添加Exception异常说明,通过把它传递到控制台,就不必在main中写try-catch语句了
12.13 异常使用指南
1)在恰当的级别处理
2)解决问题并且重新调用产生异常的方法
3)进行少量的修补,然后绕过异常发生的地方继续执行
4)用别的数据进行计算,以替代方法预计会返回的值
5)把当前运行环境下能做的事情做完,然后把相同的异常抛到更高层
6)把当前运行环境下能做的事情做完,然后把相同的异常抛到更高层
7)终止程序
8)进行简化
9)让类库和程序更安全