12.1.概念
看看百度百科的说法
12.2.基本异常
抛出异常以后会发生什么?首先使用new 在堆上创建异常对象,然后当前的执行被终止,并且弹出对异常对象的“引用”。此时异常处理机制接管程序,他的任务是使程序从错误张太重恢复,使程序要么换一种方式运行,要么继续运行下去。(好像和没说一样啊)
12.2.1异常参数
java的异常对象,也是一个对象,他有两个构造器。一个是默认无参构造器、另一个是传入一个字符串的构造器,这个字符串就是异常的报错信息。
12.3.捕获异常
12.3.1try块
try块的作用是捕获并处理异常,当有一段代码(或者其内部调用的方法)会抛出异常时,你可以选择继续向上抛出异常,要么就通过try包括可能出现异常的代码块。
12.3.2异常处理程序
异常是通过catch捕获的,catch内是抛出异常的类型或者其父类型。catch有点像switch
里的case,而且是每个case结束判断后有break形式,一旦捕获到一个异常就不能继续捕获后续的异常。
12.3.3终止与恢复
异常处理理论有两种模型,java支持终止模型。这种模型中,异常出现后程序不能到发生异常的地方继续执行。
另一种是恢复模型,希望出现异常后能修正错误。但是想要恢复代码使其争取执行,无疑需要出错的相关信息,导致代码的耦合性大大增加。因此不实用。
12.4.创建自定义异常
有时候,java本身提供的异常类可能不能描述你的异常,那么你可以自定义异常类。
class SimpleException extends Exception {}
public class InheritingExceptions {
public void f() throws SimpleException {
System.out.println("Throw impleException 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 impleException from f()
caught it!
*/
可以为异常类定义一个接受字符串参数的构造器
package exception;
class MyException extends Exception {
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
public class FullConstructors {
public static void f() throws MyException {
System.out.println("Throw MyException from f()");
throw new MyException();
}
public static void g() throws MyException {
System.out.println("Throw MyException from g()");
throw new MyException();
}
public static void main(String[] args) {
try {
f();
} catch (MyException e) {
e.printStackTrace(System.out);
}
try {
g();
} catch (MyException e) {
e.printStackTrace(System.out);
}
}
}
/*
Throw MyException from f()
exception.MyException
at exception.FullConstructors.f(FullConstructors.java:15)
at exception.FullConstructors.main(FullConstructors.java:25)
Throw MyException from g()
exception.MyException
at exception.FullConstructors.g(FullConstructors.java:20)
at exception.FullConstructors.main(FullConstructors.java:30)
*/
在异常处理程序中,调用了在throwable类生命的printStachTrace()方法。就像从输出中看到的,他将打印“从方法调用出到异常抛处的方法调用序列”。这里信息被发送到了System.out,并自动打印出来,但是如果不适用参数的话,信息则被发送到标准错误流。
12.4.1异常与记录日志 ((1)了解即可,(2)的getMessage可以看一下)
使用java.util.logging 工具包记录日志
package exception;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
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("caught1 "+e);
}
try {
throw new LoggingException();
}catch(LoggingException e) {
System.err.println("caught2 "+e);
}
}
}
/*
四月 26, 2019 2:10:06 下午 exception.LoggingException
严重: exception.LoggingException
at exception.LoggingExceptions.main(LoggingExceptions.java:20)
caught1 exception.LoggingException
四月 26, 2019 2:10:06 下午 exception.LoggingException
严重: exception.LoggingException
at exception.LoggingExceptions.main(LoggingExceptions.java:25)
caught2 exception.LoggingException
*/
我们将StringWriter对象传递给printWriter的构造器,通过toString()方法,我们就可以抽取一个String。(我也不明白啥意思,大概就是StringWriter trace = new StringWriter();
printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());这段代码能将错误信息转换成String罢了)
getMessage()看一下:
package exception;
class MyException2 extends Exception {
private int x;
public MyException2() {
}
public MyException2(String msg) {
super(msg);
}
public MyException2(String msg, int x) {
super(msg);
this.x = x;
}
public int val() {
return x;
}
public String getMessage() {
return "Detail Message " + x + " " + super.getMessage();
}
}
public class ExtreaFeatures {
public static void f() throws MyException2 {
System.out.println("Throwing MyException2 from f()");
throw new MyException2("Driginated in f()");
}
public static void g() throws MyException2 {
System.out.println("Throwing MyException2 from g()");
throw new MyException2("Driginated in g()");
}
public static void h() throws MyException2 {
System.out.println("Throwing MyException2 from h()");
throw new MyException2("Driginated in h()", 6);
}
public static void main(String[] args) {
try {
f();
} catch (MyException2 e) {
e.printStackTrace(System.out);
}
try {
g();
} catch (MyException2 e) {
e.printStackTrace(System.out);
}
try {
h();
} catch (MyException2 e) {
e.printStackTrace(System.out);
System.out.println("e.val()= " + e.val());
}
}
}
/*
Throwing MyException2 from f()
exception.MyException2: Detail Message 0 Driginated in f()
at exception.ExtreaFeatures.f(ExtreaFeatures.java:25)
at exception.ExtreaFeatures.main(ExtreaFeatures.java:37)
Throwing MyException2 from g()
exception.MyException2: Detail Message 0 Driginated in g()
at exception.ExtreaFeatures.g(ExtreaFeatures.java:29)
at exception.ExtreaFeatures.main(ExtreaFeatures.java:43)
Throwing MyException2 from h()
exception.MyException2: Detail Message 6 Driginated in h()
at exception.ExtreaFeatures.h(ExtreaFeatures.java:33)
at exception.ExtreaFeatures.main(ExtreaFeatures.java:49)
e.val()= 6
*/
这个异常增加了字段x,以及设定x的构造器。此外还覆盖了Throwable的getMessage()方法。getMessage方法一点类似toString()方法,从结果可以看出来我们可以打印出来。
12.5异常说明throws
java通过在接口或者方法上,对异常进行声明,来表示方法有哪些受检异常。
异常声明的形式应该是这样的
void f()throws TooBig,TooSmall,DivZero{…}
如果你在方法里面抛出(throw)异常的话,编译器会提示你应该throws这个异常,就像刚才的例子。此外,有些异常是不用抛出的,比如继承了RuntimeExecption的异常就不需要Throws声明异常。这种需要在方法上Throws的异常,我们称为受检异常。
12.6捕获所有异常
由于Exception是所有异常的子类,所以catch Exception就可以捕获到所有的异常
try{
...
}catch(Exception e){
...
}
实际上,这样使我们对出现的异常,得到的信息少之又少。但是如果只能捕获Exception的情况下,我们可以尽量获得更多的信息。Exception其父类Throwable有一些方法可以重用,String getMessage()、StringLocalizedMessage() void printStackTrace()等等,具体使用的例子我就不写了。
12.6.1栈轨迹
printStackTrace()方法所提供的信息可以通过getStackTrace()方法来访问,这个方法返回的是一个所调用方法名称构成的 数组。元素0是调用序列的最后调用的方法。
package exception;
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
*/
在这里我们只打印了方法名字,需要其他信息可以 System.out.println(ste); 可以打印出错的位置你可以自己试一下。
12.6.2重新抛出异常
有时候希望把刚不获得异常重新抛出(适用于什么情况?),尤其在使用Exception,捕获异常的时候
既然已经得到了当前异常对象的引用可以直接把他抛出。
catch(Exception e){
throw e;
}
注意throw 抛出异常会使你抛出异常到上一级环境,此外同一个try块的后续catch子语句都被忽略。另外printStacjTrace()方法现实的是原异常抛出点的信息,而不是重新抛出点的信息,要想更新这个信息,可以调用fillInStackTrace()方法。
(1)例子:
package exception;
public class Rethrowing {
public static void f() throws Exception {
System.out.println("orignating the exceptioin in f()");
throw new Exception("throw 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("main1:printStackTrace()");
e.printStackTrace(System.out);
}
try {
h();
} catch (Exception e) {
System.out.println("main2:printStackTrace()");
e.printStackTrace(System.out);
}
}
}
//注意"main1:printStackTrace()"、"main2:printStackTrace()"之后的结果
由于使用了方法e.fillInStackTrace(),抛出异常的位置在上层了。
/*
orignating the exceptioin in f()
Inside g() ,e.printStackTrace()
java.lang.Exception: throw from f()
at exception.Rethrowing.f(Rethrowing.java:6)
at exception.Rethrowing.g(Rethrowing.java:11)
at exception.Rethrowing.main(Rethrowing.java:29)
main1:printStackTrace()
java.lang.Exception: throw from f()
at exception.Rethrowing.f(Rethrowing.java:6)
at exception.Rethrowing.g(Rethrowing.java:11)
at exception.Rethrowing.main(Rethrowing.java:29)
orignating the exceptioin in f()
Inside h() ,e.printStackTrace()
java.lang.Exception: throw from f()
at exception.Rethrowing.f(Rethrowing.java:6)
at exception.Rethrowing.h(Rethrowing.java:20)
at exception.Rethrowing.main(Rethrowing.java:35)
main2:printStackTrace()
java.lang.Exception: throw from f()
at exception.Rethrowing.h(Rethrowing.java:24)
at exception.Rethrowing.main(Rethrowing.java:35)
*/
(2)当你捕获到异常后再次抛出异常,对于第二个异常来说会丢失前一个异常的信息,他只是知道异常发生在第二次抛出异常的地方。
例子略。
12.6.3异常链
针对上面的情况,我们想要在捕获异常后抛出另一个异常,并且保存前一个异常的信息,这个思路就叫异常链。Throwable的子类的构造其中可以传一个参数进去。
============2019.4.30