• 沒有找到結果。

[ 第 8 章 ] 例外與檔案處理 JAVA 基礎程式設計班

N/A
N/A
Protected

Academic year: 2022

Share "[ 第 8 章 ] 例外與檔案處理 JAVA 基礎程式設計班"

Copied!
67
0
0

加載中.... (立即查看全文)

全文

(1)

JAVA基礎程式設計班

國立台灣大學 資訊工程學系暨研究所 資訊系統訓練班

講師:宋浩

[第8章] 例外與檔案處理

(2)

章節目錄

 8-1 例外處理

 8-2 自訂例外

 8-3 檔案處理

 8-4 序列化處理

(3)

8-1 例外處理

(4)

例外處理(Exception-handling)

 Java程式語言非常嚴謹,對程式的錯誤 處理有一套很嚴謹的處理機制

 除了在編譯時期嚴格的檢查原始碼可能 產生的錯誤之外,在程式執行時期也提 供了相關的處理機制,而這項機制稱為

「例外處理機制(Exception-handling

Mechanishm)」

(5)

Java的例外與錯誤

 程式執行時可能產生不正常的情況,而這不 常的情況可能是所謂的「例外」,比方說 :

◦ 開啟一個不存在的檔案

◦ 除數為零

◦ 陣列的存取超出了索引值的範圍

◦ 未載入相關的套件即使用套件中的類別

(6)

為什麼要有例外?

public class ReadFile {

public void Read() {

開啟檔案;

決定檔案大小;

配置記憶體;

將檔案讀入記憶體;

關閉檔案;

} }

指定檔案不存在

檔尾記號損毀,無法決定大小 記憶體不足

檔案毀損,讀檔失敗

檔案他人使用中,無法關閉

程式停止!

• 如果沒有例外處理…

(7)

