Presentation is loading. Please wait.

Presentation is loading. Please wait.

9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒

Similar presentations


Presentation on theme: "9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒"— Presentation transcript:

1 9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒
第 09 章 例外處理與多執行緒 9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒 9.6 繼承Thread類別建立執行緒 9.7 實作Runnable介面建立執行緒9.8 Timer(計時器)類別 9.9 實例

2   所謂「例外」,就是當程式碼編輯完,在編譯期間沒有出現錯誤訊息,但在程式執行時卻發生錯誤,這種錯誤又稱為執行時期錯誤。
  所謂「多執行緒」,就是多工處理不同敘述區塊的程式碼,即可讓不同區塊的程式碼同時執行。   Timer類別建立計時器物件,可設定每隔一段間就執行特定的程式區段。

3 9.1 程式偵錯   在編譯程式或執行程式時常會遇到各種不同錯誤,以致無法正常完成工作。研發軟體時最容易遇到三種錯誤(Bug):語法錯誤、執行時期錯誤(Runtime error)、邏輯錯誤。 1. 語法錯誤  語法錯誤是初學者最容易犯的錯誤。在編譯過程中,系統常能立即指出  此種錯誤的所在,並要求程式設計者修正後才能正式執行。這樣錯誤最  容易解決,只要熟悉語法多多練習就可以減少錯誤產生。

4 2. 執行時期錯誤  程式在執行時,有時候會因下列情況而使得程式中斷執行,如:輸入資料不符合要求、在計算過程分母為0、磁碟中無此檔案存在、陣列的索引值超出陣列宣告範圍…等。這種錯誤的問題在編譯時,並不會發生,被Java稱為「例外」,而Java也提供了例外處理的方式來解決問題。 3. 邏輯錯誤  邏輯錯誤是最難找出的,尤其在大型應用程式最為明顯。程式在執行過程並沒有出現錯誤,也會有執行結果,甚至有時候結果是正確的。除非你仔細觀察,多人多次測試,否則不見得會發現。因此誤信程式完全正確,推出正式使用,應用過程又沒有注意異常狀況,往往造成很大損失。有些系統提供偵錯(Debug)工具,用來協助找出錯誤之處。若沒有偵錯工具,就只能自己設定偵測點,輸出目前主要變數內容是否如預測結果,以推測可能錯誤之處,再仔細研讀程式,尋找邏輯上錯誤之處,加以修正。

5 9.2 捕捉例外   當程式在執行時期發生錯誤或不正常狀況,稱之為例外(Exception)。進行例外處理是不希望程式中斷。而是希望程式能捕捉錯誤,進行錯誤補救,並繼續執行程式。若錯誤是使用者輸入不正確資料所造成的,可以要求使用者輸入正確資料再繼續執行,或者不處理使用者輸入資料繼續做其他工作。 Java使用 try … catch … finally 敘述來解決例外處理,它的方式是將被監視的敘述區段寫在try大括號內,當程式執行到try內的敘述有發生錯誤時,會逐一檢查捕捉(catch)該錯誤,以便執行該catch內敘述。最後不管是否有符合catch,都會執行最後的finally敘述區段。例外處理的格式如下:

6 1. 行01:使用try敘述,至少要一個catch(捕捉)配合。

7 2. 行04、07:多個catch(捕捉)敘述區段,由上至下catch逐一檢查,若遇到符合條件,則執行該對應敘述區段,以下catch就不再處理。其中exception1、exception2 … 等為例外類別。 3. 常用到的例外類別如下表: 4. 行10~12:finally敘述區段在最後一個catch之後,不論是否執行catch (捕捉)敘述區段,都會執行finally敘述區段。finally敘述區段也可以省略。

8

9 1. 行02~05:Division(int num1, int num2)方法為計算兩個整數相除, 然後顯示其結果。
2. 行07:呼叫Division(12, 0) 方法,因除數num2 = 0,所以第03行不 能被執行,程式被迫中斷,並在主控制台顯示分母為零的錯誤訊息。 3. 行08:呼叫Division(12, 2)方法,因程式被迫中斷,此行沒被執行。

