對於一個非常熟悉 C++ 異常處理模型的程序員來說,它幾乎可以不經任何其它培訓和學習,就可以完全接受和能夠輕松地使用 Java 語言中的異常處理編程方法。這是因為 Java 語言中的異常處理模型幾乎與 C++ 中異常處理模型有 99% 的相似度,無論是從語法規則,還是語義上來說,它們二者都幾乎完全一致。
當然,如果你對 Java 語言中的異常處理模型有更多,或更深入的了解,你還是能夠發現 Java 異常處理模型與 C++ 中異常處理模型還是存在不少差別的。是的,Java 語言本來就是 C++ 語言的完善改進版,所以,Java 語言中的異常處理模型也就必然會繼承了 C++ 異常處理模型的風格和優點。但是,好的東西不僅僅是需要繼承優點,更重要的是需要“去其糟粕,取其精華”,需要發展!!!毫無疑問,Java 語言中的異常處理模型完全達到了這一“發展”高度。它比 C++ 異常處理模型更安全,更可高,更強大和更豐富。
下面的內容中,阿愚不打算再向大家詳細介紹一遍有關 Java 異常處理編程的具體語法和規則。因為這與 C++ 中的異常處理幾乎完全一樣,而且這些基礎知識在太多太多的有關 java 編程的書籍中都有詳細闡述。而阿愚認為: 這裡更需要的是總結,需要的是比較,需要的是重點突出,需要的是關鍵之處 。所以,下面著重把 Java 語言中的異常處理模型與 C++ 異常處理模型展開比較,讓我們透徹分析它到底有何發展?有何優勢?與 C++ 異常處理模型到底有哪些細節上的不同?又為什麼要這樣做?
借鑒並引進了 SEH 異常模型中的 try-finally 語法
要說 Java 異常處理模型與 C++ 中異常處理模型的最大不同之處,那就是在 Java 異常處理模型中引入了 try-finally 語法,阿愚認為這是從微軟的 SEH 借鑒而來。在前面的一些文章中,詳細而深入闡述 SEH 異常處理模型的時候,我們從中獲知,SEH 主要是為 C 語言而設計的,便於第三廠商開發的 Window 驅動程序有更好更高的安全保障。同時,SEH 異常處理模型中除了 try-except 來用於處理異常外,還有一個 try-finally 語法,它主要用來清除一些曾經分配的資源(由於異常出現,而導致這些資源不能夠按正常的順序被釋放,還記得嗎?這被稱為“ UNWIND ”),try-finally 本質上有點類似於面向對象編程中的析構函數的作用,由於這項機制的存在,才導致 SEH 的強大和風光無比。
現在,Java 異常處理模型也吸收了這項設計。可我們知道,無論是 JAVA 中,還是 C++ 中,它們都有“析構函數”呀!它們完全可以利用面向的析構函數來自動釋放資源呀!是的,沒錯!理論上是這樣的。可是在實踐中,我們也許會發現或經常碰到,僅僅利用析構函數來釋放資源,並不是那麼好使,例如,我們經常需要動態得從堆上分配的對象,這時,釋放對象必須要顯式地調用 delete 函數來觸發該對象的析構函數的執行。如果這期間發生了異常,不僅該對象在堆中的內存不能達到被釋放的結果,而且,該對象的析構函數中釋放更多資源的一些代碼也不能得以執行。因此這種後果非常嚴重,這也算 C++ 異常處理模型中一種比較大的缺陷吧!(雖然,C++ 有其它補救措施,那就是利用“智能指針”來保證一些對象的及時而有效地被釋放)。另外,其實很有許多類似的例子,例如關閉一個內核句柄等操作( CloseHandle )。
在 Java 語言中,由於它由於采用了垃圾回收技術,所以它能有效避免上述所講的類似於 C++ 語言中的由於異常出現所導致的那種資源不能得以正確釋放的尴尬局面。但是,它仍然還是在它的異常處理模型中引入了 try-finally 語法,因為,無論如何,它至少會給程序員帶來了極大的方便,例如如下的程序片斷,就可以充分反映出 try-finally 對提高我們代碼的質量和美觀是多麼的重要。
import java.io.*;
/** *//**
author by http://www.bt285.cn http://www.5a520.cn
*/
public class Trans
{
public static void main(String[] args)
{
try
{
BufferedReader rd=null;
Writer wr=null;
try
{
File srcFile = new File((args[0]));
File dstFile = new File((args[1]));
rd = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile), args[2]));
wr = new OutputStreamWriter(new FileOutputStream(dstFile), args[3]);
while(true)
{
String sLine = rd.readLine();
if(sLine == null) break;
wr.write(sLine);
wr.write("\r\n");
}
}
finally
{
// 這裡能保證在何種情況下,文件流的句柄都得以被正確關閉
// 該方法主要用於清理非內存性質的資源(垃圾回收機制無法
// 處理的資源,如數據庫連接、 Socket 關閉、文件關閉等等)。
wr.flush();
wr.close();
rd.close();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
所有的異常都必須從 Throwable 繼承而來
在 C++ 異常處理模型中,它給予程序員最大的自由度和發揮空間(這與 C++ 中所主導的設計思想是一致的),例如它允許程序員可以拋出任何它想要的異常對象,它可以是語言系統中原本所提供的各種簡單數據類型(如 int 、 float 、 double 等),也可以是用戶自定義的抽象數據對象(如 class 的 object 實例)。雖然說,無論從何個角度上考量,我們都應該把異常處理與面向對象緊密結合起來(采用帶有繼承特點的層次化的類對象結構來描述系統中的各種異常),但是 C++ 語言規范中並無此約束;況且,即便大家都不約而同地采用面向對象的方法來描述“異常”,但也會由於各個子系統(基礎運行庫)不是一個廠商(某一個人)所統一設計,所以導致每個子系統所設計出的異常對象系統彼此相差甚遠。這給最終使用(重用)這些庫的程序員帶來了很大的不一致性,甚至是很大的麻煩,我們不僅需要花費很多很寶貴的時間來學習和熟悉這些不同的異常對象子系統;而且更大的問題是,這些不同子系統之間語義上的不一致,而造成程序員在最終處理這些異常時,將很難把它們統一起來,例如,MFC 庫系統中,采用 CMemoryException 來表示一個與內存操作相關的異常;而其它的庫系統中很可能就會采用另外一個 class 來表示內存操作的異常錯誤。本質上說,這是由於缺乏規范和統一所造成的惡劣後果,所以說,如果在語言設計的時候,就充分考慮到這些問題,把它們納入語言的統一規范之中,這對廣大的程序員來說,無疑是個天大的好事情。
Java 語言毫無疑問很好地做到了這一點,它要求 java 程序中(無論是誰寫的代碼),所有拋出( throw )的異常都必須是從 Throwable 派生而來,例如,下面的代碼編譯時就無法通過。
import java.io.*;
/** *//**
*author by http://www.bt285.cn http://www.5a520.cn
*/
public class Trans
{
public static void main(String[] args)
{
try
{
BufferedReader rd=null;
Writer wr=null;
try
{
File srcFile = new File((args[0]));
File dstFile = new File((args[1]));
rd = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile), args[2]));
wr = new OutputStreamWriter(new FileOutputStream(dstFile), args[3]);
// 編譯時,這裡將被報錯
// 而正確的做法可以是: throw new Exception("error! test!");
if (rd == null || wr == null) throw new String("error! test!");
while(true)
{
String sLine = rd.readLine();
if(sLine == null) break;
wr.write(sLine);
wr.write("\r\n");
}
}
finally
{
wr.flush();
wr.close();
rd.close();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
編譯時,輸出的錯誤信息如下:
E:\work\\Trans.java:20: incompatible types
found : java.lang.String
required: java.lang.Throwable
if (rd == null || wr == null) throw new String("error! test!");
1 error
當然,實際的 Java 編程中,由於 JDK 平台已經為我們設計好了非常豐富和完整的異常對象分類模型。因此,java 程序員一般是不需要再重新定義自己的異常對象。而且即便是需要擴展自定義的異常對象,也往往會從 Exception 派生而來。所以,對於 java 程序員而言,它一般只需要在它的頂級函數中 catch(Exception ex) 就可以捕獲出所有的異常對象,而不必像 C++ 中采用 catch(…) 那樣不倫不類,但又無可奈何的語法。因此,在 java 中也就不需要(也沒有了) catch(…) 這樣的語法。
至於 JDK 平台中的具體的異常對象分類模型,主人公阿愚打算放在另外單獨的一篇文章中詳細討論,這裡只簡單的概括一下: 所有異常對象的根基類是 Throwable ,Throwable 從 Object 直接繼承而來(這是 java 系統所強制要求的),並且它實現了 Serializable 接口(這為所有的異常對象都能夠輕松跨越 Java 組件系統做好了最充分的物質准備)。從 Throwable 直接派生出的異常類有 Exception 和 Error 。 Exception 是 java 程序員所最熟悉的,它一般代表了真正實際意義上的異常對象的根基類。也即是說,Exception 和從它派生而來的所有異常都是應用程序能夠 catch 到的,並且可以進行異常錯誤恢復處理的異常類型。而 Error 則表示 Java 系統中出現了一個非常嚴重的異常錯誤,並且這個錯誤可能是應用程序所不能恢復的,例如 LinkageError ,或 ThreadDeath 等。
對異常處理的管理更嚴格,也更嚴謹!
同樣還是與 C++ 異常處理模型作比較,在 Java 系統中,它對異常處理的管理更嚴格,也更嚴謹!為什麼這麼說呢?下面請聽阿愚娓娓道來!
首先還是看一個例子吧!代碼如下:
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
BufferedReader rd=null;
Writer wr=null;
try
{
File srcFile = new File((args[0]));
File dstFile = new File((args[1]));
rd = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile), args[2]));
wr = new OutputStreamWriter(new FileOutputStream(dstFile), args[3]);
// 注意下面這條語句,它有什麼問題嗎?
if (rd == null || wr == null) throw new Exception("error! test!");
while(true)
{
String sLine = rd.readLine();
if(sLine == null) break;
wr.write(sLine);
wr.write("\r\n");
}
}
finally
{
wr.flush();
wr.close();
rd.close();
}
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
}
熟悉 java 語言的程序員朋友們,你們認為上面的程序有什麼問題嗎?編譯能通過嗎?如果不能,那麼原因又是為何呢?好了,有了自己的分析和預期之後,不妨親自動手編譯一下上面的小程序,呵呵!結果確實如您所料?是的,的確是編譯時報錯了,錯誤信息如下:
E:\Trans.java:20: unreported exception java.lang.Exception; must be caught or declared to be thrown
if (rd == null || wr == null) throw new Exception("error! test!");
1 error
上面這種編譯錯誤信息,相信 Java 程序員肯定見過(可能還是屢見不鮮!),而且,這也是許多從 C++ 程序員剛剛轉來寫 Java 程序不久(還沒有真正入行)時,最感到迷糊和不解的地方,“俺代碼明明沒什麼問題嗎?可為什麼編譯有問題呢”!
呵呵!阿愚相信老練一些的 Java 程序員一定非常清楚上述編譯出錯的原因。那就是如錯誤信息中(“ must be caught ”)描述的那樣,在 Java 的異常處理模型中,要求所有被拋出的異常都必須要有對應的“異常處理模塊” 。也即是說,如果你在程序中 throw 出一個異常,那麼在你的程序中(函數中)就必須要 catch 這個異常(處理這個異常)。例如上面的例子中,你在第 20 行代碼處,拋出了一個 Exception 類型的異常,但是在該函數中,卻沒有 catch 並處理掉此異常的地方。因此,這樣的程序即便是能夠編譯通過,那麼運行時也是致命的(可能導致程序的崩潰),所以,Java 語言干脆在編譯時就盡可能地檢查(並卡住)這種本不應該出現的錯誤,這無疑對提高程序的可靠性大有幫助。相反,C++ 中則不是這樣,它把更多的事情和責任交給 C++ 程序員去完成,它總相信 C++ 程序員是最優秀的,會自己處理好這一切的所有事情。如果程序中真的出現了未被捕獲的異常,系統它(實際上是 C++ 運行庫中)就認為這是出現了一個致命的、不可恢復的錯誤,接著調用 terminate 函數終止該進程( NT 系統中稍微負責任一些,還彈出個“系統錯誤”對華框,並且 report 出一些與異常相關的錯誤信息)。
從上面的分析可以看出,Java 異常處理模型的確比 C++ 異常處理模型更嚴謹和更安全。實際上,這還體現在更多的方面,例如,在 C++ 異常處理模型中,異常的聲明已經可以成為聲明函數接口的一部分,但這並非語言所強求的。雖然許多優秀的庫系統在設計時,都把這當成了一種必須的約定,甚至是編程規范,例如 MFC 中,就有許多可能拋出異常的函數,都顯式地做了聲明,如 CFile::Read 函數,聲明如下:
virtual UINT Read( void* lpBuf , UINT nCount );
throw( CFileException );
但是,在 Java 語言中,這就是必須的。 如果一個函數中,它運行時可能會向上層調用者函數拋出一個異常,那麼,它就必須在該函數的聲明中顯式的注明(采用 throws 關鍵字,語法與 C++ 類似) 。還記得剛才那條編譯錯誤信息嗎?“ must be caught or declared to be thrown ”,其中“ must be caught ”上面已經解釋了,而後半部分呢?“ declared to be thrown ”是指何意呢?其實指的就是“必須顯式地聲明某個函數可能會向外部拋出一個異常”,也即是說,如果一個函數內部,它可能拋出了一種類型的異常,但該函數內部並不想(或不宜) catch 並處理這種類型的異常,此時,它就必須( 注意,這是必須的!強求的!而 C++ 中則不是必須的 )使用 throws 關鍵字來聲明該函數可能會向外部拋出一個異常,以便於該函數的調用者知曉並能夠及時處理這種類型的異常。下面列出了這幾種情況的比較,代碼如下:
// 示例程序 1 ,這種寫法能夠編譯通過
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
test();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
static void test()
{
try
{
throw new Exception("test");
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
// 示例程序 2 ,這種寫法就不能夠編譯通過
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
test();
}
// 雖然這裡能夠捕獲到 Exception 類型的異常
catch(Exception ex)
{
ex.printStackTrace();
}
}
static void test()
{
throw new Exception("test");
}
}
// 示例程序 3 ,這種寫法又能夠被編譯通過
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
test();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
// 由於函數聲明了可能拋出 Exception 類型的異常
static void test() throws Exception
{
throw new Exception("test");
}
}
// 示例程序 4 ,它又不能夠被編譯通過了
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
// 雖然 test() 函數並沒有真正拋出一個 Exception 類型的異常
// 但是由於它函數聲明時,表示它可能拋出一個 Exception 類型的異常
// 所以,這裡仍然不能被編譯通過。
// 呵呵!體會到了 Java 異常處理模型的嚴謹吧!
test();
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
static void test() throws Exception
{
}
}
不知上面幾個有聯系的示例是否能夠給大家帶來“豁然開朗”的感覺,坦率的說,Java 提供的異常處理模型並不復雜,相信太多太多 Java 程序員有著比阿愚更深刻的認識。也許,阿愚對於這些簡單而瑣碎的事情如此婆婆媽媽絮叨個沒完,有點招人笑話了!但是,阿愚還是堅持認為,非常有必要把這些簡單但非常管用的准則闡述透徹。最後,補充一種例外情況,請看如下代碼:
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
test();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
static void test() throws Error
{
throw new Error(" 故意拋出一個 Error");
}
}
朋友們!上面的程序能被編譯通過嗎?注意,按照剛才上面所總結出的規律:在 Java 的異常處理模型中,要求所有被拋出的異常都必須要有對應的 catch 塊!那麼上面的程序肯定不能被編譯通過,因為 Error 和 Exception 都是從 Throwable 直接派生而來,而 test 函數聲明了它可能拋出 Error 類型的異常,但在 main 函數中卻並沒有 catch(Error) 或 catch(Throwable) 塊,所以它理當是會編譯出錯的!真的嗎?不妨試試!呵呵!結果並非我們之預料,而它恰恰是正確編譯通過了。為何? WHY ? WHY ?
其實,原因很簡單,那就是因為 Error 異常的特殊性。 Java 異常處理模型中規定: Error 和從它派生而來的所有異常,都表示系統中出現了一個非常嚴重的異常錯誤,並且這個錯誤可能是應用程序所不能恢復的 (其實這在前面的內容中已提到過)。因此,如果系統中真的出現了一個 Error 類型的異常,那麼則表明,系統已處於崩潰不可恢復的狀態中,此時,作為編寫 Java 應用程序的你,已經是沒有必要(也沒能力)來處理此等異常錯誤。所以,javac 編譯器就沒有必要來保證:“在編譯時,所有的 Error 異常都有其對應的錯誤處理模塊”。當然,Error 類型的異常一般都是由系統遇到致命的錯誤時所拋出的,它最後也由 Java 虛擬機所處理。而作為 Java 程序員的你,可能永遠也不會考慮拋出一個 Error 類型的異常。因此 Error 是一個特例情況!
特別關注一下 RuntimeException
上面剛剛討論了一下 Error 類型的異常處理情況,Java 程序員一般無須關注它(處理這種異常)。另外,其實在 Exception 類型的異常對象中,也存在一種比較特別的“異常”類型,那就是 RuntimeException ,雖然它是直接從 Exception 派生而來,但是 Java 編譯器( javac )對 RuntimeException 卻是特殊待遇,而且是照顧有加。不信,看看下面的兩個示例吧!代碼如下:
// 示例程序 1
// 它不能編譯通過,我們可以理解
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
test();
}
static void test()
{
// 注意這條語句
throw new Exception(" 故意拋出一個 Exception");
}
}
// 示例程序 2
// 可它卻為什麼能夠編譯通過呢?
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
test();
}
static void test()
{
// 注意這條語句
throw new RuntimeException(" 故意拋出一個 RuntimeException");
}
}
對上面兩個相當類似的程序,javac 編譯時卻遭遇了兩種截然不同的處理,按理說,第 2 個示例程序也應該像第 1 個示例程序那樣,編譯時報錯!但是 javac 編譯它時,卻例外地讓它通過它,而且在運行時,java 虛擬機也捕獲到了這個異常,並且會在 console 打印出詳細的異常信息。運行結果如下:
java.lang.RuntimeException: 故意拋出一個 RuntimeException
at Trans.test(Trans.java:13)
at Trans.main(Trans.java:8)
Exception in thread "main"
為什麼對於 RuntimeException 類型的異常(以及從它派生而出的異常類型),javac 和 java 虛擬機都特殊處理呢?要知道,這可是與“ Java 異常處理模型更嚴謹和更安全”的設計原則相抵觸的呀!究竟是為何呢?這簡直讓人不法理解呀!主人公阿愚勸大家稍安勿躁,我們不妨換個角度,還是從 C++ 異常處理模型展開對比和分析,也許這能使我們茅塞頓開!
在 VC 實現的 C++ 異常處理中(基於 SEH 機制之上),一般有兩類異常。其一,就是通過 throw 語句,程序員在代碼中人為拋出的異常(由於運行時動態地監測到了一個錯誤);另外一個呢?就是系統異常,也即前面我們一直重點討論的 SEH 異常,例如,“被 0 除”,“段存儲保護異常”,“無效句柄” 等,這類異常實際上程序員完全可以做得到避免它(只要我們寫代碼時足夠小心,足夠嚴謹,使得寫出的代碼足夠安全可靠),但實際上,這也許無法完全做到,太理想化了。因此,為了徹底解決這種隱患,提高程序整體可靠性(可以容忍程序員留下一些 BUG ,至少不至於因為編碼時考慮不周,或一個小疏忽留下的一個小 BUG ,而導致整個應用系統在運行時崩潰!),VC 提供的 C++ 異常處理中,它就能夠捕獲到 SEH 類型的系統異常。 這類異常在被處理時,實際上它首先會被操作系統的中斷系統所接管(也即應用程序出現這類異常時,會導致觸發了一個系統“中斷”事件),接著,操作系統會根據應用程序中(實際上是 VC 運行庫)所設置的一系列“異常”回調函數和其它信息,來把處理該“系統異常”的控制權轉交給應用程序層的“ VC 異常處理模型”中。 還記得上一篇文章中,《第 28 集 如何把 SEH 類型的系統異常轉化為 C++ 類型的異常》中的方法和技術原理嗎?我們不妨回顧一下,“把 SEH 類型的系統異常轉化為 C++ 類型的異常”之後,它就提供了一種很好的技術方法,使得 VC 提供的 C++ 異常處理中,完全把“系統異常”的處理容納到了按 C++ 方式的異常處理之中。
毫無疑問,這種技術大大的好!大大的妙!同樣,現在 Java 的異常處理模型中,它理當應該也具備此項技術功能(誰讓它是 C++ 的發展呢?),朋友們,阿愚現在是否已經把該問題解釋明白了呢?是的,實際上,RuntimeException 異常的作用,就相當於上一篇文章中,阿愚為 C++ 所設計的 seh_exception_base 異常那樣,它們在本質上完成同樣類似的功能。只不過,Java 語言中,RuntimeException 被統一納入到了 Java 語言和 JDK 的規范之中。請看如下代碼,來驗證一下我們的理解!
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
test();
}
static void test()
{
int i = 4;
int j = 0;
// 運行時,這裡將觸發了一個 ArithmeticException
// ArithmeticException 從 RuntimeException 派生而來
System.out.println("i / j = " + i / j);
}
}
運行結果如下:
java.lang.ArithmeticException: / by zero
at Trans.test(Trans.java:16)
at Trans.main(Trans.java:8)
Exception in thread "main"
又如下面的例子,也會產生一個 RuntimeException ,代碼如下:
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
test();
}
static void test()
{
String str = null;
// 運行時,這裡將觸發了一個 NullPointerException
// NullPointerException 從 RuntimeException 派生而來
str.compareTo("abc");
}
}
所以,針對 RuntimeException 類型的異常,javac 是無法通過編譯時的靜態語法檢測來判斷到底哪些函數(或哪些區域的代碼)可能拋出這類異常(這完全取決於運行時狀態,或者說運行態所決定的),也正因為如此,Java 異常處理模型中的“ must be caught or declared to be thrown ”規則也不適用於 RuntimeException (所以才有前面所提到過的奇怪編譯現象,這也屬於特殊規則吧)。但是,Java 虛擬機卻需要有效地捕獲並處理此類異常。當然,RuntimeException 也可以被程序員顯式地拋出,而且為了程序的可靠性,對一些可能出現“運行時異常( RuntimeException )”的代碼區域,程序員最好能夠及時地處理這些意外的異常,也即通過 catch(RuntimeExcetion) 或 catch(Exception) 來捕獲它們。如下面的示例程序,代碼如下:
import java.io.*;
public class Trans
{
public static void main(String[] args)
{
try
{
test();
}
// 在上層的調用函數中,最好捕獲所有的 Exception 異常!
catch(Exception e)
{
System.out.println("go here!");
e.printStackTrace();
}
}
// 這裡最好顯式地聲明一下,表明該函數可能拋出 RuntimeException
static void test() throws RuntimeException
{
String str = null;
// 運行時,這裡將觸發了一個 NullPointerException
// NullPointerException 從 RuntimeException 派生而來
str.compareTo("abc");
}
}
總結
•Java 異常處理模型與 C++ 中異常處理模型的最大不同之處,就是在 Java 異常處理模型中引入了 try-finally 語法,它主要用於清理非內存性質的一些資源(垃圾回收機制無法處理的資源),例如,數據庫連接、 Socket 關閉、文件流的關閉等。
•所有的異常都必須從 Throwable 繼承而來,不像 C++ 中那樣,可以拋出任何類型的異常。因此,在 Java 的異常編程處理中,沒有 C++ 中的 catch(…) 語法,而它的 catch(Throwable e) 完全可以替代 C++ 中的 catch(…) 的功能。
•在 Java 的異常處理模型中,要求所有被拋出的異常都必須要有對應的“異常處理模塊”。也即是說,如果你在程序中 throw 出一個異常,那麼在你的程序中(函數中)就必須要 catch 這個異常(處理這個異常)。但是,對於 RuntimeException 和 Error 這兩種類型的異常(以及它們的子類異常),卻是例外的。其中,Error 表示 Java 系統中出現了一個非常嚴重的異常錯誤;而 RuntimeException 雖然是 Exception 的子類,但是它卻代表了運行時異常(這是 C++ 異常處理模型中最不足的,雖然 VC 實現的異常處理模型很好)
•如果一個函數中,它運行時可能會向上層調用者函數拋出一個異常,那麼,它就必須在該函數的聲明中顯式的注明(采用 throws 關鍵字,語法與 C++ 類似)。
本篇文章雖然有點長,但是如果你能夠堅持完整地看下來,相信你對 Java 的異常處理模型一定有了更深一步的了解(當然,由於阿愚水平有限,難免會有不少認識上或理解上的錯誤,歡迎大家不吝賜教!呵呵!討論和交流也可以)。在下一篇文章中,阿愚將繼續分析和闡述一些有關 Java 異常處理模型中更細節的語法特點和使用上的技巧,以及注意事項。感興趣的朋友們,繼續吧!GO !