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 <init>
嚴重: exception.LoggingException
at exception.LoggingExceptions.main(LoggingExceptions.java:20)
caught1 exception.LoggingException
四月 26, 2019 2:10:06 下午 exception.LoggingException <init>
嚴重: 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