10

11

12 1. 行02~14:Division(int num1, int num2) 方法為計算兩個整數相 除,然後顯示其結果。在此方法中應用例外處理try…catch技巧。
2. 行16:呼叫Division(12, 0) 方法,因除數num2 = 0,所以第04~ 行不會被執行,而直接跳到第07行執行catch,捕捉例外。 3. 行07:捕捉到例外,其例外類別Java.lang.ArithmeticException (算術運算錯誤)由第08~09行顯示出來,其錯誤的情形是「/ by zero」(除數為0 )。 4. 行11~13:無論有否執行catch,皆會執行finally內的敘述區塊。 5. 行17:本程式不會中斷執行,呼叫Division(12, 6) 方法,因此會執行 / 2 = 6 的結果。

13

14

15 1. 行03:宣告陣列arr,其索引範圍為0~9,共10個元素。
解說 1. 行03:宣告陣列arr,其索引範圍為0~9,共10個元素。 2. 行07:arr[10],其索引10,已超出範圍,因此第07、08行沒有被執行 ,開始接受偵測。(本例共設計了三層偵測的關卡)  首先跳至第10行接受偵測,第10行是偵測「算術運算錯誤」。  若非第10行的錯誤類型,則再跳至第13行接受偵測,第13行是偵測 「陣列索引超出宣告範圍」。  若非第13行的錯誤類型,則再跳至第16行接受偵測,第16行是偵測 程式執行時產生的其它錯誤。 很明顯第07行的錯誤是「陣列索引超出宣告範圍」,會在第13行被偵 測到,故執行了第13~15行敘述,然後離開try…catch敘述區段,因此 不會跳至第16行接受偵測。 3. 行16~18 : 例外類別Exception若移到catch敘述區段最前面時,則其餘 例行類別要刪除,否則會產生錯誤。因為Exception例外類別為第10行 和第13行例外類別的上層類別,只要有錯誤一定會執行此catch敘述區 段,後面的catch敘述區段都是多餘的。

16 9.3 自行拋出例外   在9.2節的範例所產生的例外類別,如:Exception、ArithmeticException、ArrayIndexOutOfBoundsException…等,這些例外都是JVM(Java虛擬機器)執行所產生的。在預設的情況下,當程式執行時發生例外,該例外就會被 JVM攔截並抛出,再用try…catch…finally處理。如果遇到一些特殊情形,例如:學生學期成績被輸入負的分數,我們也希望用try…catch…finally處理。此時就需要用自行抛出例外或自定抛出例外類別技巧。 本節將介紹自行抛出例外,以及如何捕捉自行所抛出的例外。自行抛出例外的方式有下列兩種: (1) 在程式敘述中,使用throw關鍵字抛出例外。 (2) 在定義方法時,使用throws關鍵字宣告該方法可能會抛出的 例外。

17 9.3.1 使用throw抛出例外 在程式敘述中,使用throw關鍵字抛出例外的語法如下: throw 例外物件;
解說 1. 例外物件:為抛出的例外物件,如 抛出ArithmeticException例外物件。 2. 【簡例】 使用new建立例外物件,建構子引數值可用getMessage()傳出。 throw new ArithmeticException(“除數為零”) ;

18

19

20 1. 行05:抛出ArithmeticException例外物件。 2. 行09-11:捕捉自行抛出的例外。
解說 1. 行05:抛出ArithmeticException例外物件。 2. 行09-11:捕捉自行抛出的例外。

21 使用throws抛出例外 若在方法內的敘述可能會發生例外,但該方法內却沒有try…catch…finally處理例外,則在定義該方法時,要使用throws關鍵字宣告該方法可能會抛出的例外。宣告的語法如下: [修飾子][static]傳回值資料型別 方法名稱([引數串列]) throws 例外類別串列 { 敘述區段; return 運算式; } 說明 1. throws 例外類別串列:即有可能是 throws 例外類別1, 例外類別2…。

22

23 1. 行03:用 throws 抛出IOException例外。

24 2. 若用第04行取代第03行,即沒使用 throws IOException 抛出可能會發生的 例外,則在編譯程式時便會產生下列錯誤訊息。
D:\Java2\J9_3_2\J9_3_2.java:12: error: unreported exception IOException; must be caught or declared to be thrown data = keyin.readLine(); ^ 1 error 3. 行13:本敘述是將字串資料轉為整數。若字串資料為非整數字串, 如『0.3』,則程式執行會發生下列錯誤訊息,程式也會中斷執行。 輸入成績 0~100 (離開直接按 <Enter> 鍵) 0.3 Exception in thread "main" java.lang.NumberFormatException: For input string: "0.3" at java.lang.NumberFormatException.forInputString (NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:492) at java.lang.Integer.parseInt(Integer.java:527) at J9_3_2.main(J9_3_2.java:14)

25

26

27 2. 但本程式若輸入資料的整數值 小於 0 或 大於 100,不會 出現錯誤訊息。
解說 1. 行12~18:使用NumberFormatException類別,捕捉非 整數字串。如果輸入為非整數字串,則執行第15~17行, 然後跳到第08行繼續執行。 2. 但本程式若輸入資料的整數值 小於 0 或 大於 100,不會 出現錯誤訊息。

28

29

30 1. 本程式為一個代表性檢查資料程式,輸入資料不正確不會中斷程式,只 要求重新輸入資料,除非直接按 鍵。

31 解說 2. 行12~20:捕捉非整數字串。(不包含第14~15行) 3. 行14~15:檢查輸入整數資料是否在0~100之間。第15行所拋出的例外 類別使用NumberFormatException。如果不是在0~100之間數,執行 第17~20行,然後跳到第08行。 4. 行12~20:捕捉非整數字串與檢查輸入資料是否在0~100之間,都使用 相同例外類別NumberFormatException,因此可合併處理。若使用不 相同例外類別,請參閱『範例J9_4_2 』解說。

32 9.4 自定例外類別 Java也能自定例外類別,然後在程式敘述中使用throw抛出自定例外類別的例外物件。 一、定義自定例外類別 說明
語法: class 自定例外類別 extends 例外類別 { : } 說明 1. 自定例外類別必須繼承系統預設或衍生出來的例外類別。 【簡例】 class CCheckNumberException extends NumberFormatException { ;

33 二、拋出自定例外類別物件 語法: throw 自定例外類別物件 ; 【簡例】
throw new CCheckNumberException ();

34

35

36

37 1. 行02~06:為自定例外類別CCheckNumberException,此類別 繼承NumberFormatException例外類別。 CCheckNumberException類別只只有一個建構子,此建構子將 字串 msg傳給父類別的建構子。 2. 行25~27:檢查成績範圍是否在0~100之間的數。 3. 行26:拋出自定例外類別CCheckNumberException的物件與類 別。 4. 行20:使用例外類別為NumberFormatException。 5. 行28:使用例外類別為CCheckNumberException。而 NumberFormatException為CCheckNumberException的父類 別。

38 三、在方法成員名稱後面加 throws 自定例外類別
【簡例】 static void CheckScore(int num) throws CCheckNumberException { if ( num < 0 || num >100 ) throw new CCheckNumberException(); }

39

40

41 1. 行08~11:CheckScore()方法為檢查數值是否在0~100之間 ,第10行 拋出 new CCheckNumberException() 自定例外類別物件。
2. 行21~32:NumberFormatException為CCheckNumberException的 父類別。兩對try…catch合併成一個try兩catch,前面的catch使用子 類別,若用父類別編譯時會有錯。

42 9.5 多執行緒 目前電腦的作業系統都具有多工(Multi Task)的功能,也就是允許兩個以上應用程式同時執行。多工作業就像餐廳來了兩桌客人(每一桌至少有一個客人),他們分別點很多道菜。但是餐廳只有一個廚師,一組廚具,為了讓他們感覺餐廳一直不停為他們服務。廚師需要輪流為他們炒菜,而不是先炒其中一桌全部點的菜,再處理另一桌的菜。只要炒菜速度夠快,客人吃菜速度不會太快,客人在吃飯過程就不會有等待感覺。如果廚師速度太慢,客人太多,就會有等待現象發生。解決這個問題就是聘請手腳靈活廚師,或者增加廚師,甚至找其他餐廳支援。這就是目前電腦CPU不斷增快,而且在同一電腦有兩個以上CPU,甚至多個電腦同時協力解決問題,尤其目前網路通暢,有人透過網路,結合網路幾千個萬個電腦來解決各種問題,使一個本來需要幾百年來運算的問題,能夠在短時間解決。

43 在應用程式中,若有A與B二組的敘述區段彼此無關聯,也就是不一定要先執行A敘述區段才能執行B敘述區段,或者先執行B敘述區段,再執行A敘述區段。兩個敘述區段可以同時執行,Java可以讓這兩組A、B敘述區段各取一個名稱,稱為執行緒(Thread)。讓A執行緒與B執行緒同時執行。這與作業系統多工處理應用程式道理是相同的。 每個程式至少會有一個執行緒,Java在主程式類別中的main()方法,是執行Java程式的入口,寫在這方法內的程式流程,就是主執行緒。到目前為止,我們所學所用的程式碼都只有在這單一執行緒下執行程式。 但多執行緒,可以一次同時執行多個程式區段。Java提供兩種建立多執行緒的方式,一種是繼承Thread類別,一種是實作Runnable介面。在正式介紹兩個執行緒以上的程式之前,我們先來認識一些多執行緒常用的方法。如下:

44 1. void run():執行新執行緒 用來撰寫新建立執行緒的程式碼,其宣告變數、呼叫其它方法、使用 其它類別…等用法與main()相同。但main()是主執行緒的流程入口, 而本方法run()需要用start()方法來呼叫。 2. void start():啟動新執行緒 用來呼叫run()方法,即新執行緒的流程入口。 3. Thread.sleep(long 毫秒):延遲 使執行緒進入延遲或暫停狀態,延遲的時間以毫秒為單位計數, 毫秒等於1秒。因使用Thread.sleep()方法會拋出一個 InterruptedException 例外來catch,所以Thread.sleep()方法必須寫在 try區段內。(參考範例J9_6_1)。

45 9.6 繼承Thread類別建立執行緒 使用繼承Thread類別來建立執行緒的方式,要有四個步驟,格式如下:
01 class 執行緒類別名稱 extends Thread { 資料成員; 方法成員; public void run() { 執行緒的敘述區段; } 04 } 05 class 主類別名稱 { public static void main(String arg[]) { : 執行緒類別名稱 執行緒物件 = new執行緒類別名稱(); 執行緒物件.strat(); } 10 }

46 說明 1. 行01:為第一個步驟。宣告一個「執行緒類別」來繼承Thread類別。
2. 行02:為第二個步驟。將執行緒所要執行的程式碼,寫在run()方法裡面。當run()方法執行完畢,該執行緒即執行完畢。 3. 行07:為第三個步驟。建立繼承Thread類別的執行緒類別物件,該物件稱為「執行緒物件」。 4. 行08:為第四個步驟。使用執行緒物件的start()方法,啟動執行緒並間接呼叫寫在執行緒類別的run()方法。 5. 行05:第07~08行可以寫在其他類別中,不一定要在主類別中。

47

48

49

50 1. 程式中有兩個執行緒同時執行,第01行的CAddThread執行緒類 別與第16行的CFactThread執行緒類別。在兩執行緒中皆有安排 0~1秒內不固定時間的延遲,使每次執行緒交互執行的情形不一 樣,造成不同的執行結果。 2. 行01~15:為CAddThread執行緒類別,執行累加2共5次。 3. 行16~29:為CFactThread執行緒類別,執行共計算5個階乘。 4. 使用繼承Thread類別的方式來建立執行緒,並執行的方式,要有 四個步驟。本程式有兩個執行緒,因此有兩組四個步驟,分別出現 在第01、02、32、34行和 第16、17、33、35行。 5. 執行緒的程式執行內容寫在run()方法內,但啟動執行緒run()方法 需要用start()方法來呼叫,所以start()方法是執行緒程式碼被啟動 的進入口。執行緒在run()方法內的敘述執行完畢即結束。 6. 行09~12、行23~26:是應用Thread.sleep()方法分別讓兩執行緒 執行時,在迴圈中造成延遲的效果。每一個執行緒皆有5次延遲, 每一個延遲時間皆在0~1秒之間,但不固定。因此每一次重新執行 本程式時,兩個執行緒交互被執行,無法確定哪一個執行緒先執 行,哪一個執行緒先結束。

51 9.7 實作Runnable介面建立執行緒   一個類別只能繼承一個父類別,萬一要建立執行緒的類別必須繼承其它父類別而無法繼承Thread類別時,可用實作Runnable介面來建立執行緒。使用實作Runnable介面來建立執行緒的方式,有五個步驟,格式如下:

52 01 class 執行緒類別名稱 implements Runnable {
資料成員; 方法成員; public void run() { 執行緒的敘述區段; } 04 } 05 class 主類別名稱 { public static void main(String arg[]) { : 執行緒類別名稱 物件 = new執行緒類別名稱(); Thread 執行緒物件 = new Thread(物件); 執行緒物件.strat(); } 11 }

53 1. 行01:為第一個步驟。宣告一個執行緒類別來實作Runnable介面。
2. 行02:為第二個步驟。將執行緒所要執行的程式碼,寫在run()方法裡面。當run()方法執行完畢,該執行緒即執行完畢。 3. 行07:為第三個步驟。配合第01行所宣告的類別建立一個類別物件 4. 行08:為第四個步驟。再建立一個Thread物件,該物件稱為「執行緒物件」。 行07、08:可以寫在其他類別中,不一定要在主類別中。可合併寫成 6. 行09:為第五個步驟。使用執行緒物件的start()方法,啟動執行緒並間接呼叫寫在執行緒類別的run()方法。 Thread 執行緒物件 = new Thread(new 執行緒類別名稱);

54

55

56

57 1. 行01~22:程式中只有一個執行緒類別CAddThread,因此只有一個run()方法。
2. 行27~28:有兩個執行緒物件 add2_thread累加2與add5_thread累加5。而這兩個執行緒物件會各自執行自己的執行緒內容,彼此互不干擾。 3. 本程式的兩個執行緒共用了CAddThread類別,也共用了run()方法。但因傳入的引數值不同,而有不同的執行內容。使用實作Runnable介面的方式來建立執行緒的方式,要有五個步驟,這兩組的五個步驟分別出現在第01、08、25、27、29行和第01、08、26、28、30行。 行25、27:可合併一行寫成 同理,第26、28行亦可以合併。 Thread add2_thread = new Thread(new CAddThread("累加2執行緒:", 2));

58 9.8 Timer(計時器)類別 如果需要每隔一段時間就觸發一個或多個事件(ActionEvent)以改變內容或執行特定程式,可以使用Timer類別來製作計時器。 Timer類別的建構子與常用的方法如下: 1. Timer類別的建構子 Timer(int delay, ActionListener listener) 建立一個計時器物件,以delay引數值(單位:毫秒)為間隔的時間,定時觸 動計時器一次。而listener為計時器物件的事件傾聽者。 2. void start() 啟動計時器物件,使它開始向其傾聽者發送動作事件。 3. void stop() 停止計時器物件,使它停止向其傾聽者發送動作事件。 4. void restart() 重新啟動計時器物件。

59

60

61

62

63 1. 行07:建立一個計時器物件timer。設定每隔1000毫秒(1秒),觸動 timer計時器一次,而以本視窗為計時器事件的傾聽者。
2. 行22~29:當 (btnReset)鈕被點按後要執行之程式敘述。 3. 行30~52:當btnStart物件被點按後要執行之程式敘述。btnStart鈕 有三種外貎,分別為 、 、 。

64

65 9.6 實例 分析 視窗中有兩個放入圖片的標籤物件lblPic1(免子)、lblPic2(烏龜)。兩標籤圖片從視窗左側出發,當標籤圖片移到視窗的右側邊框時,會再從視窗左側出發。免子與烏龜移動時機由電腦亂數決定,但免子移動的速度約為烏龜的兩倍。

66

67

68 1. 行02~25:為CMovePic執行緒類別。 2. 行05~10:為CMovePic執行緒類別建構子。此建構子傳入4個 引數:  lblPic:移動標籤圖片。  end_x:標籤圖片到視窗的右邊的x座標。  pos_y:標籤圖片在視窗內的y座標。  d_time:標籤圖片移動的延遲時間。

69 3. 行11~19:run()方法的敘述區段,設計標籤圖片由 視窗的左邊出發移動到視窗的右邊。若已到了右 邊,標籤圖片再由左邊重新出發移動。
4. 在第35、36行有兩個執行緒物件movePic1及 movePic2,這兩個各自傳入4個引數來執行自己的 執行緒內容,彼此互不干擾。

70

71 1. 猜數的操作:以亂數產生0~99數。 2. 所猜數太大或太小都會有提示,並且告知目前已猜幾次。 3. 視窗下方有一部小汽車,由左至右不斷重複移動。

72

73

74

75

76 1. 本程式看似只有一個汽車移動的執行緒,其實主程式中的main() 方法也是一個執行緒。因此本程式亦屬於多執行緒的應用程式。
2. 行03~26:CMovePic執行緒類別,用來執行汽車移動的執行緒程式 碼,汽車移動執行緒的物件建立與啟動在第50、51行。其設計原理, 請參考實例J9_9_1的解說。 3. 行27~80:CGuessF類別,此為猜數遊戲。被猜數由電腦產生0~ 中一個數(第48、61行)。

77

78 1. 「賽跑」、「猜數遊戲」兩視窗應用程式,皆各自有多執行緒、多工表達。而這兩視窗又是可獨立操作、互不干擾,也是一種另類的多工表現。
2. 你可以一邊玩猜數遊戲,然後一邊觀賞賽跑,看哪一個跑在前面。 3. 本範例的程式碼,幾乎皆可由實例J9_9_1與J9_9_2直接拷貝過來,其中CMovePic執行緒類別的程式碼還是共用。

79

80

81

82

83 1.行03:CMovePic執行緒類別是兩視窗(賽跑視窗CMovePicF與猜數視窗CGuessF)皆會使用到的類別,是用來執行標籤物件移動的執行緒程式碼。
2.行11~15:CMovePicF類別是實例J9_9_1「賽跑」所使用的類別。其中第13行有修改,原來的程式碼是 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  其目的是當點按了視窗關閉鈕   時,只會移除該視窗,而不會影響到其它視窗的執行狀態。

84 3. 行16~22:CGuessF類別是實例J9_9_2 「猜數遊戲」使用的類別。其中第18行有修改,解說同上。
4. 行23~50:CMainF類別是主視窗的類別,視窗內有三個按鈕選項。  第39~42行:當點按 (btnPlay1)鈕,建立與開啟 playF1「賽跑」視窗,並重新設定該視窗擺放位置。  第43~46行:當點按 (btnPlay2)鈕,建立與開啟 playF2「猜數遊戲」視窗,並重新設定該視窗擺放位置。  第47~48行:當點按 (btnEnd)鈕,結束應用程式 執行。


Download ppt "9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒"

Similar presentations


Ads by Google