《Java從小白到大牛》之第14章 異常處理(下)

《Java從小白到大牛》紙質版已經上架了!!!
Java從小白到大牛書皮html

釋放資源

有時在try-catch語句中會佔用一些非Java資源,如:打開文件、網絡鏈接、打開數據庫鏈接和使用數據結果集等,這些資源並不是Java資源,不能經過JVM的垃圾收集器回收,須要程序員釋放。爲了確保這些資源可以被釋放可使用finally代碼塊或Java 7以後提供自動資源管理(Automatic Resource Management)技術。java

finally代碼塊 {#finally}

try-catch語句後面還能夠跟有一個finally代碼塊,try-catch-finally語句語法以下:程序員

try{

//可能會生成異常語句

} catch(Throwable e1){

//處理異常e1

} catch(Throwable e2){

//處理異常e1

} catch(Throwable eN){

//處理異常eN

} finally{

//釋放資源

}

不管try正常結束仍是catch異常結束都會執行finally代碼塊,如同14-2所示。數據庫

圖14-2 finally代碼塊流程

使用finally代碼塊示例代碼以下:網絡

//HelloWorld.java文件

package com.a51work6;

… …

public class HelloWorld {

public static void main(String[] args) {

Date date = readDate();

System.out.println("讀取的日期 = " + date);

}

public static Date readDate() {

FileInputStream readfile = null;

InputStreamReader ir = null;

BufferedReader in = null;

try {

readfile = new FileInputStream("readme.txt");

ir = new InputStreamReader(readfile);

in = new BufferedReader(ir);

// 讀取文件中的一行數據

String str = in.readLine();

if (str == null) {

return null;

}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

Date date = df.parse(str);

return date;

} catch (FileNotFoundException e) {

System.out.println("處理FileNotFoundException...");

e.printStackTrace();

} catch (IOException e) {

System.out.println("處理IOException...");

e.printStackTrace();

} catch (ParseException e) {

System.out.println("處理ParseException...");

e.printStackTrace();

} finally { ①

try {

if (readfile != null) {

readfile.close(); ②

}

} catch (IOException e) {

e.printStackTrace();

}

try {

if (ir != null) {

ir.close(); ③

}

} catch (IOException e) {

e.printStackTrace();

}

try {

if (in != null) {

in.close(); ④

}

} catch (IOException e) {

e.printStackTrace();

}

} ⑤

return null;

}

}

上述代碼第①行~第⑤行是finally語句,在這裏經過關閉流釋放資源,FileInputStream、InputStreamReader和BufferedReader是三個輸入流,它們都須要關閉,見代碼第②行~第④行經過流的close()關閉流,可是流的close()方法還有能夠能發生IOException異常,因此這裏又針對每個close()語句還須要進行捕獲處理。框架

注意 爲了代碼簡潔等目的,可能有的人會將finally代碼中的多個嵌套的try-catch語句合併,例如將上述代碼改爲以下形式,將三個有能夠發生異常的close()方法放到一個try-catch。讀者本身考慮一下這處理是否穩妥呢?每個close()方法對應關閉一個資源,若是第一個close()方法關閉時發生了異常,那麼後面的兩個也不會關閉,所以以下的程序代碼是有缺陷的。ide

try {

... ...優化

} catch (FileNotFoundException e) {設計

... ...code

} catch (IOException e) {

... ...

} catch (ParseException e) {

... ...

} finally {

try {

if (readfile != null) {

readfile.close();

}

if (ir != null) {

ir.close();

}

if (in != null) {

in.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

### 自動資源管理 {#-0}

14.4.1節使用finally代碼塊釋放資源會致使程序代碼大量增長,一個finally代碼塊每每比正常執行的程序還要多。在Java 7以後提供自動資源管理(Automatic Resource Management)技術,能夠替代finally代碼塊,優化代碼結構,提升程序可讀性。

自動資源管理是在try語句上的擴展,語法以下:
```java
try (聲明或初始化資源語句) {

//可能會生成異常語句

} catch(Throwable e1){

//處理異常e1

} catch(Throwable e2){

//處理異常e1

} catch(Throwable eN){

//處理異常eN

}

在try語句後面添加一對小括號「()」,其中是聲明或初始化資源語句,能夠有多條語句語句之間用分號「;」分隔。

示例代碼以下:

//HelloWorld.java文件

package com.a51work6;

… …

public class HelloWorld {

public static void main(String[] args) {

Date date = readDate();

System.out.println("讀取的日期 = " + date);

}

public static Date readDate() {

// 自動資源管理

try (FileInputStream readfile = new FileInputStream("readme.txt"); ①

InputStreamReader ir = new InputStreamReader(readfile); ②

BufferedReader in = new BufferedReader(ir)) { ③

// 讀取文件中的一行數據

String str = in.readLine();

if (str == null) {

return null;

}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

Date date = df.parse(str);

return date;

} catch (FileNotFoundException e) {

System.out.println("處理FileNotFoundException...");

e.printStackTrace();

} catch (IOException e) {

System.out.println("處理IOException...");

e.printStackTrace();

} catch (ParseException e) {

System.out.println("處理ParseException...");

e.printStackTrace();

}

return null;

}

}

上述代碼第①行~第③行是聲明或初始化三個輸入流,三條語句放到在try語句後面小括號中,語句之間用分號「;」分隔,這就是自動資源管理技術了,採用了自動資源管理後再也不須要finally代碼塊,不須要本身close這些資源,釋放過程交給了JVM。

注意 全部能夠自動管理的資源須要實現AutoCloseable接口,上述代碼中三個輸入流FileInputStream、InputStreamReader和BufferedReader從Java 7以後實現AutoCloseable接口,具體哪些資源實現AutoCloseable接口須要查詢API文檔。

throws與聲明方法拋出異常 {#throws}

在一個方法中若是可以處理異常,則須要捕獲並處理。可是本方法沒有能力處理該異常,捕獲它沒有任何意義,則須要在方法後面聲明拋出該異常,通知上層調用者該方法有能夠發生異常。

方法後面聲明拋出使用throws關鍵字,回顧一下10.3.3節成員方法語法格式以下:

class className {

[public | protected | private ] [static] [final | abstract] [native] [synchronized]

type methodName([paramList]) [throws exceptionList] {

//方法體

}

}

其中參數列表以後的[throws exceptionList]語句是聲明拋出異常。方法中可能拋出的異常(除了Error和RuntimeException及其子類外)都必須經過throws語句列出,多個異常之間採用逗號(,)分隔。

注意 若是聲明拋出的多個異常類之間有父子關係,能夠只聲明拋出父類。但若是沒有父子關係狀況下,最好明確聲明拋出每個異常,由於上層調用者會根據這些異常信息進行相應的處理。假如一個方法中有可能拋出IOException和ParseException兩個異常,那麼聲明拋出IOException和ParseException呢?仍是隻聲明拋出Exception呢?由於Exception是IOException和ParseException的父類,只聲明拋出Exception從語法是容許的,可是聲明拋出IOException和ParseException更好一些。

若是將14.3節示例進行修改,在readDate()方法後聲明拋出異常,代碼以下:

//HelloWorld.java文件

package com.a51work6;

… …

public class HelloWorld {

public static void main(String[] args) { ①

try {

Date date = readDate(); ②

System.out.println("讀取的日期 = " + date);

} catch (IOException e) { ③

System.out.println("處理IOException...");

e.printStackTrace();

} catch (ParseException e) { ④

System.out.println("處理ParseException...");

e.printStackTrace();

}

}

public static Date readDate() throws IOException, ParseException { ⑤

// 自動資源管理

FileInputStream readfile = new FileInputStream("readme.txt"); ⑥

InputStreamReader ir = new InputStreamReader(readfile);

BufferedReader in = new BufferedReader(ir);

// 讀取文件中的一行數據

String str = in.readLine(); ⑦

if (str == null) {

return null;

}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

Date date = df.parse(str); ⑧

return date;

}

}

因爲readDate()方法中代碼第⑥、⑦、⑧行都有可能引起異常。在readDate()方法內又沒有捕獲處理,全部須要在代碼第⑤行方法後聲明拋出異常,事實上有三個異常FileNotFoundException、IOException和ParseException,因爲FileNotFoundException屬於IOException異常,因此只聲明IOException和ParseException就能夠了。

一旦readDate()方法聲明拋出了異常,那麼它的調用者main()方法,也會面臨一樣的問題:要麼捕獲本身處理,要麼拋出給上層調用者。若是一旦發生異常main()方法也選擇拋出那麼程序運行就會終止。本例中main()方法是捕獲異常進行處理,捕獲異常過程前面已經介紹過了,這裏再也不贅述。

自定義異常類

有些公司爲了提升代碼的可重用性,本身開發了一些Java類庫或框架,其中少不了本身編寫了一些異常類。實現自定義異常類須要繼承Exception類或其子類,若是自定義運行時異常類需繼承RuntimeException類或其子類。

實現自定義異常類示例代碼以下:

package com.a51work6;

public class MyException extends Exception { ①

public MyException() { ②

}

public MyException(String message) { ③

super(message);

}

}

上述代碼實現了自定義異常,自定義異常類通常須要提供兩個構造方法,一個是代碼第②行的無參數的默認構造方法,異常描述信息是空的;另外一個是代碼第③行的字符串參數的構造方法,message是異常描述信息,getMessage()方法能夠得到這些信息。

自定義異常就這樣簡單,主要是提供兩個構造方法就能夠了,

throw與顯式拋出異常 {#throw}

Java異常相關的關鍵字中有兩個很是類似,它們是throws和throw,其中throws關鍵字前面14.5節已經介紹了,throws用於方法後聲明拋出異常,而throw關鍵字用來人工引起異常。本節以前讀者接觸到的異常都是因爲系統生成的,當異常發生時,系統一個異常對象,並將其拋出。但也能夠經過throw語句顯式拋出異常,語法格式以下:

throw Throwable或其子類的實例

全部Throwable或其子類的實例均可以經過throw語句拋出。

顯式拋出異常目的有不少,例如不想某些異常傳給上層調用者,能夠捕獲以後從新顯式拋出另一種異常給調用者。

修改14.4節示例代碼以下:

//HelloWorld.java文件

package com.a51work6;

… …

public class HelloWorld {

public static void main(String[] args) {

try {

Date date = readDate();

System.out.println("讀取的日期 = " + date);

} catch (MyException e) {

System.out.println("處理MyException...");

e.printStackTrace();

}

}

public static Date readDate() throws MyException {

// 自動資源管理

try (FileInputStream readfile = new FileInputStream("readme.txt");

InputStreamReader ir = new InputStreamReader(readfile);

BufferedReader in = new BufferedReader(ir)) {

// 讀取文件中的一行數據

String str = in.readLine();

if (str == null) {

return null;

}

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

Date date = df.parse(str);

return date;

} catch (FileNotFoundException e) { ①

throw new MyException(e.getMessage()); ②

} catch (IOException e) { ③

throw new MyException(e.getMessage()); ④

} catch (ParseException e) {

System.out.println("處理ParseException...");

e.printStackTrace();

}

return null;

}

}

若是軟件設計者不但願readDate()方法中捕獲的FileNotFoundException和IOException異常出如今main()方法(上層調用者)中,那麼能夠在捕獲到FileNotFoundException和IOException異常時,經過throw語句顯式拋出一個異常,見代碼第②行和第④行throw new MyException(e.getMessage())語句,MyException是自定義的異常。

注意 throw顯式拋出的異常與系統生成並拋出的異常,在處理方式上沒有區別,就是兩種方法:要麼捕獲本身處理,要麼拋出給上層調用者。在本例中是聲明拋出,因此在readDate()方法後面要聲明拋出MyException異常。

本章小結

本章介紹了Java異常處理機制,其中包括Java異常類繼承層次、捕獲異常、釋放資源、throws、throw和自定義異常類。讀者須要重點掌握捕獲異常處理,熟悉throws和throw的區分和用法。

配套視頻

http://edu.51cto.com/topic/1246.html

配套源代碼

http://www.zhijieketang.com/group/5

與本書免費版對應的還有一個收費版本:

  1. 進入百度閱讀電子書

  2. 進入圖靈社區電子書
相關文章
相關標籤/搜索