但程式設計中還有一種很常見的狀況:「實驗室就算爆炸了,外面的瓦斯總開關跟水龍頭,還是得有人冒著生命危險去關閉!」
當你的程式向作業系統「借了對外管道」(例如打開硬碟讀取檔案、連線到遠端資料庫、或是開一個 Scanner 監聽鍵盤),不管裡面的程式碼順利跑完,還是中途炸出 Exception,這扇借來的門都必須被關好!
close() 就是交棒儀式——代表上一位使用者的結束,也代表資源回歸自由、讓下一位可以借用。
這正是為什麼大家必須請出第三位成員——「善後保潔阿姨 finally」:
為什麼一定要關門?恐怖的「資源洩漏(Resource Leak)」
Scanner sc = new Scanner(System.in);
// 中間做一堆運算、讀取資料...
sc.close(); // 用完歸還資源:上一位結束,下一位才能使用!
理想很豐滿: 程式平平安安跑完,sc.close() 乖乖執行,資源物歸原位。
現實很骨感: 如果在第 2 行讀取資料時,使用者亂打字或檔案格式錯誤,程式引爆炸彈,直接跳過後續程式碼立衝出 try 的 {}, 直奔 catch 急救站去處理錯誤或印出死因。
墊底的 close() 淪為犧牲品: 原本老老實實寫在 try 尾巴的 sc.close(),就被當成空氣跳過,永遠沒機會執行,借出來的資源也就這樣被活生生卡死在半空中!
這會引發什麼災難?
佔著茅坑不拉屎: 作業系統借給你的檔案通道、網路連線通道一直掛在那裡,系統以為你「還在用」,不敢收回。
慢性中毒的崩潰: 一次兩次或許電腦還撐得住;但如果伺服器每天跑幾萬次,借出去的通道統統沒還,可用資源被耗盡,系統就會無預警卡死、宕機給你看!
風雨無阻的清潔工:finally 善後區
為了解決「程式一炸就沒人關門」的資源洩漏危機,Java 在 try-catch 身後派了一位無所畏懼的清潔工——finally:
Scanner sc = null;
try {
// 1. 派掃描員盯緊鍵盤進來啥 (System.in)
sc = new Scanner(System.in);
System.out.println("請輸入整數:");
// 2. sc.nextLine() 把整行字元吸進來組裝成字;Integer.parseInt 負責轉成整數!
// 引爆點:若輸入「十九歲」,文字轉數字失敗,當場炸飛跳出!
int num = Integer.parseInt(sc.nextLine());
System.out.println("成功取得數字:" + num);
} catch (NumberFormatException e) {
// 3. 急救站接住炸彈,印出死因報告
System.out.println("攔截到格式錯誤:" + e.getMessage());
} finally {
// 4. 不論晴雨,保潔阿姨百分之百準時打卡!
if (sc != null) {
sc.close(); // 確實歸還資源,把通道關緊!
System.out.println("資源已安全關閉!");
}
}
為什麼說 finally 是「見過大風大浪的狠角色」?
無論死活,必定執行:
try 順利跑完?finally 照常清掃。try 炸得面目全非進了 catch?finally 依然準時上工。
想提早落跑?沒門!
哪怕你在 try 或 catch 裡面寫了 return; 企圖提早收工打卡,Java 依舊會霸氣攔下你說:「等一下!先把 finally 裡的門關好再走!」
為什麼要寫 if (sc != null)?
因為怕程式在一開頭 sc = new Scanner(...) 就因為系統問題連建立都沒成功(sc 還是空的 null)。如果是空的,你還去叫它關門(null.close()),反而會在清潔區又引爆一顆 NullPointerException!這個判斷是為了確保「確實借了資源,才去執行歸還」。
只要把借資源的宣告,塞進 try (...) 的小括號裡
我現在要借鍵盤的管線用一下(指派給 sc 拿著)。只要我一踏出 try 大門,請你務必自動幫我把東西還回去
// 只要放進 try 的小括號中,離開區塊時 Java 就會「全自動幫你呼叫 .close()」!
try (Scanner sc = new Scanner(System.in)) {
// 架設好進貨管線:把鍵盤送來的原始訊號整理成可讀工具
System.out.println("請輸入整數:");
// sc.nextLine() 把使用者敲的整行字元吸成字串;
// Integer.parseInt() 再把這段文字轉成整數!
int num = Integer.parseInt(sc.nextLine());
System.out.println("成功取得數字:" + num);
} catch (NumberFormatException e) {
// 遇到格式錯誤(例如輸入「十八」),立刻攔截並印出死因
System.out.println("輸入錯誤:" + e.getMessage());
} // 大括號一結束:完全不需要寫 finally 手動 .close(),Java 自動幫你鎖門!
為什麼這是現代 Java 的首選?
出門自動隨手關門:
不管程式是平平安安執行到最後,還是中途丟出 Exception 被彈到 catch,只要一跨出 try 的大括號 {},Java 保證第一時間自動替你執行 sc.close()。
徹底消除「忘記關門」的人為失誤:
不再需要宣告 Scanner sc = null; 也不用在 finally 裡小心翼翼地防呆(檢查 sc != null)。