java編程思想-異常

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章