public class ReadFile {

public int Read() {

int ErrCode = 0;

開啟檔案;

if (檔案存在) {

決定檔案大小;

if (檔案大小已定) {

配置記憶體;

if (記憶體足夠) {

將檔案讀入記憶體;

if (讀入成功) {

開始操作檔案內容;

} else {

ErrCode = -1;

} }

else {

ErrCode = -2;

} }

else {

ErrCode = -3;

}

關閉檔案;

if (檔案無法關閉 && 無其它錯誤) ErrCode = -4;

else

ErrCode = -5;

} else

ErrCode = -6;

return ErrCode;

用 if-else 來處

理例外呢?

(8)

 Java 將所有的例外 (Exception),通通宣 告成物件,且全部繼承 Throwable 物件

Java 例外的概念 (1)

java.lang.Object Throwable

Error Exception

RuntimeException

CheckedException

(9)

 Java VM Errors

◦ 此區所發出的例外事件,JVM 會自行處理

 Checked Exceptions

◦ 此區所發出的例外事件,是 Java 強迫要求程式師 一定要處理的,如不處理,則 Compile 時期就會 發生錯誤訊息

 Runtime Exceptions

◦ 此區所發出的例外事件,是使用者操作不小心所 引起的,程式師可處理可不處理,Java 並沒有硬 性規定要攔截此類 Exception。

Java 例外的概念 (2)

(10)

常見 JVM Errors 介紹

Error

ThreadDeath VirtualMachineError

ThreadDeath: 當一個程式無預警停止時,就會發出此錯誤

VirtualMachineError: 當 JVM 內部出現無法解決的問題時,就會發出此錯誤

(11)

常見 Checked Exceptions 介紹

 ClassNotFoundException:

◦ 當存取一個不存在或不正確的類別時,就會發出此例外

 CloneNotSupportedException:

◦ 嘗試呼叫 .Clone(),但該類別並未支援時,會發出此例外

 DataFormatException:

◦ 嘗試將一個資料指定給型態不符的物件時,就會發出此例外

 IOException:

◦ 檔案不存在,或開檔不成功時,就會發出此例外

(12)

常見 Runtime Exceptions 介紹

例外名稱 例外狀況

ArithmeticException 分母為零時

IndexOutOfBoundsException 索引值超過物件上限時

ArrayIndexOutOfBoundsException 陣列索引值超過上限時 (子類別) StringIndexOutOfBoundsException 字串索引值超過上限時 (子類別) NegativeArraySizeException 當宣告陣列時,索引值為負數時 NullPointerException 呼叫了一個內容為 null 的變數 ArrayStoreException 陣列元素型態與陣列所宣告不合 ClassCastException 類別轉型 (Casting) 失敗

IllegalArgumentException 傳入參數型態不符

NumberFormatException parse 轉型失敗

(13)

對於Java而言,每個Exception都是一個物件。也就是說,當程式發生Exception時,就會 產生某個特定的Exception物件。如果程式在執行時真的產生了異常情況,JVM會依照一 定的程序來處理異常情況,過程如下:

1. 中止產生異常情況的指令的執行 2. 產生此異常情況的 Exception 物件

3. 如果設計者有撰寫處理該 Exception 的程式碼,則交由該區段的程式碼來處 理;如果沒有設計處理的程式碼,則交由JVM處理

4. 如果是由JVM來處理 Exception 物件,則程式會異常終止

Java 的例外處理程序

(14)

以下的範例顯示若沒有使用例外處理的語法時,程式會在產生例外的敘述上停止執行:

Java 的例外處理範例

程式9-1:Chap9\No Try.java 01: public class NoTry{

02: public static vod main (String[] args){

03: int count;

04:

05: String showName[]={

06: "Alex", 07: "Ivy", 08: "Fanny"

09: };

10:

11: for (count=0; count<4; count++){

12: System.out.println("第 "+count + 個名字是:"+showName[count]);

13: }

14: System.out.println("程式執行結束");

15: } 16: }

程式的執行結果為:

第 0 個名字是:Alex 第 1 個名字是:Ivy 第 2 個名字是:Fanny

Exception in thread "main" java.lang.

(15)

try-catch (1)

「try-catch」是Java程式的例外處理敘述,該語法中的處理如下:

try {

//可能產生例外的敘述 }

catch (ExceptionType E1)

{ //產生ExceptionType例外時,要處理的敘述 }

catch (Exception E2) {

//產生其他的例外時,要處理的敘述 }

在語法「try」區段中包含了可能產生例外的程式碼。「catch」敘述後以「( )」表示需要處理的例外 的類別,e1或是e2則是產生的例外物件。「catch」可以有多個區段,可分別用來處理不同的例外情 況。當程式進入「try」區段執行敘述時,如果沒有發生例外,則程式會在執行完「try」區塊中的敘 述後跳過所有的「catch」區段,繼續往下執行。如果在執行「try」區段中的敘述時產生例外,則程 式會由例外的發生點跳出「try」區段,並嘗試進入第一個「catch」區段。如果該例外可以由該

「catch」後的Exception類別處理,則程式會進入該「catch」區段,並執行相關的敘述。執行完畢後,

程式不會再執行其他「catch」區段的判斷,而是直接跳出整個「try-catch」敘述。如果第一個

(16)

以上一個「NoTry.java」範例加上捕捉例外的機制來示範如何撰寫「try-catch」:

程式9-2:Chap9\Try1.java 01: public class Try1 {

02: public static void main (String[] args){

03: int count;

04:

05: String showName[] = {"Alex","Ivy","Fanny"};

06:

07: try{

08: for (count=0; count<4; count++){

09: System.out.println(showName[count]);

10: }

11: } catch (ArrayIndexOutOfBoundsException e){

12: System.out.println(e.toString());

13: System.out.println("陣列索引超範圍");

14: } catch (Exception e){

15: System.out.println("其餘的錯誤" + e.getMessage());

16: }

17: System.out.println("程式執行結束");

18: } 19: }

try-catch (2)

(17)

程式的執行結果為:

try-catch (3)

Alex Ivy Fanny

java.lang.ArrayIndexOutOfBoundsException: 3 陣列索引超範圍

程式執行結束

範例程式中將列印字串陣列的內容放在「try」區段中,由上一個範例得知在迴圈執 行時會產生 「ArrayIndexOutOfBoundsException」的例外。因此在第一個「catch」

區段中試圖捕捉該例外,而「e」是當捕捉到這個例外時所產生的物件。程式的第12 行使用了「toString()」方法來列印出該例外的相關訊息。程式執行時也有可能會產生 我們在設計時無法得知的例外。因此第二個「catch」區段嘗試捉「Exception」類別 的例外,因為這個類別是所有例外的父類別,所以如果程式產生了不同於

「ArrayIndexOutOfBoundsException」例外而第一個「catch」區段無法捕捉到時,此 區段會負責捕捉其他的例外

我們可以從程式的執行結果得知,程式在執行完第11~13行的例外處理後,並不會再 執行第14和15行中的敘述而是直接跳離整個「try-catch」區塊,且執行第17行的敘述

(18)

catch 區段的順序

在上述的範例程式中,如果刪除第11~13行的程式,那執行的結果會是:

Alex Ivy Fanny

其餘的錯誤3 程式執行結束

其中的「3」是第15行程式中的「getMessage()」方法所產生的訊息。必需注意的是因 為「Exception」類別是所有例外的父類別,因此不可以將「Exception」類別放在第一 個「catch」區段中,除非只使用一個「catch」區段。若將上一個範例程式的順序修改:

} catch (Exception e) {

System.out.println("其餘的錯誤" + e.getMessage());

} catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.toString());

System.out.println("陣列索引超出範圍");

}

因為「Exception」類別會捕捉所有的「Checked Exception」。因此第二個「catch」區 段並無作用,所以該程式在編譯時會產生

「exception.java.lang.ArrayIndexOutOfBoundsException has already been caught」的錯誤

(19)

finally 區塊 (1)

程式在執行「try」區段時,如果產生例外,則程式會跳到相關的「catch」區段去處理,

並在處理完成後,再直接跳出「try-catch」區段。也因此「try」區段中產生例外的程 式行以下的程式碼並不會被執行。這樣的執行流程可能會產生一些問題。例如:如果 你打算在「try」區段中撰寫開檔、寫入、關檔等程序,但程式在進行寫入的動作時產 生了例外,那「關檔」的動作就無法執行了。因此「try-catch」敘述中還提供了

「finally」區段來保證某此程式碼一定可以執行

只要設計了「try-catch」機制,「finally」區段是一個不論例外是否發生,它都一定會 執行的一個區段。如果你打算在「try」區段中撰寫開檔,寫入、關檔等程序,那可以 將開檔、寫入的動作放在「try」區段中,而將「關檔」的動作寫在「finally」區段中, 以確定在「寫入」的動作產生了例外時,還可以關閉開啟的檔案

要是你擔心如果在「開檔」的動作就產生例外,而程式在「finally」區段中無法關閉未 開啟的檔案時,還可以在「finally」區段中再設計「try-catch」區段來捕捉這項例外

(20)

參考以下的範例,並了解「finally」區段的執行情形:

finally 區塊 (2)

程式9-3:Chap9/try2.java 01:public class Try2 {

02: public static void main(String[] args){

03: int i, j, result;

04:

05: i = 100;

06: j = 0;

07:

08: try{

09: result = i / j;

10: } catch (ArithmeticException e){

11: System.out.println("除數不得為零");

12: } catch (Exception e){

13: System.out.println("其餘的錯誤");

14: } finally {

15: System.out.println("執行finally區段");

16: }

17: System.out.println("程式執行結束");

18: } 19: }

(21)

finally 區塊 (3)

程式的執行結果為:

除數不得為零 執行finally區段 程式執行結束

範例程式的第6行中,特地將「j」的值設定為0,因此程式第9行執行時會產生

「ArithmeticException」的例外。在執行完第11行的敘述後,程式會再執行第14-15行 的「finally」區段的內容,最後再執行第17行的程式碼

(22)

修改第6行的敘述改為「j = 10」,雖然不會有例外產生,但程式仍然會執行第14~15行

finally 區塊 (4)

程式9-3:Chap9/try2.java 01:public class Try2 {

02: public static void main(String[] args){

03: int i, j, result;

04:

05: i = 100;

06: j = 10;

07:

08: try{

09: result = i / j;

10: } catch (ArithmeticException e){

11: System.out.println("除數不得為零");

12: } catch (Exception e){

13: System.out.println("其餘的錯誤");

14: } finally {

15: System.out.println("執行finally區段");

16: }

17: System.out.println("程式執行結束");

18: } 19: }

(23)

finally 區塊 (5)

雖然「try-catch」機制中「finally」是不管例外是否產生它都一定會執行的區段,但是 在特殊的情況下「finally」區段仍然有可能會無法執行完該區段中的所有程式碼。唯一 會導致「finally」區段不執行的情況是JVM不再執行,例如:呼叫「System.exit(0)」或是 電腦當機。如果以上述的範例為例,如果我們在「finally」區段中加上「System.exit(0)」

的敘述,如下列所示:

finally {

System.exit(0);

System.out.println("執行finally區段");

}

那程式執行結果就會變成:

除數不得為零

實際上該程式在執行完「System.exit(0)」時就已經執行完畢了,所以「finally」區段中 的第二行敘述當然就不會再被執行了

(24)

finally 區塊 (6)

最後,我們將「finally」區段做個總整理,如果有下列的程式碼:

//敘述一 try {

//敘述二

} catch (Exception e) { //敘述三

} finally { //敘述四 }

如果有以下的情況發生,敘述四執行的狀況分別為:

1. 如果敘述一產生例外,則敘述四不會執行 2. 不管敘述二是否產生例外,敘述四都會執行 3. 不管敘述三是否產生例外,敘述四都會執行

(25)

throws 敘述 (1)

Java程式語言要求programmer必需自行處理可能產生Checked Exception的程式碼。但其 實當例外產生時,你也可以不要使用「try-catch」機制立即處理它。可以取代的作法是:

將它「丟(throws)」出來給另一支程式處理。如果要使用這種方法,則必需使用

「throws」關鍵字。參考以下的範例:

程式9-4:Chap9/Throws1.java 01:public class Throws1 {

02: public static void main(String[] args){

03: int a = 10, b = 0;

04: double result = 0;

05: try{

06: result = calculate(a, b);

07: } catch (ArithmeticException e){

08: System.out.println("除數不得為零");

09: } catch (Exception e){

10: System.out.println("其他的例外");

11: }

12: System.out.println("result的值為:" + result);

13: System.out.println("程式執行結束");

14: } 15:

16: public static double calculate(int i, int j) throws ArithmeticException{

(26)

throws 敘述 (2)

程式執行結果為:

除數不得為零 result的值為0 程式執行結束

注意範例程式中第16行calculate方法的宣告方式。該方法會傳回兩數相除的結果,預 估該方法可能產生除數為零的情況。但在「calculate」方法中,我們並沒有撰寫捕捉 例外的程式碼,而是使用了「throws」關鍵字將例外丟出。此時呼叫該方法的程式區 段就必需撰寫「try-catch」來捕捉可能產生的例外。這就是第5到第11行程式使用

「try-catch」的原因

請注意:使用throws並不代表方法一定會丟出例外。範例程式中是因為我們特地將除 數設定為0,calculate方法才會丟出例外。請自行將第3行程式中的b設定為非0的數值,

例如:1。程式的執行結果會是:

result的值為:10.0 程式執行結束

(27)

方法中的程式碼在執行的過程中可能會產生不只一種類型的例外,你可以使用逗號將各 種例外區隔開來以便處理不同類型的例外。例如:

throws 敘述 (3)

public static double calculate(int i, int j) throws ArithmeticException, Exception{

//相關程式碼;

}

或者也可以直接將所有可能產生的例外物件都轉型成Exception物件,例如:

public static double calculate (int i, int j) throws Exception{

//相關程式碼;

}

範例程式中如果直接將「ArithmeticException」的例外物件轉型為「Exception」的例外 物件,程式的執行結果仍會是相同的

有一點必需要知道的是:「Throws1.java」中的calculate()方法丟出來的

ArithmeticException例外是屬Runtime Exception。這類型的例外也可以不需要自行捕捉,

程式也可以通過編譯。因此即使main()方法中只有:

double result = calculate (a, b)

System.out.println("程式執行結束");

(28)

如果方法中丟出來的例外是屬於Checked Exception,在呼叫該方法時就不能不去處理它。

否則,程式在編譯時就會產生「unreported exception」的編譯錯誤。例如:

程式9-5:Chap9/Throws2.java 01:public class Throws2 {

02: public static void main(String[] args){

03: tryToAccess();

04: System.out.println("程式執行結束");

05: } 06:

07: public static void tryToAccess() 08: throws IOException{

09: System.out.println("嘗試使用Method");

10: } 11: }

則必需在呼叫tryToAccess()方法時使用「try-catch」程式才能通過編譯。例如:

try{

tryToAccess();

} catch (IOException e){}

throws 敘述 (4)

(29)

Call Stack機制

在宣告方法時,如果使用了「throws」關鍵字將可能產生的例外丟出,那麼呼叫此方法 的類別就必需使用「try-catch」來補捉例外。這種將例外一直往上層丟出,直到該例外 被處理為止的機制就稱為「Call Stack」機制。但如果呼叫此方法的類別又沒有處理產 生的例外時,程式會發生什麼樣的情況呢?請參考下圖:

假定主程式「main()」呼叫「A( )」方法,而「A( )」方法中又呼叫了「B( )」方法,而

「B( )」方法中又呼叫了「C( )」方法,但目前,各方法中都沒有設計「try-catch」的機 制。如果「C( )」方法中產生了例外,此例外會往上丟給「B( )」方法處理。如果無法 處理,該例外會再丟給「A( )」方法來處理。同樣的,「A( )」如果無法處理,該例外 會再丟給「main( )」方法來處理。最後則由JVM來處理接到的例外並且中止程式的執行

(30)

8-2 自訂例外

(31)

使用throw關鍵字 (1)

雖然Java語言已經提供了相當多的例外類別,但有時候,為了設計出更User Friendly的程 式,我們可能需要自己設計例外處理的機制。在探討如何自定例外之前,我們要先了解 throw關鍵字的使用

「throw」關鍵字用在方法的內部,請不要和用於方法宣告時的「throws」關鍵字混為 一談。如果在方法內部想要丟出例外的類別物件時需使用「throw」關鍵字。為了能夠 自定例外類別,我們先了解Exception類別產生物件的方式。常用的Exception類別的建構

子有: 建構子 作用

Exception() 建立一個Exception

Exception(String message) 建立一個Exception物件,並指定例外 訊息為message

Exception

(String message, Throwable cause)

建立一個Exception物件,並指定例外 訊息為message,同時將產生例外的原 因cause傳入

Exception(Throwable cause) 建立一個Exception物,同時將產生例 外的原因cause傳入

throw關鍵字可以用來丟出例外物件,它可以丟出Java內部的例外物件,也可以丟出自定 的例外物件,throw的語法如下:

throw 例外物件; //丟出Java例外物件

(32)

使用throw關鍵字 (2)

程式9-6 : Chap9\Throw1.java 01: public class Throw1{

02: public static void main(String[] args) { 03: tryToCalculate(10, 0);

04: tryToCalculate(10, 2);

05: } 06:

07: public static void tryToCalculate(int x, int y) { 08: if (y == 0) {

09: try {

10: throw new Exception("Divided by Zero!!!");

11: }catch (Exception e) {

12: System.out.println(e.toString());

13: } 14: } else {

15: System.out.println("The Result is : " + x / y);

16: } 17: } 18: }

執行的結果為:

(33)

範例程式在第10行使用了throw來丟出一個自定的例外物件,因為throw會產生例外物件,

所以程式必需使用「try-catch」來捕捉產生的例外。Exception中的「除數不得為零!!!」

的訊息會在例外物件產生時傳入該物件中。你也可以不需要直接處理這個例外物件,但 是要再將這個物件丟出來,例如改寫範例程式的第11~13行為:

使用throw關鍵字 (3)

catch (Exception e) { throw e;

}

如果是用這樣的處理方式,則必需要有另一個程式來呼叫main方法,並且在該程式中使 用「try-catch」來處理例外

(34)

01: class MyException extends Exception{

02: private int value;

03: MyException (int value){

04: this.value = value;

05: }

06: public String toString(){

07: String s = "本程式執行無效\n" +

08: "錯誤代碼為 " + value + " \n" + 09: "請將此代碼告知管理人員\n";

10: return s;

11: } 12: }

自定例外類別 (1)

MyException類別繼承自Exception類別,第3行的建構子需要傳入一個整數值當作是自 定的錯誤代碼。程式的第6~11行改寫了toString()方法,新的toString()方法會顯示出錯 誤代碼及相關的訊息

(35)

自定例外類別 (2)

再來,以下的程式碼示範如何丟出MyException類別的例外訊息:

01: public static void tryToCalculates (int x, int y){

02: if (y == 0){

03: try{

04: throw new MyException(0);

05: } catch (MyException e){

06: System.out.println(e.toString());

07: } 08: } else {

09: System.out.println("結果為: " + x / y);

10: } 11: }

tryToCalculates(int x, int y)方法會計算兩數相除的結果,但是除數y不可以為0,如果y 的值為0,在程式的第4行會丟出MyException類別的例外,並指定錯誤代碼為0。程式 的第5行會捕捉這個例外物件,並且在第6行利用toString()方法顯示錯誤訊息

(36)

自定例外類別 (3)

main()的內容:

程式9-7:Chap9/Throws2.java 01: public class Throw2{

02: public static void main (String[] args) { 03: tryToCalculates(10, 0);

04: tryToCalculates(10, 2);

05: } 06: }

程式的執行結果為:

第3行的程式碼由於傳入的參數會 導致除數為0的情況,該行程式會 引發們定的MyException例外物件,

並顯示自訂的錯誤訊息

(37)

課堂練習 PT81

例外練習 (PT81.java):

設計一個程式在執行時需要輸入一個整數數值。

請考慮可能產生的例外情況,例如:未輸入數值,

數值格式不正確等情形。如果例外產生,請使用

throw 的方式直接丟出例外,並顯示相關訊息。如果

沒有例外產生,請利用該數值計算正方形的面積。

(38)

8-3 檔案處理

(39)

串流

 串流 (Stream)

◦ 將各類儲存媒體 (Floppy, HDD, CD-

ROM…) 內的資料,統一成固定格式,以利 讀取與寫入。

輸入串流

輸出串流

(40)

File 類別 (1)

在 JAVA 中,檔案的讀、寫、在螢幕上顯示訊息、記憶體的存取,或是網路資源的使 用等動作,都被視為「串流(Stream)」。「串流」可以被視為資料輸出、輸入的通道 JAVA 將處理串流資料的相關類別放於「java.io」套件之中,需事先引用這個套件 在實際探討串流資料處理之前,我們先來了解「File」類別。嚴格說來,它並不屬於 串流的類別,因為該類別中的方法並不會牽涉到檔案的讀寫,但該類別會被用來提供 檔案或是目錄的相關資訊,包括了檔案的建立,大小、修改日期、存取權限等資訊…

等等。File類別包含了三種型態的建構子:

建構子 作 用

File(String pathname, String filename) 以上層的路徑名稱parent,並以「filename」字串的內容為 檔案名稱,建立新的File物件

File(String pathname) 將「pathname」的內容轉換為絕對路徑,並在該路徑下建

立新的File物件

File(Object parent, String child) 以上層的路徑物件parent,以「child」字串的內容為子目

錄的路徑或名稱,建立新的File物件

(41)

在目前的目錄下建立一個參考到「test.txt」檔案的File物件:

File 類別 (2)

File f1 = new File("test.txt");

File類別也可以用來處理目錄,例如:

File f2 = new File("c:\\TestDir");

如果是在Windows系統下,該程式碼會產生一個指向「c:\TestDir」目錄的物件。會使用 到「\\」是因為在Windows系統下的目錄符號「\」剛好是Java中跳脫字元的前置符號,

所以使用了「\\」來代替實際的「\」符號。當然,如果你是要在Linux平台下執行Java程 式,那目錄符號要更改為「/」

你也可以直接參考到指定目錄下的檔案,例如在windows系統下以下的兩行程式都會參 考到「c:\TestDir\test.txt」檔案:

File f3 = new File("c:\\TestDir\\test.txt");

File f4 = new File("c:\\TestDir","test.txt");

或者產生一個參考到目錄的 File 物件,再利用該物件參考到檔案,例如:

File f5 = new File("c:\\TestDir");

File f6 = new File( f5, "test.txt");

(42)

File類別的方法 (1)

File類別也提供相當多的方法來處理檔案或是目錄,有些方法單用於目錄或是檔案,有 些方法則是可以共用,請參考下表:

方 法 作 用

boolean canRead() 判斷是否可以根據提供的路徑讀取指定的檔案。如果可以,

則傳回true,如果不可以,則傳回false。

boolean canWrite() 判斷是否可以根據提供的路徑寫檔。如果可以,則傳回ture,

如果不可以,則傳回false。

boolean exists() 判斷指定的路徑下是否有指定的檔案

File getAbsoluteFile() 根據File物件參考的內容,取得指定的檔案參考

String getAbsolutePath() 根據File物件參考的內容,取得檔案的絕對路徑

String getName() 根據File物件參考的內容,取得目錄或是檔案的名稱,但不會

包含路徑

String getParent() 根據File物件參考的內容,取得上一層的路徑名稱

boolean isDirectory() 根據File物件參考的內容,判斷是否為目錄

boolean isFile() 根據File物件參考的內容,判斷是否為檔案

long lastModified() 根據File物件參考的內容,取得該檔案最後被修改的日期,傳

回毫秒值,並以1970年一月一日零時為計算基準,如果傳回 OL,代表該檔案不存在

long length() 根據File物件參考的內容,取得檔案的長度

File[] listFiles() 根據File物件參考的內容,取得該路徑下所有的檔案名稱

boolean renameTo(File dest) 將File物件參考的內容更改為指定的dest名稱

(43)

通常我們會先用isFile方法來判斷物件是否為檔案,再利用相關的方法來操作其他的需求:

File類別的方法 (2)

程式10-2:Chap 10\File 2.java 01: import java.io.File;

02: import java.util.Date;

03:

04: class File2{

05: public static void main(String[] args) {

06: File f = new File("c:\\Work\\Chap10\\Test.java");

07: System.out.println("f物件是否存在:"+ f.exists());

08: System.out.println("f物件是否為檔案:"+ f.isFile());

09: System.out.println("f物件是否可以讀取:"+ f.canRead());

10: System.out.println("f物件是否可以寫入:"+ f.canWrite());

11: System.out.println("f物件的實際名稱為:"+ f.getName());

12: System.out.println("f物件的修改日期為:"+ new Date(f.1astModified());

13: System.out.println("f物件的實際大小為:"+ f.length() +"Bytes");

14: System.out.println("f物件的絕對路徑為:"+ f.getAbsolutePath());

15: System.out.println("f物件的路徑為:"+ f.getParent());

16: } 17: }

程式的執行結果為: f物件是否存在:true

f物件是否為檔案:true f物件是否可以讀取:true f物件是否可以寫入:true f物件的實際名稱為:Test.java

(44)

File類別的方法 (3)

用 java 模擬 Windows 的 dir 指令,或 Unix 的 ls 指令:

程式10-3:Chap 10\File 3.java 01: import java.io.File;

02:

03:

04: class File3{

05: public static void main(String[] args) { 06: File f = new File("C:\\java\\");

07: File[] lists = f.listFiles();

08: int i ; 09:

10: for (i = 0 ; i<lists.legngth-1 ; i++) ; 11: if (lists[i].isDirectory()) {

12: System.out.println("<Dir>\t");

13: } else {

14: System.out.println(lists[i].getName()+"\t"+lists[i].length()+"Bytes");

15: } 16: } 17: } 18: }

執行的結果類似:

File1 .class 680 Bytes File1 .java 319 Bytes

(45)

import java.io.*;

public class FileRename {

public static void main(String[] args) {

File fs = new File(args[0]);

File fd = new File(args[1]);

if (fs.exists()) {

if (!fd.exists()) {

if (fs.renameTo(fd)) {

System.out.println(fs.getName() + " 已改名為 " + fd.getName());

System.out.println("1 file(s) has been renamed!");

} } }

File類別的方法 (4)

更改檔名:

(46)

字元串流的處理

 JAVA 的緩衝處理

◦ 緩衝資料流(Buffered Stream)

◦ 藉由一塊記憶體緩衝區來連結I/O資料流,

並讓資料的操作能夠增加效能

 相關類別

◦ InputStreamReader

◦ BufferedReader

(47)

BufferedReader (1)

我們也可以使用BufferedReader來儲存自鍵盤輸入的資料,藉由該類別中的readLine方法 可以一次讀取整行的資料,請參考以下的範例:

程式10-8:Chap 10\ InputStreamReader 2.java 01: import java.io.*;

02:

03: public class InputStreamReader 2 { 04: public static void main(String[] args) {

05: InputStreamReader ir = new InputStreamReader(System.in);

06: BufferedReader br = new BufferedReader(ir);

07: float salary;

08:

09: try{

10: System.out.print("請輸入月薪:");

11: salary = Float.parseFloat(br.readLine());

12: System.out.print("你的年薪是:"+salary*12);

13: } catch (IOException e) {

14: System.out.println(e.toString());

15: } catch (Exception e) {

16: System.out.println(e.toString());

17: }

(48)

程式的執行結果為:

請輸入月薪:35 你的年薪是:420.0

程式的第6行將InputStreamReader物件轉為BufferedReader物件,而在程式的第11行則使 用readLine方法來讀取輸入的整行資料。由於讀入的資料是String型態的資料,所以,

再使用「Float.parseFloat()」方法來將資料轉為float型態

BufferedReader (2)

(49)

課堂練習 PT82

串流練習 (PT82.java):

以 InputStreamReader 寫一個程式可以分別詢問使用 者他的:

◦ 名字

◦ 年齡

◦ 性別

輸入完後再將結果印在畫面:

Hi 名字 (先生/小姐) 你好,你今年 年齡 歲

(50)

讀寫檔案

 JAVA 中讀寫檔案的方法有許多種

 在此介紹英文與中日韓文語系中最常使 用的兩種類別工具:

◦ 英文讀寫:

 DataInputStream / DataOutputStream

◦ 中日韓文讀寫:

 FileReader / FileWriter

(51)

Input/Output Stream

DataInputStream和DataOutputStream類別是 位元資料處理的串接類別。

DataInputStream與DataOutputStream須藉由FileInputStream與 FileOutputStream對檔案做存取

假設有一檔案 file1.txt 內容為:

(52)

DataInputStream

以下的範例示範如何使用DataInputStream類別,並讀取「file1.txt」檔案中的資料:

程式10-14:Chap 10\ DataInputStream1.java import java.io.*;

class DataInputStream1 {

public static void main(String[] args) throws IOException {

FileInputStream fis = new FileInputStream("file1.txt");

DataInputStream dis = new DataInputStream(fis);

int i;

while((i = dis.read()) != -1) {

System.out.print((char) i);

}

dis.close();

fis.close();

} }

程式的執行結果為: You will always on my mind.

(53)

DataOutputStream

以下的範例示範如何使用DataOutputStream類別,並將資料寫入「File2.txt」檔案中:

程式10-15:Chap 10\ DataOutputStream 1.java 01: import java.io.*;

02:

03: public class DataOutputStream 1 {

04: public static void main (String[] args) throws IOException { 05: FileOutputStream fos = new FileOutputStream("file2.txt");

06: DataOutputStream dos = new DataOutputStream(fos);

07:

08: byte[] c = {'J', 'a', 'v', 'a'};

09: dos.write( c);

10:

11: fos.close();

12: dos.close();

13: } 14: }

範例程式的第5行建構出一個FileOutputStream物件,並將此物件傳給DataOutputStream 物件。程式的第9行使用「write()」方法將資料寫入「File2.txt」檔案中

(54)

FileReader/FileWrite類別

不論是FileInputStream、FileOutputStream、DataInputSteam、DataOutputStream類別,它 們都是針對位元組資料所設計的類別。對於亞洲體系的文字,由於使用的是雙位元的資 料,這些類別並無法很完善的處理這些類型的資料。如果要處理雙位元的資料,我們必 需使用FileReader、BufferedReader、FileWrite、BufferedWriter等類別。處理方式請參考 下圖:

File 類別

File 類別

(55)

FileReader 類別

以下的範例示範如何使用FileReader類別,並讀取「file1.txt」檔案中的資料:

程式10-16:Chap 10\ FileReader1.java 01: import java.io.*;

02:

03: public class FileReader1 {

04: public static void main (String[] args) throws IOException { 05: File f = new File("file1.txt");

06: FileReader fr = new FileReader(f);

07: int i;

08:

09: while((i = fr.read()) !=-1) { 10:

11: System.out.print((char) i);

12: }

13: fr.close();

14: } 15: }

程式執行的結果為:

You will always on my mind.

北風傳來了熟悉的聲音

(56)

FileWriter 類別

以下的範例示範如何使用FileWrite類別,並將字串寫入「file3.txt」檔案中的資料:

程式10-17:Chap 10\ FileWriter 1.java 01: import java.io.*;

02:

03: public class FileWriter 1 {

04: public static void main (String[] args) throws IOException { 05: File f = new File("file3.txt");

06: FileWriter fw = new FileWriter(f);

07:

08: String str = "Java is easy to learn.\n";

10:

11: fw.write(str);

12: fw.close();

13: } 14: }

(57)

檔案輸出入練習 (PT83.java):

假定某位學生的紀錄如下:

姓名:name,字串型態,值為「Alex」

數學成績:mathRecord,int型態,值為「95」

國文成績:chnRecord,int型態,值為「90」

英文成績:engRecord,int型態,值為「94」

請建構串流物件out,並將該筆記錄寫入「record.txt」檔 案中,再將「record.txt」檔案的內容讀出,並顯示在螢 幕上

課堂練習 PT83

(58)

8-4 序列化處理

(59)

 序列化:

◦ 將物件寫進串流中

◦ ObjectInputStream

 反序列化:

◦ 將被寫進串流中的物件讀出

◦ ObjectOutputStream

 要序列化某個類別,則該類別須實作 Serializable 介面

序列化處理 (1)

(60)

序列化的處理 (2)

程式10-18:Chap 10\ Employee.java 01: import java.io.*;

02:

03: public class Employee implements Serializable { 04: private String name;

05: private String ID;

06: private float salary;

07:

08: Employee(String name , String ID , float salary) { 09: this.name = name;

10: this.ID = ID;

11: this.salary = salary;

12: }

13: public String getName(){

14: return name;

15: }

16: public String getID() { 17: return ID;

18: }

19: public float getSalary() { 20: return salary;

(61)

程式10-19:Chap 10\ WriteData.java 01: import java.io.*;

02:

03: public class WriteData {

04: public static void main(String[] args) { 05: Employee[] s = new Employee[2];

06:

07: s[0] = new Employee("Alex" ,"001" , 32000.f);

08: System.out.println(s[0].getName());

09: System.out.println(s[0].getID());

10: System.out.println(s[0].getSalary());

11:

12: s[1] = new Employee("Ivy","002", 43000.f);

13: System.out.println(s[1].getName());

14: System.out.println(s[1].getID());

15: System.out.println(s[1].getSalary());

16:

17: try{

18: FileOutputStream fs = new FileOutputStream("Employee.txt");

19: ObjectOutputStream out = new ObjectOutputStream(fs);

20: out.writeObject(s);

21: out.close();

22: fs.close();

23: } catch (IOException e){

24: System.out.println(e.toString());

25: }

序列化的處理 (3)

(62)

程式的執行結果為:

序列化的處理 (4)

Alex 001 32000.0 Ivy

002 43000.0

程式執行後你會在目錄下找到「Employee.txt」文字檔,但無法直接瀏覽該檔的內容 範例程式的第5行宣告一個Employee陣列,可用來儲存2筆資料。而程式的第7~15行分 別建立Employee物件,並且將物件的內容顯示在螢幕上。程式的第18~22行是本範例程 式的重點。第18行宣告了一個FileOutputStream物件,用來將內容寫入Employee.txt檔案 中。再於第19行程式中將FileOutputStream物件傳遞給ObjectOutputStream,並建立新 的ObjectOutputStream物件。第20行程式中,使用ObjectOutputStream物件的

WriteObject方法將Employee陣列的內容寫入Employee.txt檔案中。寫入的方式很簡單,

但請注意,程式中寫入Employee陣列的資料,因此在讀取資料時,也必需將檔案的內 容輸入Employee陣列中,否則會因型態不符而產生classCastException的例外

(63)

序列化的處理 (5)

程式10-20:Chap 10\ ReadData.java 01: import java.io.*;

02:

03: public class ReadData {

04: public static void main(String[] args) { 05: try{

06: FileInputStream fs = new FileInputStream("Employee.txt");

07: ObjectInputStream in = new ObjectInputStream(fs);

08:

09: Employee[] s = (Employee[] ) in.readObject();

10:

11: for(int i = 0 ; i<s.length; i++) {

12: System.out.println(s[i].getName());

13: System.out.println(s[i].getID());

14: System.out.println(s[i].getSalary());

15: }

16: in.close();

17: fs.close();

18: } catch (Exception e) {

19: System.out.println( e.toString());

20: } 21: }

(64)

序列化的處理 (6)

程式的執行結果為:

Alex 001 32000.0 Ivy

002 43000.0

(65)

1. 庫存系統練習 (HW81.java):

假設有一個庫存系統,在存貨類別的處理方面,庫存的計算 可能會有兩例外狀態:一種是庫存數量小於

0(NegativeStockException),另一種是需要提出的貨品數量大 於庫存量(ShortStockExctpion)

請設計上述兩個例外類別,並使用throw來測試例外。

2. 存款利息計算程式 (HW82.java) : 假定有以下的資料型態:

請設計一個程式,使用者可以由鍵盤依次輸入上述的資料,

輸入完成後,再將輸入的內容顯示在螢幕上,並依輸入的本 金和利率,再計算一年的存款利息

String name; //儲存姓名的資料

float premium; //代表銀行存款的本金數額 float iRate; //代表銀行的存款利率

回家作業 – HW8 (1)

(66)

回家作業 – HW8 (2)

3. 猜數字程式 v2 (HW83.java):

請改進 HW61.java的猜數字程式。以BufferedReader的方法讓

使用者可以一直猜,直到猜對正確答案為止

(67)

繳交回家作業

 PT8 (課堂練習) 與 HW8 (回家作業) 請 一起繳交

 把相關 *.java 檔案以 zip 或 rar 壓縮並 取名為「 學號.zip 」或「 學號.rar 」並附 件到 email 中寄到:

◦ 主旨: [JAVA245][hw][8]姓名

◦ 收件人: allan@csie.org

 Deadline:下一次上課前

參考文獻

相關文件

method void setInt(int j) function char backSpace() function char doubleQuote() function char newLine() }. Class

[r]

[r]

微算機基本原理與應用 第15章

第四章: 中學報稅的設計 第五章: 初中諒程主建議 第六章: 高中諒我建議,..

強制轉型:把 profit轉換成double的型態

int main(int argc, char** argv).

微算機原理與應用 第6