9.1 Java異常處理機(jī)制概述
主要考慮的兩個問題:(1)如何表示異常情況?(2)如何控制處理異常的流程?
9.1.1 Java異常處理機(jī)制的優(yōu)點
Java語言按照面向?qū)ο蟮乃枷雭硖幚懋惓#沟贸绦蚓哂懈玫目删S護(hù)性。
Java異常處理機(jī)制具有以下優(yōu)點:
- 把各種不同類型的異常情況進(jìn)行分類,用Java類來表示異常情況,發(fā)揮類的可擴(kuò)展性和可重用性。
- 異常流程的代碼和正常流程的代碼分離,提供了程序的可讀性,簡化了程序的結(jié)構(gòu)。
- 可以靈活地處理異常,如果當(dāng)前方法有能力處理異常,就捕獲并處理它,否則只需要拋出異常,由方法調(diào)用者來處理它。
9.1.2 Java虛擬機(jī)的方法調(diào)用棧
如果方法中的代碼塊可能拋出異常,有如下兩種處理方法:
(1)在當(dāng)前方法中通過try...catch語句捕獲并處理異常;
(2)在方法的聲明處通過throws語句聲明拋出異常。
當(dāng)Java虛擬機(jī)追溯到調(diào)用棧的底部的方法時,如果仍然沒有找到處理該異常的代碼,將按以下步驟處理:
(1)調(diào)用異常對象的printStachTrace()方法,打印來自方法調(diào)用棧的異常信息。
(2)如果該線程不是主線程,那么終止這個線程,其它線程繼續(xù)正常運行。如果該線程是主線程,那么整個應(yīng)用程序被終止。
9.1.3 異常處理對性能的影響
一般來說,影響很小,除非方法嵌套調(diào)用很深。
9.2 運用Java異常處理機(jī)制
9.2.1 try...catch語句:捕獲異常
9.2.2 finally語句:任何情況下都必須執(zhí)行的代碼
主要用于關(guān)閉某些流和數(shù)據(jù)庫連接。
9.2.3 thorws子句:聲明可能會出現(xiàn)的異常
9.2.4 throw語句:拋出異常
9.2.5 異常處理語句的語法規(guī)則
(1)try代碼塊不能脫離catch代碼塊或finally代碼塊而單獨存在。try代碼塊后面至少有一個catch代碼塊或finally代碼塊。
(2)try代碼塊后面可以有零個或多個catch代碼塊,還可以有零個或至多一個finally代碼塊。
(3)try代碼塊后面可以只跟finally代碼塊。
(4)在try代碼塊中定義的變量的作用域為try代碼塊,在catch代碼塊和finally代碼塊中不能訪問該變量。
(5)當(dāng)try代碼塊后面有多個catch代碼塊時,Java虛擬機(jī)會把實際拋出的異常類對象依次和各個catch代碼塊聲明的異常類型匹配,如果異常對象為某個異常類型或其子類的實例,就執(zhí)行這個catch代碼塊,而不會再執(zhí)行其他的catch代碼塊。
(6)如果一個方法可能出現(xiàn)受檢查異常,要么用try...catch語句捕獲,要么用throws子句聲明將它拋出,否則會導(dǎo)致編譯錯誤。
9.2.6 異常流程的運行過程
(1)finally語句不被執(zhí)行的唯一情況是先執(zhí)行了用于終止程序的System.exit()方法。
(2)return語句用于退出本方法。
(3)finally代碼塊雖然在return語句之前被執(zhí)行,但finally代碼塊不能通過重新給變量賦值來改變return語句的返回值。
(4)建議不要在finally代碼塊中使用return語句,因為它會導(dǎo)致以下兩種潛在的錯誤
A:覆蓋try或catch代碼塊的return語句
public class SpecialException extends Exception {
public SpecialException() {
}
public SpecialException(String msg) {
super(msg);
}
}
public class FinallyReturn {
/**
* @param args
*/
public static void main(String[] args) {
FinallyReturn fr = new FinallyReturn();
System.out.println(fr.methodB(1));// 打印100
System.out.println(fr.methodB(2));// 打印100
}
public int methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
return money;
}
@SuppressWarnings("finally")
public int methodB(int money) {
try {
return methodA(money);// 可能拋出異常
} catch (SpecialException e) {
return -100;
} finally {
return 100;// 會覆蓋try和catch代碼塊的return語句
}
}
}
B:丟失異常
public class ExLoss {
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out.println(new ExLoss().methodB(1));// 打印100
System.out.println("No Exception");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public int methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
return money;
}
@SuppressWarnings("finally")
public int methodB(int money) {
try {
return methodA(money);// 可能拋出異常
} catch (SpecialException e) {
throw new Exception("Wrong");
} finally {
return 100;// 會丟失catch代碼塊中的異常
}
}
}
9.3 Java異常類
所有異常類的祖先類為java.lang.Throwable類,它的實例表示具體的異常對象,可以通過throw語句拋出。
Throwable類提供了訪問異常信息的一些方法,常用的方法包括:
- getMessage() --返回String類型的異常信息。
- printStachTrace()--打印跟蹤方法調(diào)用棧而獲得的詳細(xì)異常信息。在程序調(diào)試階段,此方法可用于跟蹤錯誤。
public class ExTrace {
/**
* @param args
*/
public static void main(String[] args) {
try {
new ExTrace().methodB(1);
} catch (Exception e) {
System.out.println("--- Output of main() ---");
e.printStackTrace();
}
}
public void methodA(int money) throws SpecialException {
if (--money <= 0) {
throw new SpecialException("Out of money");
}
}
public void methodB(int money) throws Exception {
try {
methodA(money);
} catch (SpecialException e) {
System.out.println("--- Output of methodB() ---");
System.out.println(e.getMessage());
throw new Exception("Wrong");
}
}
}
打印結(jié)果:
--- Output of methodB() ---
Out of money
--- Output of main() ---
java.lang.Exception: Wrong
at chapter09.d0903.ExTrace.methodB(ExTrace.java:45)
at chapter09.d0903.ExTrace.main(ExTrace.java:26)
Throwable類有兩個直接子類:
- Error類--表示僅靠程序本身無法恢復(fù)的嚴(yán)重錯誤,如內(nèi)存不足等。
- Exception類--表示程序本身可以處理的異常。
9.3.1 運行時異常
RuntimeException類及其子類都被稱為運行時異常,這種異常的特點是Java編譯器不會檢查它,會編譯通過,但運行時如果條件成立就會出現(xiàn)異常。
例如當(dāng)以下divided()方法的參數(shù)b為0,執(zhí)行“a/b”操作時會出現(xiàn)ArrithmeticException異常,它屬于運行時異常,Java編譯器不會檢查它。
public int divide2(int a, int b) {
return a / b;// 當(dāng)參數(shù)為0,拋出ArrithmeticException
}
下面的程序中的IllegalArgumentException也是運行時異常,divided()方法即沒有捕獲它,也沒有聲明拋出它。
public class WithRuntimeEx {
/**
* @param args
*/
public static void main(String[] args) {
new WithRuntimeEx().divide(1, 0);
System.out.println("End");
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("除數(shù)不能為0");
}
return a / b;
}
}
由于程序代碼不會處理運行時異常,因此當(dāng)程序在運行時出現(xiàn)了這種異常時,就會導(dǎo)致程序異常終止。以上程序的打印結(jié)果為:
Exception in thread "main" java.lang.IllegalArgumentException: 除數(shù)不能為0
at chapter09.d0903.WithRuntimeEx.divide(WithRuntimeEx.java:29)
at chapter09.d0903.WithRuntimeEx.main(WithRuntimeEx.java:23)
9.3.2 受檢查異常
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于受檢查異常(Checked Exception)。這種異常要么catch語句捕獲,要么throws子句聲明拋出,否則編譯出錯。
9.3.3 區(qū)分運行時異常和受檢查異常
受檢查異常表示程序可以處理的異常。
運行時異常表示無法讓程序恢復(fù)運行的異常,導(dǎo)致這種異常的原因通常是由于執(zhí)行了錯誤操作。一旦出現(xiàn)了錯誤操作,建議終止程序,因此Java編譯器不檢查這種異常。
9.3.4 區(qū)分運行時異常和錯誤
Error類及其子類表示程序本身無法修復(fù)的錯誤,它和運行時異常的相同之處是:Java編譯器都不會檢查它們,當(dāng)程序運行時出現(xiàn)它們,都會終止程序。
兩者的不同之處是:Error類及其子類表示的錯誤通常是由Java虛擬機(jī)拋出。
而RuntimeException類表示程序代碼中的錯誤,它是可擴(kuò)展的,用戶可以根據(jù)特定的問題領(lǐng)域來創(chuàng)建相關(guān)的運行時異常類。
9.4 用戶定義異常
9.4.1 異常轉(zhuǎn)譯和異常鏈
public class BaseException extends Exception {
protected Throwable cause = null;
public BaseException() {
}
public BaseException(String msg) {
super(msg);
}
public BaseException(Throwable cause) {
this.cause = cause;
}
public BaseException(String msg, Throwable cause) {
super(msg);
this.cause = cause;
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() {
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new PrintStream(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null) {
getCause().printStackTrace(writer);
}
writer.flush();
}
}
9.4.2 處理多樣化異常
public class MultiBaseException extends Exception {
protected Throwable cause = null;
private List<Throwable> exceptions = new ArrayList<Throwable>();
public MultiBaseException() {
}
public MultiBaseException(Throwable cause) {
this.cause = cause;
}
public MultiBaseException(String msg, Throwable cause) {
super(msg);
this.cause = cause;
}
public List getException() {
return exceptions;
}
public void addException(MultiBaseException ex) {
exceptions.add(ex);
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() {
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new PrintStream(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null) {
getCause().printStackTrace(writer);
}
writer.flush();
}
}
9.5 異常處理原則
9.5.1 異常只能用于非正常情況
9.5.2 為異常提供說明文檔
9.5.3 盡可能地避免異常
9.5.4 保持異常的原子性
9.5.5 避免過于龐大的try代碼塊
9.5.6 在catch子句中指定具體的異常類型
9.5.7 不要在catch代碼塊中忽略被捕獲的異常,可以處理異常、重新拋出異常、進(jìn)行異常轉(zhuǎn)譯
posted on 2008-02-22 17:50
CoderDream 閱讀(888)
評論(0) 編輯 收藏 所屬分類:
Java-14.Exception