2009年5月13日 星期三

Practical Java Programming Language Guide-第三章 異常處裡

這個章節介紹了java Exception的一些概念,java很好心的提供了一個完善捕捉例外的方法
不過例外處裡不是萬靈丹,已有人提唱不要依賴他,也有許多相關的重構手法

case 16:認識『異常控制流』
這段沒有特別提到什麼,只是簡單介紹try-catch的流程
真要說的話,就是try可以不接catch直接用finally這段吧

try{
System.out.println(1);
}finally{
System.out.println(2);
}


case 17:絕對不可以輕忽異常
裡面提到異常發生時,我們不該忽略他,以及異常發生時我們可以做些什麼

  1. 捕捉並處裡,防止異常進一步散播(propagate)

  2. 捕捉並再次拋出,這麼一來他會向上傳給呼叫者

  3. 捕捉拋出新異常給呼叫端/li>
  4. 不捕捉這個異常,放任他繼續往上傳,直到main或是遇到try-catch



case 18:千萬不要遮掩異常
這段有提到一個很重要的概念,遮掩異常
考慮下面一段程式

f() throws Exception{
try{
throw new Exception("Try");
}
catch(Exception e){
throw new Exception("catch");
}
finally{
throw new Exception("finally");
}
...
try{
f();
}catch(Exception e)
{
System.out.println(e.getMessage());
}

他會印出finally,為什麼呢?丟出異常後不是後面的程式都不會執行了嗎
來觀察整個流程,當try丟出一個例外後被catch捕捉,如果此時沒有finally,最後收到的就是catch的例外,try的例外被隱藏了,這就是遮掩異常。
而我們有做finally,不論try或catch做了什麼只要進入try-catch區塊finally就一定會執行
所以catch執行完又做了finally,這個範例最後丟出的例外是
new Exception("finally");
在看下面一段程式

f() throws Exception{
try{
return;//或是System.exit(0);
}
catch(Exception e){
throw new Exception("catch");
}
finally{
throw new Exception("finally");
}
...
try{
f();
}catch(Exception e)
{
System.out.println(e.getMessage());
}

他依然會印出finally,為什麼?不是return了,上面有提到就算執行System.exit(0)也一樣
如果今天只有catch沒有finally,那他什麼也不會印出,但是有了finally,不管怎樣也會執行那個區塊,除非停電還是電腦關機一類外力強迫電腦無法執行

因為這是我以前都沒注意到的事情,所以我覺得值得做個重點
『直要進入try-catch區塊,只要有finally無論如何都會執行』
如果要避免遮掩異常,可以用一個List記錄這一路丟出了哪些例外然後再丟出新例外

class ListException extends Exception{
ArrayList list=new ArrayList();
void add(Exception e)
{
list.add(e);
}
}
...
f() throws Exception{
ListException _liste=new ListException();
try{
_liste.add(new Exception("try"));
}
catch(Exception e){
_liste.add(new Exception("catch"));
}
finally{
_liste.add(new Exception("finally"));
throw _liste;//丟出一個例外串列
}


case 19:明察throws子句的缺點
這個case簡單來說就是向上丟出例外,無法保證上面的呼叫者一定會處理他
通常例外有兩個解決方式,自己處理掉或是拋出給呼叫者處裡

case 20:細緻而全面理解throws子句
這個章節提醒要擲出所有的例外,不要有遺漏

void f() throws Exception1{//不好

if(...)throw new Exception1();
if(...)throw new Exception2();
if(...)throw new Exception3();
}
//////////////////////////
void f() throws Exception1,Exception2,Exception3{//應該丟出所有可能的例外

if(...)throw new Exception1();
if(...)throw new Exception2();
if(...)throw new Exception3();
}


case 21:使用finally避免資源洩漏(resource leaks)
簡單來說,就是如果有開啟什麼resource,像是socket還是file,必須在finally把他關起來
因為不能保證他會在try把他關裡來

Socket s=new Socket();
try{
...
s.close();//不保證跑到這行
}
catch(Exception e){
s.close();//程式碼可讀性降低
}
finally{
s.close();//最好是放這裡
}

case 22:不要從try區段中返回
字面意思就是這樣

case 23:將try-catch區段置於迴圈之外
主要是因為把try-catch放到迴圈裡面會降低效能

void f1(){
for(.....)
{
try{
}catch(Exception e){
}
}
}

void f2(){
try{
for(.....)
{
}
}catch(Exception e){
}
}

以上面為例,f2的效能會比f1還高,書中用bytecode作解釋,在此不贅言

case 24:不要將exception用於流程控制
簡單來說就是不要用try-catch去取代if-else

case 25:不要每逢出錯就使用exception
這段大意是說不要再不該用exception的場合使用他,比方說跳出迴圈

try{
while(true){
if(i==0)throw new Exception("");//多此一舉
}
}catch(){...}


case 26:在建構式中拋出異常
期望用拋出異常的方式告知使用者這個建構式失敗了,他期望怎麼處裡
典型範例:FileInputStream等IO類別的建構式

case 27:拋出異常之前先將物件回復成有效狀態
這個case的概念我覺得很不錯,很多人在處裡例外都只是印出錯誤訊息後就不管了
而沒試者去讓修復他讓他可以繼續執行
這讓我想起大學教授舉的一個例子『假設今天你寫的程式是給那些插肺管的病人用,而今天你程式當掉了,重開要兩分鐘,你有辦法閉氣兩分鐘嗎?一個健康的人都沒辦法,何況是那些病人』

上面的例子是極端了點,但是我覺得這很重要,捕捉到例外就應該處裡,就算無法處裡也該詳細記錄哪個地方出問題。
還記得我大學寫的遊戲,全螢幕縮下來時候會拋出一個例外,如果不處裡那個例外我的遊戲就會死當,當初這問題困擾我很久但是也讓我學到了例外處理的重要性

沒有留言: