為違例編寫代碼時
我們經常要解決的一個問題是
一旦產生違例
會正確地進行清除嗎?
大多數時候都會非常安全
但在構建器中卻是一個大問題
構建器將對象置於一個安全的起始狀態
但它可能執行一些操作——如打開一個文件
除非用戶完成對象的使用
並調用一個特殊的清除方法
否則那些操作不會得到正確的清除
若從一個構建器內部
擲
出一個違例
這些清除行為也可能不會正確地發生
所有這些都意味著在編寫構建器時
我們必須特別加以留意
由於前面剛學了finally
所以大家可能認為它是一種合適的方案
但事情並沒有這麼簡單
因為finally每次都會執行清除代碼——即使我們在清除方法運行之前不想執行清除代碼
因此
假如真的用finally進行清除
必須在構建器正常結束時設置某種形式的標志
而且只要設置了標志
就不要執行finally塊內的任何東西
由於這種做法並不完美(需要將一個地方的代碼同另一個地方的結合起來)
所以除非特別需要
否則一般不要嘗試在finally中進行這種形式的清除
在下面這個例子裡
我們創建了一個名為InputFile的類
它的作用是打開一個文件
然後每次讀取它的一行內容(轉換為一個字串)
它利用了由Java標准IO庫提供的FileReader以及BufferedReader類(將於第
章討論)
這兩個類都非常簡單
大家現在可以毫無困難地掌握它們的基本用法
//: Cleanup
java
// Paying attention to exceptions
// in constructors
import java
io
*;
class InputFile {
private BufferedReader in;
InputFile(String fname) throws Exception {
try {
in =
new BufferedReader(
new FileReader(fname));
// Other code that might throw exceptions
} catch(FileNotFoundException e) {
System
out
println(
Could not open
+ fname);
// Wasn
t open
so don
t close it
throw e;
} catch(Exception e) {
// All other exceptions must close it
try {
in
close();
} catch(IOException e
) {
System
out
println(
in
close() unsuccessful
);
}
throw e;
} finally {
// Don
t close it here!!!
}
}
String getLine() {
String s;
try {
s = in
readLine();
} catch(IOException e) {
System
out
println(
readLine() unsuccessful
);
s =
failed
;
}
return s;
}
void cleanup() {
try {
in
close();
} catch(IOException e
) {
System
out
println(
in
close() unsuccessful
);
}
}
}
public class Cleanup {
public static void main(String[] args) {
try {
InputFile in =
new InputFile(
Cleanup
java
);
String s;
int i =
;
while((s = in
getLine()) != null)
System
out
println(
+ i++ +
:
+ s);
in
cleanup();
} catch(Exception e) {
System
out
println(
Caught in main
e
printStackTrace()
);
e
printStackTrace();
}
}
} ///:~
該例使用了Java
IO類
用於InputFile的構建器采用了一個String(字串)參數
它代表我們想打開的那個文件的名字
在一個try塊內部
它用該文件名創建了一個FileReader
對FileReader來說
除非轉移並用它創建一個能夠實際與之
交談
的BufferedReader
否則便沒什麼用處
注意InputFile的一個好處就是它同時合並了這兩種行動
若FileReader構建器不成功
就會產生一個FileNotFoundException(文件未找到違例)
必須單獨捕獲這個違例——這屬於我們不想關閉文件的一種特殊情況
因為文件尚未成功打開
其他任何捕獲從句(catch)都必須關閉文件
因為文件已在進入那些捕獲從句時打開(當然
如果多個方法都能產生一個FileNotFoundException違例
就需要稍微用一些技巧
此時
我們可將不同的情況分隔到數個try塊內)
close()方法會擲出一個嘗試過的違例
即使它在另一個catch從句的代碼塊內
該違例也會得以捕獲——對Java編譯器來說
那個catch從句不過是另一對花括號而已
執行完本地操作後
違例會被重新
擲
出
這樣做是必要的
因為這個構建器的執行已經失敗
我們不希望調用方法來假設對象已正確創建以及有效
在這個例子中
沒有采用前述的標志技術
finally從句顯然不是關閉文件的正確地方
因為這可能在每次構建器結束的時候關閉它
由於我們希望文件在InputFile對象處於活動狀態時一直保持打開狀態
所以這樣做並不恰當
getLine()方法會返回一個字串
其中包含了文件中下一行的內容
它調用了readLine()
後者可能產生一個違例
但那個違例會被捕獲
使getLine()不會再產生任何違例
對違例來說
一項特別的設計問題是決定在這一級完全控制一個違例
還是進行部分控制
並傳遞相同(或不同)的違例
或者只是簡單地傳遞它
在適當的時候
簡單地傳遞可極大簡化我們的編碼工作
getLine()方法會變成
String getLine() throws IOException {
return in
readLine();
}
但是當然
調用者現在需要對可能產生的任何IOException進行控制
用戶使用完畢InputFile對象後
必須調用cleanup()方法
以便釋放由BufferedReader以及/或者FileReader占用的系統資源(如文件句柄)——注釋⑥
除非InputFile對象使用完畢
而且到了需要棄之不用的時候
否則不應進行清除
大家可能想把這樣的機制置入一個finalize()方法內
但正如第
章指出的那樣
並非總能保證finalize()獲得正確的調用(即便確定它會調用
也不知道何時開始)
這屬於Java的一項缺陷——除內存清除之外的所有清除都不會自動進行
所以必須知會客戶程序員
告訴他們有責任用finalize()保證清除工作的正確進行
⑥
在C++裡
破壞器
可幫我們控制這一局面
在Cleanup
java中
我們創建了一個InputFile
用它打開用於創建程序的相同的源文件
同時一次讀取該文件的一行內容
而且添加相應的行號
所有違例都會在main()中被捕獲——盡管我們可選擇更大的可靠性
這個示例也向大家展示了為何在本書的這個地方引入違例的概念
違例與Java的編程具有很高的集成度
這主要是由於編譯器會強制它們
只有知道了如何操作那些違例
才可更進一步地掌握編譯器的知識
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19319.html