Microsoft PowerPoint - SE7ch10.ppt

Similar documents
Microsoft Word - 投影片ch13

chp6.ppt

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

Microsoft PowerPoint - ch15.ppt

Microsoft PowerPoint - 13_Exception.ppt

投影片 1

0 0 = 1 0 = 0 1 = = 1 1 = 0 0 = 1

Microsoft PowerPoint - EmbSys101_Java Basics.ppt [相容模式]

untitled

Microsoft Word - JAVA Programming Language Homework I ans

Microsoft PowerPoint - 13_ClassAndObj.ppt

10-2 SCJP SCJD 10.1 昇陽認證 Java 系統開發工程師 的認證程序 Java IT SCJD

EJB-Programming-4-cn.doc

任務二 : 產生 20 個有炸彈的磚塊, 放在隨機的位置編輯 Block 類別的程式碼 import greenfoot.; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) Write a description of class

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

untitled

雲端 Cloud Computing 技術指南 運算 應用 平台與架構 10/04/15 11:55:46 INFO 10/04/15 11:55:53 INFO 10/04/15 11:55:56 INFO 10/04/15 11:56:05 INFO 10/04/15 11:56:07 INFO

JavaIO.PDF

Microsoft Word - 04_object_Boxing_property_indexer.doc

Chapter 9: Objects and Classes

4. 如下的程式碼中, 宣告了類別 A3 和 B3 由於在兩個類別中都定義了 print() 的方法, 所以依據定義, 總共有兩次的 overloading 以及一次的 overriding public class A3 { public class B3 extends A3 { protect

3.1 num = 3 ch = 'C' 2

第1章

Microsoft Word - JAVA Programming Language Homework VI_ans.doc

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

運算子多載 Operator Overloading

Chapter 9: Objects and Classes

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌

Microsoft Word - 投影片ch15

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

男人的大腦 女人的大腦

untitled

untitled

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

第1章

EJB-Programming-3.PDF

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数


2009年3月全国计算机等级考试二级Java语言程序设计笔试试题

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Microsoft Word - 01.DOC

untitled

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

<A4E2BEF7B4FAB8D5B3F8A F52322E786C7378>

Java Access 5-1 Server Client Client Server Server Client 5-2 DataInputStream Class java.io.datainptstream (extends) FilterInputStream InputStream Obj

The Embedded computing platform

使用MapReduce读取XML文件

Python a p p l e b e a r c Fruit Animal a p p l e b e a r c 2-2

Microsoft PowerPoint - SE7ch05.ppt

Microsoft PowerPoint - SE7ch02.ppt

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

Microsoft PowerPoint - ch04_AEL0080.ppt

Java

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

Chapter 3 Camera Raw Step negative clarity +25 ] P / Step 4 0 ( 下一頁 ) Camera Raw Chapter 3 089


投影片 1

内容介绍 6.1 任务预览 6.2 异常 6.3 异常种类与层次结构 6.4 异常处理代码块 try-catch-finally 6.5 throw 语句与 throws 子句 6.6 自定义异常类 6.7 异常处理代码块嵌套 6.8 错误与断言 6.9 本章小结 6.10 实训 6: 除法运算程序

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

《大话设计模式》第一章

Microsoft PowerPoint - 14Threads.ppt

untitled

Microsoft PowerPoint - chap08.ppt

Microsoft PowerPoint - P766Ch06.ppt


Microsoft PowerPoint - SE7ch07.ppt

Microsoft Word - Mail2000_SecurityPatch_

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

Microsoft PowerPoint - Chap03.ppt [相容模式]

CU0594.pdf

Spyder Anaconda Spyder Python Spyder Python Spyder Spyder Spyder 開始 \ 所有程式 \ Anaconda3 (64-bit) \ Spyder Spyder IPython Python IPython Sp

一、

Microsoft Word - 投影片ch11

2009年9月全国计算机等级考试二级Java真题及答案

Microsoft PowerPoint - STU_C_Lang_CH05

第1章

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

Microsoft Word - GK40程式語言_題+解+評OK_.doc

Java 程式設計初階 第 5 章:基本輸出入 & 流程控制

javaexample-02.pdf

Microsoft PowerPoint - B9-2.pptx

Learning Java

附录J:Eclipse教程

Visual C# 2005程式設計

PowerPoint 簡報

CC213

JBuilder Weblogic

星星排列 _for loop Protected Sub Page_Load(ByVal sender As Object, ByVal e As Dim h As Integer = 7 'h 為變數 ' Dim i, j As Integer For i = 1 To h

Java 1 Java String Date

Microsoft PowerPoint - C-Ch11.ppt

基于CDIO一体化理念的课程教学大纲设计

Microsoft PowerPoint - SE7ch03.ppt

The golden pins of the PCI card can be oxidized after months or years

Microsoft PowerPoint - chap07.ppt

软件工程文档编制


untitled

Transcription:

第十章例外處理 課前指引通常讀者學習到本章時, 已經有一些撰寫程式的經驗, 您是否覺得當程式實際運作時, 常常會發生一些非預期的錯誤呢? 先別煩惱,Java 為了這些問題提出了解決之道, 本章將為您解決這些問題 章節大綱 10.1 什麼是例外 10.2 例外的種類 10.6 例外的處理與轉交 10.7 巢狀例外 10.3 例外的引發 10.4 例外的處理 10.8 重新丟出例外 10.9 本章回顧 10.5 throws 由方法拋出例外 備註 : 可依進度點選小節

10.1 什麼是例外 撰寫程式的過程可以分為三個層次來討論 (1) 按照需求撰寫合乎語法, 可成功編譯的程式 (2) 假設程式編譯成功, 則按照需求檢查與測試程式是否符合需求 ( 一般用於正確輸入的測試 ) (3) 檢查與測試程式是否能夠接受各種非預期的狀況 ( 一般用於錯誤輸入的測試 ) 若第一階段有錯誤, 稱之為語法錯誤或編譯時期錯誤 若第二階段有錯誤, 稱之為語意錯誤或語意不符合需求 若第三階段有錯誤, 稱之為執行時期的錯誤 (Run Time Error; 簡稱執行期錯誤 ), 而這些錯誤也就是 程式執行的例外狀況 3 10.1 什麼是例外 第一階段的錯誤是屬於比較容易解決的問題第二階段的錯誤則和程式設計師的功力有關 ( 是否具備將問題使用程式解決的能力 ) 至於第三階段的錯誤, 則和程式設計師對於每個輸入與執行細節的專注力有關 通常, 第三階段的錯誤, 是難以避免的 甚至可能必須將程式交到使用者手上, 執行過一段時間後才會被發現 4

10.1 什麼是例外 本章介紹的 例外處理 就是針對第三階段錯誤 ( 即例外狀況 ) 的捕捉與處理 由於現今的程式越來越龐大, 因此第三階段錯誤發生的可能性大增, 故而 例外處理 更形重要 在 JDK 7.0 版中 Java 也針對 例外處理 提供了更具有彈性的語法, 使得程式設計師能夠輕鬆地撰寫程式處理例外情況 5 10.1 什麼是例外 例外狀況 是執行時期的錯誤, 而那些是屬於執行時期的錯誤呢? 舉例來說, 當您的程式中使用了除法, 而除數恰為 0 時, 就會發生這種錯誤 當然, 如果您在進行所有除法之前, 先檢查除數是否為 0, 就可以避免這種錯誤, 但這並不代表, 您只需要檢查使用者輸入的值是否為 0 而已, 因為除數可能是使用者輸入的數字經過複雜運算後得到的值 例如 y/(100-x), 假設 x 為使用者輸入的資訊, 則 x 為 100 時, 將會導致除數為 0 6

10.1 什麼是例外 有些時候, 執行時期的錯誤甚至與使用者的輸入無關, 而是和執行程式時的環境有關舉例來說, 假設程式要讀取磁碟機 A 的檔案, 而磁碟機 A( 一般是軟碟機 ) 根本沒有放入磁片 ( 甚至沒有磁碟機 ), 也會發生執行期錯誤 又例如, 程式欲讀取網路遠端的資料庫資料時, 但網路卻在中途因不明原因斷線, 也會發生執行期錯誤 這種種的執行期錯誤, 編譯器都無法幫您檢查出來, 且程式設計師通常在設計程式時也無法完全考慮進去 7 10.1 什麼是例外 Java 是一種物件導向程式語言, 並有利於開發大型程式, 換句話說,Java 必須對於設計大型程式可能遭遇的種種問題, 提出解決方案 對於小型程式 ( 如 500 行以內的程式 ) 而言, 上述的執行期錯誤通常容易被發現並加以解決, 而對於大型程式 ( 如數千行 數萬行 甚至數十萬行的程式 ) 而言, 上述的執行期錯誤不但更容易發生, 且更難完全由程式設計師發現 因此,Java 提供了完整的例外處理機制, 讓程式在執行時期, 可以捕捉例外狀況並發現錯誤的原因和位置, 作為程式設計師修改錯誤程式碼的依據 8

10.1 什麼是例外 例外與錯誤 程式於執行時發生無法執行而中斷的狀況可以分為例外與錯誤 其中錯誤 (Error) 指的是非常嚴重而無法處理的狀況, 例如作業系統錯誤 硬碟故障 記憶體故障等等 而例外 (Exception) 則屬於較輕微可被處理的狀況, 例如欲開啟的檔案不存在, 陣列索引為負值等等 本章要介紹的執行期錯誤屬於例外狀況 (Exception), 至於錯誤 (Error) 則無法由 Java 程式來處理 9 10.1 什麼是例外 觀念範例 10-1 : 撰寫一個除數為 0 的程式並執行它, 以產生一個執行期錯誤 範例 10-1:ch10_01.java( 隨書光碟 myjava\ch10\ch10_01.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* 檔名 :ch10_01.java 功能 : 發生例外狀況的範例 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_01 // 主類別 public static void main(string args[]) fraction obj= new fraction(); obj.set_value(); obj.print_value(); 10

10.1 什麼是例外 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class fraction public fraction() console=system.console(); private Console console; private int numerator; // 分子 private int denominator; // 分母 public void set_value() System.out.print(" 輸入分子 :"); numerator=integer.parseint(console.readline()); System.out.print(" 輸入分母 :"); denominator=integer.parseint(console.readline()); public void print_value() System.out.print(numerator + "/" + denominator + "="); System.out.println(numerator/denominator); 這個除法可能會產 生例外 11 10.1 什麼是例外 執行結果 : 第一次執行 第二次執行 C:\>java myjava.ch10.ch10_01 輸入分子 :8 輸入分母 :4 8/4=2 C:\>java myjava.ch10.ch10_01 輸入分子 :2 輸入分母 :0 2/0=Exception in thread "main" java.lang.arithmeticexception: / by zero at myjava.ch10.fraction.print_value(ch10_01.java:33) at myjava.ch10.ch10_01.main(ch10_01.java:13) 範例說明 : (1) 在第一次執行的時候輸入的值為 8 和 4, 所以執行結果很正常 程式符合 Java 語法 ( 能通過編譯器的檢查 ) 且從程式內容與執行結果看來, 也都符合運算 12

10.1 什麼是例外 (2) 在第二次執行的時候輸入的值為 2 和 0, 此時執行到第 33 行時, 就會發生 2 除以 0 的情形, 而產生例外狀況 當然, 在範例 10-1 中, 如果在進行除法之前先透過 if 敘述檢查除數是否為 0, 然後才決定是否執行除法, 就可以避免出現執行時期的錯誤不過一旦程式變大, 這些用來處理例外的 if-else 敘述, 可能會佔去程式的很大篇幅並影響程式執行效率而過多的 if-else 敘述, 也會使得程式難以閱讀並且還不能保證不會有所遺漏因此,Java 針對於這一點提供了例外處理機制, 使得程式效率不會影響太大, 並且也更容易閱讀 在範例 10-1 中, 由於我們並未撰寫處理例外的程式, 因此它將採用 Java 預設的例外處理機制也就是先拋出例外 ( 如執行結果中的 2/0=Exception in thread ), 然後停止程式的執行 13 10.2 例外的種類 如果我們要處理例外, 而非採用預設的例外處理機制, 則必須對 Java 的例外處理機制先進行一番了解 首先, 在 Java 例外處理中, 每個例外都是一個物件, 並且當例外產生時, 代表它產生某個例外類別的物件, 我們可以適當地處理這個物件, 以達到例外處理的目的 至於例外類別則包含兩大類 (1)Java 內建的例外類別 (2) 自定例外類別我們將在本節中分別說明 14

10.2.1 Java 內建的例外類別 錯誤可能產生的原因有很多, 而 Java 針對各類常見錯誤, 定義了不同的類別, 這些類別大體上都繼承自內建的 java.lang.throwable 類別, 並且又可以分為兩大類 一類繼承自 java.lang.exception 類別另一類則繼承自 java.lang.error 類別 其中, 繼承自 java.lang.error 的類別, 代表的是極嚴重的錯誤, 例如動態繫結發生錯誤, 此時,JVM 會丟出 Error 物件, 但通常由於錯誤過於嚴重, 因此, 我們並不會去處理此種錯誤 15 10.2.1 Java 內建的例外類別 另一類繼承自 java.lang.exception 的類別, 代表的是不太嚴重的錯誤, 我們將之稱為例外, 這些類別則稱為例外類別 例如範例 10-1 的 java.lang.arithmeticexception 類別代表的是算術運算的例外 ( 如除以 0) 而 NegativeArraySizeException 類別則代表程式欲建立一個負值大小的陣列所造成的例外 16

10.2.1 Java 內建的例外類別 針對 Java 的內建例外類別, 在該方法定義時就會列出例如在範例 B-1( 請見附錄 B.8 節 ) 所使用的 BufferedReader 類別的 readline 方法, 它就指明了會丟出 (throw) IOException 例外, 語法節錄如下 : readline public String readline() throws IOException Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed. Returns: A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached Throws: IOException - If an I/O error occurs 17 10.2.1 Java 內建的例外類別 IOException 類別的繼承表如下, 它是用來處理與 IO 有關的例外 ( 例如緩衝讀取時發生的例外 ): IOException 類別的父類別為 java.lang.exception, 它是許多例外類別的父類別, 提供了應用程式補捉與處理例外的基本例外類別, 故大多數的例外類別都繼承自它 下表是它的幾個重要子類別的功能說明 18

10.2.1 Java 內建的例外類別 java.lang.exception 之常用子孫類別孫 java.lang.classnotfoundexception java.lang.clonenotsupportedexception 孫 java.lang.illegalaccessexception 孫 java.lang.instantiationexception java.lang.interruptedexception 孫 java.lang.nosuchfieldexception 孫 java.lang.nosuchmethodexception java.lang.runtimeexception 功能找不到指定類別 使用繼承自 Object 類別的 clone() 方法, 但該類別並未實作 Cloneable 介面 ( 這是 Java 的特殊規定, 子類別欲使用 clone 方法, 必須實作 Cloneable 介面 ) 非法取用封裝等級不容許的物件成員 透過 newinstance() 方法建立物件實體失敗 原因可能是該類別為抽象類別或介面 無法中斷執行緒的錯誤, 原因可能是該執行緒並非處於執行狀態 ( 這與多執行緒有關, 後面章節再作說明 ) 找不到該類別的欄位 找不到該類別的方法 JVM 運作時會自動丟出的例外 其中,java.lang.RuntimeException 例外類別又包含許多子類別, 整理如下表格 : 19 10.2.1 Java 內建的例外類別 java.lang.runtimeexception 之子孫類別 功能 java.lang.arithmeticexception 算術運算式的錯誤, 例如除以 0 java.lang.arraystoreexception 將型態不符的物件設定為物件陣列的元 java.lang.classcastexception 素 類別轉型錯誤 java.lang.enumconstantnotpresentexception 存取列舉型態未提供對應的內容 java.lang.illegalargumentexception 孫 java.lang.illegalthreadstateexception 孫 java.lang.numberformatexception java.lang.illegalmonitorstateexception java.lang.illegalstateexception java.lang.indexoutofboundsexception 孫 java.lang.arrayindexoutofboundsexception 孫 java.lang.stringindexoutofboundsexception 呼叫方法時引數錯誤 引數為不合法的執行緒狀態 引數並非合法的數值格式 ( 例如將字串引數轉為數值時 ) 不允許監視權限的執行緒欲監視物件 執行緒狀態不符合該運作的狀態需求 索引超過界限 陣列索引超過界限 字串索引超過界限 (charat 方法 ) java.lang.negativearraysizeexception java.lang.nullpointerexception java.lang.securityexception java.lang.typenotpresentexception java.lang.unsupportedoperationexception 建立元素個數小於 0 的陣列 欲使用物件的成員, 但物件參考指向 null 違反 Java 的安全性, 此例外由安全管理器丟出 型態並不存在 要求執行的操作並不支援 20

10.2.2 自定例外類別 Java 的內建例外類別主要是根據執行時期常見的例外進行捕捉與處理, 但它無法完全滿足各式各樣的程式變化, 因此除了使用 Java 內建的例外類別之外,Java 也允許我們自定一些例外類別 自定的例外類別至少必須繼承 Throwable 類別, 才能作為例外類別而一般常見的做法則是將自定的例外類別繼承自 Exception 類別 ( 它為 Throwable 的子類別 ) 語法格式如下 : class 自定例外類別名稱 extends Exception public 自定例外類別名稱 () // 建構子 super(); // 必須為第一行 // 其他敘述 // 其他成員 21 10.3 例外的引發 例外的引發可以分為兩大類一類是由系統自動引發另一類則是在程式中透過 throw 敘述自行引發 系統自動引發例外對於 Java 內建的例外類別而言, 如果其中任一種例外發生時, 會產生一個例外物件, 並將之傳給執行時期系統, 而系統會自動尋找負責處理該例外的程式碼並執行 觀念範例 10-2 : 系統自動引發例外 範例 10-2:ch10_02.java( 隨書光碟 myjava\ch10\ch10_02.java) 22

10.3 例外的引發 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* 檔名 :ch10_02.java 功能 : 系統自動引發例外 */ package myjava.ch10; import java.lang.*; public class ch10_02 // 主類別 public static void main(string args[]) System.out.println(" 這一行會被執行 "); int ary[] = new int[-2]; System.out.println(" 這一行不會被執行 "); 雖然是負值, 但可以通過編譯 由於例外產生, 所以不會執行這一行 執行結果 : C:\>java myjava.ch10.ch10_02 這一行會被執行 Exception in thread "main" java.lang.negativearraysizeexception at myjava.ch10.ch10_02.main(ch10_02.java:11) 23 10.3 例外的引發 範例說明 : 本程式在執行到第 11 行時, 由於宣告一個負值的陣列大小, 故符合 NegativeArraySizeException 內建例外類別的狀況, 此時, 將產生一個例外物件給執行系統, 執行系統找尋處理該例外的程式碼時, 發現程式設計師並未撰寫, 因此採用內定的例外處理, 也就是印出該例外的描述字串, 然後停止程式的執行, 所以第 12 行不會被執行到 透過 throw 敘述自行引發除了由系統自動引發例外, 我們也可以透過 throw 敘述丟出一個例外物件, 此例外物件可以是內建的例外類別物件, 也可以是自定的例外類別物件 請見下列兩個範例 24

10.3 例外的引發 觀念範例 10-3 : 透過 throw 敘述丟出內建例外類別物件 範例 10-3:ch10_03.java( 隨書光碟 myjava\ch10\ch10_03.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* 檔名 :ch10_03.java 功能 : 透過 throw 敘述丟出內建例外類別物件 */ package myjava.ch10; import java.lang.*; public class ch10_03 // 主類別 public static void main(string args[]) throw 可以丟出一 System.out.println(" 這一行會被執行 "); 個例外物件 int i=-2; if(i<0) throw new NegativeArraySizeException(); //int ary[] = new int[i]; System.out.println(" 這一行不會被執行 "); 25 10.3 例外的引發 執行結果 : C:\>java myjava.ch10.ch10_03 這一行會被執行 Exception in thread "main" java.lang.negativearraysizeexception at myjava.ch10.ch10_03.main(ch10_03.java:13) 範例說明 : (1) 這個程式和範例 10-2 很像, 但我們將陣列宣告設為註解, 所以很明顯例外並不是由陣列宣告所引發 而是我們在第 13 行手動使用 throw 敘述引發, 而該行 throw 敘述丟出的例外物件是 NegativeArraySizeException 類別的物件, 這是我們自行指定的 ( 您也可以改為丟出其他種類的例外物件 ), 在下一個範例中, 我們將會丟出自定例外類別的物件 (2) 而第 14 行即使把註解取消, 也不會引發例外, 這是因為, 第 14 行根本不會被執行, 因為第 13 行被執行而丟出例外物件後, 會依照內定的處理程序結束程式 26

10.3 例外的引發 觀念範例 10-4 : 透過 throw 敘述丟出自定例外類別物件 範例 10-4:ch10_04.java( 隨書光碟 myjava\ch10\ch10_04.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* 檔名 :ch10_04.java 功能 : 透過 throw 敘述丟出自定例外類別物件 */ package myjava.ch10; import java.lang.*; public class ch10_04 // 主類別 public static void main(string args[]) throws CmyException System.out.println(" 這一行會被執行 "); int i=-2; if(i<0) throw new CmyException(" 陣列大小出現負數 "); int ary[] = new int[i]; // 這一行不會被執行 System.out.println(" 這一行不會被執行 "); 要丟出自定類別的例外物件, 這邊應該註明 例外丟出後, 這裡不會被執行 27 10.3 例外的引發 18 19 20 21 22 23 24 25 26 27 28 29 class CmyException extends Exception public CmyException() // 建構子 super(); public CmyException(String msg) super(msg); // 建構子 執行結果 : C:\>java myjava.ch10.ch10_04 自定的例外類別這一行會被執行 Exception in thread "main" myjava.ch10.cmyexception: 陣列大小出現負數 at myjava.ch10.ch10_04.main(ch10_04.java:13) 28

10.3 例外的引發 範例說明 : (1) 這次我們丟出的例外物件, 是自定例外類別的物件, 由於執行系統不會自動捕捉處理此類例外物件, 因此我們必須在發生例外的方法 main() 之後加上 throws CmyException, 否則無法通過編譯 (2) 請注意執行時期的錯誤訊息部分, 它已顯示出我們的自定例外類別 (3) 本範例第 14~15 行仍然不會被執行, 因為系統會自動結束程式 如果想要讓程式能夠繼續被執行, 則必須對於捕捉到的例外, 進行後續處理, 而非由內定的處理程序來處理例外 Coding 注意事項 內定的例外處理程序是在文字模式下印出錯誤訊息, 但不一定會自動結束程式 在文字模式下的例外產生時, 系統會自動結束程式 ; 但在 GUI 視窗程式模式下所發生的例外, 則可能不會自動結束程式 ( 仍會在文字模式下印出錯誤訊息 ) 所以例外的處理最好由程式設計師自行定義, 避免當例外發生時, 未被察覺 29 10.4 例外的處理 Java 處理例外狀況一共分為四項動作, 分別是 try( 測試 ) throw( 丟出 ) catch( 抓取並處理 ) 與 finally 在之前我們已經介紹過 throw 是用來丟出例外 而其他三項則是一個特殊的敘述, 稱之為 try catch [finally ] 敘述由敘述格式可知,finally 部分不一定存在, 而它的功用則是 不論例外是否發生, 都一定會執行的敘述區塊 30

10.4 例外的處理 故詳細的 try catch [finally ] 敘述格式如下 : try // 預期可能會發生例外的敘述 catch( 例外類別 1 例外類別 2 例外物件名稱 ) // 對應的處理程序 catch( 例外類別 3 例外物件名稱 ) 如果只有一種例外類別, 則不必加上 // 對應的處理程序... 其他的 catch 敘述... finally // 不論例外是否發生都一定會執行的敘述 // 其他敘述 31 10.4 例外的處理 語法說明 : (1) 程式設計師對於任何可能發生例外的敘述, 都可以撰寫在 try 區塊中, 除了可以包含自定類別的例外之外, 並且也可以包含內建例外類別所規範之例外 只不過, 對於自定類別的例外, 我們應該手動透過 throw 丟出例外, 而內建例外類別的例外則可以由系統自動丟出 (2)catch 區塊是當例外產生時, 撰寫例外處理程序的程式所在, 這裡可以包含一個以上的 catch 區塊, 但每個區塊之例外引數的類別型態不能相同, 否則會出現編譯錯誤, 因為無法判定當該種類的例外產生時, 要執行哪一個區塊 (3) 如果有兩個以上的 catch 區塊之 對應的處理程序 完全相同, 則可以將兩個 catch 區塊合併在一起, 只要將 catch 內的例外物件之資料型態使用 作為 or 來撰寫即可 ( 但各例外類型中, 不可以有包含的效果 ), 而物件則可以共用 ( 這是 JDK7.0 新增的功能, 稱之為 Multi-catch) 例如 catch( 例外類別 1 例外類別 2 例外物件名稱 ) 如果只有一種例外類別型態時, 則不必加上 32

10.4 例外的處理 (4)finally 區塊, 若有 不論例外是否發生都必須執行 的敘述, 則可以放在此區塊中, 若沒有這個需要, 此區塊也可以不寫 (5)try catch [finally ] 敘述是一個完整的敘述, 代表我們要自行處理例外, 故在 try 區塊中發生例外時, 只要是被任一個 catch 捕捉到, 則 Java 預設的例外處理程序就不會被執行, 而是執行某一個 catch 區塊 並且由於程式不會被中斷, 因此敘述之後的其他敘述仍舊會執行 但如果沒有被任何一個 catch 捕捉到, 則 Java 預設的例外處理程序仍舊會自動被執行, 而中斷程式 (6) 請注意, 上述的眾多 catch 區塊只有第一個捕捉到例外的區塊會被執行 其餘則不會被執行 (7) 上述區塊內仍舊可以有各類敘述, 包含其他的 try catch [finally ] 敘述, 如此將會形成巢狀例外, 我們將於後面章節解釋巢狀例外的使用方法 (8) 上述語法可以透過圖 10-1 的流程圖來表示其執行流程 : 33 10.4 例外的處理 圖 10-1 例外處理的流程 34

10.4 例外的處理 實用範例 10-5 : 設計一個包含例外處理的程式, 程式中宣告陣列存放開獎球號, 而開獎球數應該控制在 6~48 球間, 並且可由使用者決定, 若不符合, 則將之設定為 6 球 範例 10-5:ch10_05.java( 隨書光碟 myjava\ch10\ch10_05.java) 1 2 3 4 5 6 7 8 /* 檔名 :ch10_05.java 功能 : 自訂例外處理與 Multi-catch */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_05 // 主類別 35 10.4 例外的處理 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public static void main(string args[]) Console console=system.console(); System.out.print(" 請輸入陣列大小 :"); int lottosize=0; try lottosize=integer.parseint(console.readline()); if(lottosize>48) throw new CmyException1(" 例外訊息 : 陣列太大 "); else if(lottosize<6) throw new CmyException2(" 例外訊息 : 陣列太小 "); catch(cmyexception1 CmyException2 e) System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); lottosize=6; finally 不論是 CmyException1 或 CmyException2 類型的例外, 都執行這兩個動作 System.out.println(" 樂透開獎球數為 " + lottosize); int lottoary[] = new int[lottosize]; System.out.println(" 存放樂透開獎的陣列實體產生完畢 "); 36

10.4 例外的處理 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class CmyException1 extends Exception public CmyException1() // 建構子 super(); public CmyException1(String msg) super(); System.out.println(msg); class CmyException2 extends Exception public CmyException2() // 建構子 super(); public CmyException2(String msg) super(); System.out.println(msg); // 建構子 // 建構子 37 10.4 例外的處理 執行結果 第一次執行 第二次執行 請輸入陣列大小 :9 樂透開獎球數為 9 存放樂透開獎的陣列實體產生完畢 請輸入陣列大小 :99 例外訊息 : 陣列太大例外訊息 : 陣列大小將被重設為 6 樂透開獎球數為 6 存放樂透開獎的陣列實體產生完畢 第三次執行第四次執行 請輸入陣列大小 :5 例外訊息 : 陣列太小例外訊息 : 陣列大小將被重設為 6 樂透開獎球數為 6 存放樂透開獎的陣列實體產生完畢 請輸入陣列大小 :r 樂透開獎球數為 0 Exception in thread "main" java.lang.numberformatexception: For input string: "r" at java.lang.numberformatexception.forinputstring(unknown Source) at java.lang.integer.parseint(unknown Source) at java.lang.integer.parseint(unknown Source) at myjava.ch10.ch10_05.main(ch10_05.java:17) 38

10.4 例外的處理 範例說明 : (1) 陣列宣告所引發的內建類別錯誤, 只有在陣列大小被宣告為負值時才適用, 但本題之題意為陣列大小必須控制在 6~48 之間, 故我們應該自訂例外類別, 以處理此類狀況 (2) 第 38~62 行是兩種自訂的例外類別, 它是用來處理樂透球數不屬於 6~48 的例外狀況 (3) 第 15~31 行是 try catch [finally ] 敘述, 其中, 我們認為第 17 行可能會引發例外 ( 轉型的內建例外 ), 以及第 18~21 行陣列大小 lottosize 可能符合自行定義的例外 故將之放在 try 區塊中, 但要引發自行定義的例外, 則必須透過 throw 丟出例外 ( 轉型例外屬於內建例外, 會自動丟出例外, 不須撰寫 throw 敘述 ) (4) 第一次執行時, 並沒有任何的例外產生, 所以不會有任何一個 catch 區塊被執行 換句話說,try 區塊執行完畢就執行 finally 區塊, 然後執行 try catch [finally ] 敘述之後的敘述 39 10.4 例外的處理 (5) 第二次執行時, 當執行到第 18 行, 由於球數不小於等於 48 球, 在第 19 行中將由 throw 丟出例外, 此時, 第 23~27 行的 catch 區塊補捉到此例外 ( 因為與丟出的例外型別之一相符 ), 因此會執行區塊內容, 然後才執行 finally 區塊, 最後執行 try catch [finally ] 敘述之後的敘述 (6) 第三次執行時, 當執行到第 20 行, 由於球數不大於等於 6 球, 在第 21 行中將由 throw 丟出例外, 此時, 第 23~27 行的 catch 區塊補捉到此例外 ( 因為與丟出的例外型別之一相符 ), 因此會執行區塊內容, 然後才執行 finally 區塊, 最後執行 try catch [finally ] 敘述之後的敘述 (7) 第四次執行時, 當執行到第 17 行, 就會發生轉型的例外 ( 字元不符合數字格式 ), 故該行會自動丟出一個內建例外 ( 不須透過 throw 敘述 ), 由於沒有任何 catch 區塊符合該例外的型別, 故系統只能採用內定的例外處理程序來處理, 也就是印出錯誤訊息, 然後結束程式 但請注意, 由於 finally 區塊一定會被執行, 故仍會印出 樂透開獎球數為 0 (8) 這個程式有一個缺點, 也就是當產生轉型的內建例外時, 我們並未處理, 故會導致程式的中斷, 我們將於範例 10-7 改善這個問題 40

10.4 例外的處理 (9) 第 23~27 行的 Multi-catch 區塊可以分解為兩個 catch 區塊如下, 分別處理 CmyException1 與 CmyException2 型態的例外物件, 在 JDK6 之前, 是不可以撰寫如上範例中的語法, 而必須撰寫如下列語法 : 23 24 25 26 27 : : : : : catch(cmyexception1 e) System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); lottosize=6; catch(cmyexception2 e) System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); lottosize=6; 41 10.4 例外的處理 小試身手 10 1 請將範例 10 5 的第 23~27 行, 如同範例說明第 (9) 點重新撰寫, 然後進行編譯與執行, 看看是否會獲得相同的執行結果? 小試身手 10 2 請將範例 10 5 的第 51 行改寫為 class CmyException2 extends CmyException1, 然後進行編譯, 看看會獲得什麼結果? 42

10.4 例外的處理 Multi-catch 的限制 在前面我們曾經提及 Multi-catch 不允許同一個 catch( ) 內的不同例外類別具有包含的效果 所謂包含也就是繼承之父子關係 如小試身手 10-2 中, 我們將 CmyException2 繼承了 CmyException1, 所以就不可以將之放在同一個 catch( ) 內 除了這個限制之外, 還有其他限制嗎? 答案是有的, 請見下列這個範例 ( 事實上, 它也與繼承的思考有關 ) 43 10.4 例外的處理 觀念範例 10-6 :Multi-catch 的限制 範例 10-6:ch10_06.java( 隨書光碟 myjava\ch10\ch10_06.java) 1 2 3 4 5 6 7 /* 檔名 :ch10_06.java 功能 : Multi-catch 的限制 */ package myjava.ch10; import java.lang.*; public class ch10_06 // 主類別 44

10.4 例外的處理 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void main(string args[]) int lottosize=99; try if(lottosize>48) throw new CmyException1(); else if(lottosize<6) throw new CmyException2(); catch(cmyexception1 CmyException2 e) e.showmessage(); lottosize=e.changesize(); 當使用 Multi-catch 時, 不允許使用例外物件來執行 method finally System.out.println(" 樂透開獎球數為 " + lottosize); int lottoary[] = new int[lottosize]; System.out.println(" 存放樂透開獎的陣列實體產生完畢 "); 45 10.4 例外的處理 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class CmyException1 extends Exception public CmyException1() // 建構子 super(); public void showmessage() System.out.println(" 例外訊息 : 陣列大小錯誤 "); public int changesize() System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); return 6; 46

10.4 例外的處理 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class CmyException2 extends Exception public CmyException2() // 建構子 super(); public void showmessage() System.out.println(" 例外訊息 : 陣列大小錯誤 "); public int changesize() System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); return 6; 47 10.4 例外的處理 執行結果 :( 編譯時產生錯誤 ) C:\myJava\ch10>javac ch10_06.java ch10_06.java:21: error: cannot find symbol e.showmessage(); ^ symbol: method showmessage() location: variable e of type Exception ch10_06.java:22: error: cannot find symbol lottosize=e.changesize(); ^ symbol: method changesize() location: variable e of type Exception 2 errors 48

10.4 例外的處理 範例說明 : (1) 這個範例是在使用 Multi-catch 時, 於 catch 區塊中要求例外物件執行某個 method, 例如第 21 22 行分別要求執行 showmessage() 與 changesize(), 或許您會看半天也看不出程式哪裡有問題, 因為不論是 CmyException1 還是 CmyException2 都提供了這兩個 methods (2) 如果我們將程式的第 19~23 行改寫如下 ( 不使用 Multicatch), 則可以通過編譯, 也可以順利執行 19 20 21 22 23 : : : : : : catch(cmyexception1 e) e.showmessage(); lottosize=e.changesize(); catch(cmyexception2 e) e.showmessage(); lottosize=e.changesize(); 49 10.4 例外的處理 (3) 有些人會認為這是 JDK7 的 bug, 不然怎麼會分開就可以使用 e 來呼叫 method, 但一合併就不能使用了呢? 尤其這個範例的 CmyException1 與 CmyException2 又沒有繼承的關係, 並沒有違反 JDK7 的規定啊 事實雖是如此, 但筆者並不認為這是 JDK7 的 bug( 詳見筆者的話 ), 總而言之, 除非在之後的 JDK7 做了更新, 否則應該避免在 Multi-catch 中使用例外物件來執行 method; 如果一定要呼叫 method, 請不要使用 Multi-catch, 而是將之分開來成為單獨的 catch 50

10.4 例外的處理 筆者的話 為何範例 10 6 會出現編譯錯誤呢? 原因在於 JDK7 不允許在 Multi catch 內使用例外物件來執行 method 雖然 JDK 說明文件中並未做此限制, 但筆者不認為這是 bug 在 JDK7 的說明文件中, 明確說明了在同一個 Multi catch 內的例外類別間, 不允許出現包含的效果, 也就是不允許有繼承之關係 那麼我們可以很單純地想想當初 JDK7 在看到例外物件想要執行某一個 method 時, 會有何種做法? 在範例 10 6 的第 21~22 行是已經寫死不能更改的程式碼, 也就是不論 e 屬於哪一個例外類別, 都必須執行這兩個 method 但這些例外類別又不允許出現繼承的關係, 因此, 要兩個完全沒有繼承關係的類別同時都有 showmessage() 與 changesize() 方法, 機率是非常低的 因此,JDK7 可以簡單的假設這是不可能發生的, 不幸地, 在範例 10 6 中, 我們卻讓它發生了 但這是非常不合乎物件導向原則的, 因為既然兩個類別擁有相同的方法署名, 就應該讓它們有繼承關係, 如此才能減少一些程式碼的設計 因此, 筆者並不認為這是 JDK7 的 bug, 因為 JDK7 只是從物件導向的精神出發來設計 關於範例 10 6 的問題, 頂多只能說是 JDK7 的說明文件未將 在 Multi catch 之內, 不能使用例外物件來呼叫 method 做一個明白的宣示而已 小試身手 10 3 請將範例 10 6 的第 19~23 行, 如同範例說明第 (2) 點重新撰寫, 然後進行編譯與執行, 看看會獲得什麼執行結果? 51 10.4 例外的處理 捕捉超過一個例外在範例 10-5 中, 我們只示範了捕捉一個例外的情形, 事實上, 在範例 10-5 中, 可能發生的例外還有很多, 如果要捕捉超過一個例外的狀況, 則可以撰寫多個 catch 區塊來對應 實用範例 10-7 : 多個例外處理 範例 10-7:ch10_07.java( 隨書光碟 myjava\ch10\ch10_07.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 /* 檔名 :ch10_07.java 功能 : 多個例外處理 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_07 // 主類別 public static void main(string args[]) Console console=system.console(); System.out.print(" 請輸入陣列大小 :"); int lottosize=0; 52

10.4 例外的處理 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 try lottosize=integer.parseint(console.readline()); if(lottosize>48) throw new CmyException1(" 例外訊息 : 陣列太大 "); else if(lottosize<6) throw new CmyException2(" 例外訊息 : 陣列太小 "); catch(numberformatexception e) 自行處理例外 System.out.println(" 球數不為數字, 將被設定為 6 球 "); lottosize=6; catch(cmyexception1 CmyException2 e) System.out.println(" 例外訊息 : 陣列大小將被重設為 6"); lottosize=6; finally System.out.println(" 樂透開獎球數為 " + lottosize); int lottoary[] = new int[lottosize]; System.out.println(" 存放樂透開獎的陣列實體產生完畢 "); 53 10.4 例外的處理 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class CmyException1 extends Exception public CmyException1() // 建構子 super(); public CmyException1(String msg) super(); System.out.println(msg); class CmyException2 extends Exception public CmyException2() // 建構子 super(); public CmyException2(String msg) super(); System.out.println(msg); // 建構子 // 建構子 54

10.4 例外的處理 執行結果 : 請輸入陣列大小 :r 球數不為數字, 將被設定為 6 球樂透開獎球數為 6 存放樂透開獎的陣列實體產生完畢 範例說明 : 由範例 10-5 的執行結果可以得知, 內建例外屬於 NumberFormatException 類別, 故我們加上第 23~27 行的 catch 區塊, 自行處理例外發生時的處理方式 所以在輸入 r 之後, 會執行我們所撰寫的例外處理而不會中斷程式 55 10.4 例外的處理 捕捉所有的例外對於撰寫大型程式而言, 不論如何小心都有可能遺 漏了某些例外未加以處理, 而當這些例外發生時, 就會按照內定的處理程序中斷程式為了避免發生這種狀況, 我們可以多加一段敘述如下, 以捕捉所有種類的例外 : try // 可能發生例外的程式碼 catch( 例外類別名稱 1 例外類別名稱 2 物件名稱 ) // 例外處理程序 一定要寫在最後一個 catch(exception 物件名稱 ) // 當遺漏的例外發生時, 會執行的程式碼 finally // 一定會執行的程式碼 56

10.4 例外的處理 語法說明 : 例外發生時 ( 例外物件被丟出時 ), 會由上而下選擇一個符合例外物件之例外類別型態的 catch 區塊來執行, 所謂符合型態, 代表的是類別本身與其上層類別 由於所有的例外類別都繼承自 Exception 例外類別, 故若在其上並未捕捉到該例外物件, 則會在最後一個 catch 被捕捉到 在此處, 請特別注意, 一定要將 Exception 類別對應的 catch 區塊寫在最後一個, 否則會出現編譯錯誤 Coding 注意事項 例外類別之間可能具有繼承關係, 此時若使用同一個 try catch 敘述來捕捉例外, 則必須將子類別的 catch 區塊寫在父類別的 catch 區塊之前, 否則當子類別的例外物件產生時, 會先遇到父類別對應的 catch 區塊而被捕捉, 這樣一來子類別對應的 catch 區塊就不會捕捉到任何例外 不過不用擔心, 因為如此做會在編譯時產生錯誤 由以上的說明, 相信您也知道為何 JDK7 在推出 Multi catch 機制時, 為何要規定其 catch( ) 內的例外類別不能有繼承關係了吧 57 10.4 例外的處理 實用範例 10-8 : 捕捉所有的例外, 並加以解決 範例 10-8:ch10_08.java( 隨書光碟 myjava\ch10\ch10_08.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* 檔名 :ch10_08.java 功能 : 捕捉所有的例外 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_08 // 主類別 public static void main(string args[]) Console console=system.console(); int lottosize=0; int lottoary[]; 58

10.4 例外的處理 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 while(true) try System.out.print(" 請輸入陣列大小 :"); lottosize=integer.parseint(console.readline()); lottoary = new int[lottosize]; break; // 跳出 while 迴圈 catch(numberformatexception e) System.out.println(" 陣列大小請輸入數字 "); catch(exception e) System.out.println(" 有非未考慮到的例外發生了 "); finally System.out.println(" 程式正確執行中..."); 除非前三行都沒有發生例外, 才會執行到此行 59 10.4 例外的處理 執行結果 : 範例說明 : 請輸入陣列大小 :r 陣列大小請輸入數字請輸入陣列大小 :-2 有非未考慮到的例外發生了請輸入陣列大小 :3 程式正確執行中... (1) 第 28~31 行 catch 區塊的例外類別為 Exception, 故任何種類的例外都將符合, 但必須是前面的 catch 區塊未捕捉到例外物件時, 才會由該 catch 區塊來捕捉 因此在執行結果中, 輸入 r 而導致的轉型例外, 是由第 24~27 行的 catch 區塊來處理 而第二次輸入的 -2, 導致的是負值陣列大小的例外, 由於在前面未被捕捉到, 故由第 28~31 行的 catch 區塊來處理 (2) 本範例藉由一個無窮迴圈包裝起 try catch 敘述, 這使得除非 try 區塊中完全在沒有例外發生, 才會執行 break 跳離迴圈, 若有例外產生, 則會直接跳到例外處理的某個 catch 區塊, 而不會執行 break 故本範例會直到使用者輸入完全正確時, 才會執行後續的程式 這個做法, 可提供您撰寫程式時做為參考之用 60

10.4 例外的處理 捕捉所有的例外範例說明 : (1) 第 30~33 行 catch 區塊的例外類別為 Exception, 故任何種類的例外都將符合, 但必須是前面的 catch 區塊未捕捉到例外物件時, 才會由該 catch 區塊來捕捉 因此在執行結果中, 輸入 r 而導致的轉型例外, 是由第 26~29 行的 catch 區塊來處理 而第二次輸入的 -2, 導致的是負值陣列大小的例外, 由於在前面未被捕捉到, 故由第 30~33 行的 catch 區塊來處理 (2) 本範例藉由一個無窮迴圈包裝起 try catch 敘述, 這使得除非 try 區塊中完全在沒有例外發生, 才會執行 break 跳離迴圈, 若有例外產生, 則會直接跳到例外處理的某個 catch 區塊, 而不會執行 break 故本範例會直到使用者輸入正確時, 才會進行其他的程式 61 10.5 throws 由方法拋出例外 我們現在知道可能會產生例外的敘述應該撰寫於 try catch 敘述的 try 區塊內, 才能被 catch 區塊捕捉與處理, 而如果是執行某個方法 (method) 而導致例外發生 ( 方法內的任何一個敘述產生例外 ), 則可以於該方法定義時註明拋出的例外, 如此當例外產生時, 它將會把例外拋回給呼叫端, 所以呼叫端應該把呼叫方法的敘述撰寫於 try 區塊中 62

10.5 throws 由方法拋出例外 如果要使用由 方法拋出例外, 則必須在方法的宣告列後面加上 throws 例外類別 以作為註明, 格式如下 : [ 封裝 ] [ 修飾字 ] 回傳值型態方法名稱 ( 參數列 ) throws 例外類別 1, 例外類別 2, // 方法定義內容 語法說明 : (1) 由於方法內的敘述引發的例外可能不只一種, 如果要丟出多種例外物件, 可以使用, 加以區隔 (2) 使用方法丟出例外, 則方法會在執行到出現例外時, 將例外丟回給呼叫方法的敘述, 因此, 通常是呼叫端使用 try catch 敘述來捕捉與處理 其示意圖如下 : 63 10.5 throws 由方法拋出例外 圖 10-2 由方法拋出例外 (3) 對於繼承自 RuntimeException 類別的例外, 若要由方法拋出例外, 則可以不必註明例外類別 64

10.5 throws 由方法拋出例外 觀念範例 10-9 : 捕捉並處理由方法拋出的例外 範例 10-9:ch10_09.java( 隨書光碟 myjava\ch10\ch10_09.java) 1 2 3 4 5 6 7 8 9 10 11 12 /* 檔名 :ch10_09.java 功能 : 由方法丟出例外 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_09 // 主類別 public static void main(string args[]) CmyClass obj = new CmyClass(); 65 10.5 throws 由方法拋出例外 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 try 可能產生例外的呼叫敘述應該包含在 try 區塊中 obj.setvalue(); // 此方法可能產生例外 obj.printvalue(); // 此方法可能產生例外 catch(numberformatexception e) System.out.println(" 例外產生 : 請輸入數字 "); catch(arithmeticexception e) System.out.println(" 例外產生 : 分母為 0"); catch(exception e) System.out.println(" 有非未考慮到的例外發生了 "); finally 66

10.5 throws 由方法拋出例外 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class CmyClass private int numerator,denominator; public Console console=system.console(); public void setvalue() throws NumberFormatException System.out.print(" 輸入分子 :"); numerator=integer.parseint(console.readline()); System.out.print(" 輸入分母 :"); denominator=integer.parseint(console.readline()); System.out.println(" 資料輸入完畢 "); public void printvalue() throws ArithmeticException System.out.println(" 除法結果為 " + numerator/denominator); System.out.println(" 除法執行完畢 "); 67 10.5 throws 由方法拋出例外 執行結果 : 第一次執行結果 輸入分子 :5 輸入分母 :t 例外產生 : 請輸入數字 第二次執行結果 輸入分子 :5 輸入分母 :0 資料輸入完畢例外產生 : 分母為 0 範例說明 : (1) 我們將可能產生例外的方法呼叫放在 try 區塊中 ( 第 15~16 行 ), 如此當例外發生時, 就可以由 catch 區塊捕捉並處理 (2) 第 41~54 行的兩個方法都可能發生例外, 但例外種類有所不同, 因此 throws 後面所接之例外類別不同 由執行結果可以看出, 當例外發生後, 方法會被中斷直接返回呼叫處並丟出例外給呼叫敘述 (3) 本範例的 NumberFormatException 與 ArithmeticException 例外類別都繼承自 RuntimeException 類別, 因此都可以省略不寫 68

10.5 throws 由方法拋出例外 RuntimeException 例外在上一個範例中, 我們曾經說明當例外繼承自 RuntimeException 類別, 則在方法宣告時, 可以省略 ( 不必明確指出 ) 丟出該例外, 而當例外真的發生時, 仍會丟出例外給呼叫端 這是因為 RuntimeException 例外代表的是 JVM 在執行程式時, 由執行系統自動引發的執行時期例外 由於是否省略 throws 都一定會自動丟出此類例外, 因此, 我們有可能並未撰寫相關的例外處理程式, 此時若發生了例外, 且程式中沒有任何 catch 區塊捕捉到例外, 則會由執行時期預設的例外處理器來捕捉, 因此, 即使我們未撰寫相關的例外處理程式, 程式仍可以編譯無誤 69 10.5 throws 由方法拋出例外 您可以將範例 10-9 的第一個與第三個 catch 區塊刪除, 則第一次執行結果就會是預設的例外處理器的處理結果, 當然它在捕捉並印出錯誤訊息後, 會自動中斷程式 因此, 在範例中, 我們使用第三個捕捉全部例外的 ( 遺漏的 )catch 區塊來解決程式被中斷的狀況 除了使用 捕捉全部例外 的方式避免程式被預設的例外處理器中斷, 我們也建議不要將自訂的例外類別繼承 RuntimeException 類別, 如此一來, 如果我們在程式中忽略處理該例外, 編譯器就能於編譯時期提醒我們該注意哪些例外尚未被處理 70

10.5 throws 由方法拋出例外 註明 throws 由於 throws 可以註明多個例外類別, 因此, 我們建議當方法中有可能發生例外 ( 不論是自動發生或使用 throw 敘述丟出 ), 就在方法後面使用 throws 註明例外類別, 即使該類別繼承自 RuntimeException 類別也應該如此做, 並且在撰寫類別的說明文件時, 也應該將例外類別列出 事實上,JDK 文件及 Java 類別庫的原始碼都是採用如上作法, 這樣做的好處在於, 提示使用類別的程式設計師, 應該將呼叫方法的敘述放置於 try 區塊中, 並且使用 catch 區塊來捕捉處理, 以避免程式於執行時被中斷 71 10.6 例外的處理與轉交 當類別使用越趨複雜時, 可能某個方法會丟出的例外在另一個方法中並不想加以處理, 而是將該例外再轉交回給呼叫它的敘述此時, 我們可以在中繼的方法中也使用 throws 加註該例外的類別 請見下面的範例 : 觀念範例 10-10 : 由方法轉寄例外 範例 10-10:ch10_10.java( 隨書光碟 myjava\ch10\ch10_10.java) 1 2 3 4 5 6 7 8 9 10 11 /* 檔名 :ch10_10.java 功能 : 由方法轉寄例外 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_10 // 主類別 public static void main(string args[]) CmyClass2 obj2 = new CmyClass2(); 72

10.6 例外的處理與轉交 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 try obj2.run(); // 此方法可能產生例外 catch(numberformatexception e) System.out.println(" 例外產生 : 請輸入數字 "); catch(arithmeticexception e) System.out.println(" 例外產生 : 分母為 0"); catch(exception e) System.out.println(" 有非未考慮到的例外發生了 "); finally 73 35 class CmyClass1 10.6 例外的處理與轉交 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 private int numerator,denominator; public Console console=system.console(); public void setvalue() throws NumberFormatException System.out.print(" 輸入分子 :"); numerator=integer.parseint(console.readline()); System.out.print(" 輸入分母 :"); denominator=integer.parseint(console.readline()); System.out.println(" 資料輸入完畢 "); 可能產生例外 public void printvalue() throws ArithmeticException System.out.println(" 除法結果為 " + numerator/denominator); System.out.println(" 除法執行完畢 "); 可能產生例外 run() 內產生的例外未處理, 再轉交例外 class CmyClass2 回呼叫 run() 的程式碼 ( 第 15 行 ) public CmyClass1 obj1 = new CmyClass1(); public void run() throws NumberFormatException,ArithmeticException obj1.setvalue(); // 此方法可能產生例外 obj1.printvalue(); // 此方法可能產生例外. 呼叫後可能產生例外但卻不處理它 74

10.6 例外的處理與轉交 執行結果 :( 同範例 10-9) 範例說明 : (1) 產生例外的程式碼, 是在 CmyClass1 的方法中, 而 CmyClass2 的 run 方法內的敘述呼叫它卻不處理例外, 此時, 我們可以在 run 方法宣告處也加上例外類別, 如此就可以由呼叫 run 方法的敘述 ( 第 15 行 ) 去處理例外 (2) 請特別注意, 本範例第 59 行就算未註明例外類別, 程式也不會出錯, 並且執行結果也相同 這是因為兩個例外類別都繼承自 RuntimeException 類別, 如果非繼承自該類別, 則不能省略例外類別的註明, 否則將無法通過編譯, 這也就是為何我們在範例 B-1( 附錄 B.8) 的 main 方法中使用 throws IOException 註明例外類別的原因 75 10.6 例外的處理與轉交 readline 方法與 IOException 例外 在範例 B-1 中, 我們並未解釋相關的程式碼, 現在解釋如下 ( 請讀者對照範例 B-1 的程式碼 ): 在範例 B-1 中,main 方法註明了 throws IOException, 這是因為 readline 方法可能會發生例外, 而我們在 main 方法中並未處理它, 故將之交由呼叫 main 的敘述來處理, 否則無法通過編譯 ( 因為 IOException 並非繼承自 RuntimeException 類別 ) 而又由於 main 方法是由 JVM 呼叫, 因此事實上是將例外丟給 JVM 處理 76

10.6 例外的處理與轉交 事實上, 我們也可以自行在 main 之中處理例外, 如此一來就不需要在 main 後面註明 throws IOException, 也能通過編譯 ( 因為我們已經處理了例外, 而非將它轉交 ) 請見下列範例 : 觀念範例 10-11 : 改寫範例 B-1, 在 main 中處理 IOException 例外 範例 10-11:ch10_11.java( 隨書光碟 myjava\ch10\ch10_11.java) 1 2 3 4 5 6 /* 檔名 :ch10_11.java 功能 : 在 main 中處理 IOException 例外 */ package myjava.ch10; import java.lang.*; import java.io.*; 77 10.6 例外的處理與轉交 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ch10_11 // 主類別 public static void main(string args[]) try BufferedReader buf; String str1,str2; // 不丟出例外 System.out.print(" 請輸入第一個字串 :"); buf = new BufferedReader(new InputStreamReader(System.in)); str1 = buf.readline(); System.out.print(" 請輸入第二個字串 :"); str2 = buf.readline(); System.out.println(" 您所輸入的字串如下 :"); System.out.println(str1); System.out.println(str2); catch(ioexception e) // 沒有作任何事, 但可通過編譯 78

10.6 例外的處理與轉交 執行結果 :( 同範例 B-1) 範例說明 : 我們將所有的程式碼包裝在 try 區塊中, 並且捕捉 IOException 例外, 雖然我們在捕捉之後並未作任何事, 但這樣子就可以通過編譯 事實上,readLine 方法可能發生的例外情形大多是無法讀取到檔案, 但由於我們是由鍵盤輸入, 因此不會有例外發生 如果是讀取其他自己設定的檔案而檔案不存在, 則我們就應該在第 27 行進行處理 ( 例如列印錯誤訊息 ) 79 10.7 巢狀例外 例外處理也可以和決策一樣, 構成多層巢狀, 也就是在 try catch [finally] 的任何一個區塊中, 另外包含一層 try catch [finally] 敘述 雖然在 try 區塊 catch 區塊及 finally 區塊都可以包含另一個例外處理敘述, 但一般較常將內層的例外處理敘述放在 try 區塊中 同時, 在撰寫巢狀例外時, 要小心程式的順序, 以免某些程式碼因例外發生而被略過, 請見下面範例 80

10.7 巢狀例外 觀念範例 10-12 : 巢狀例外 範例 10-12:ch10_12.java( 隨書光碟 myjava\ch10\ch10_12.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 /* 檔名 :ch10_12.java 功能 : 巢狀例外 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_12 // 主類別 public static void main(string args[]) Console console=system.console(); int ary[]=new int[]0,5,10,15,20,25,30,35,40,45; int x=0,num=0; 81 10.7 巢狀例外 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 try // 外層 try 區塊 System.out.print(" 請問要讀取陣列第幾個元素 :"); x=integer.parseint(console.readline()); try 可能產生例外 可能產生例外 num=ary[x]; System.out.println("ary[" + x + "]=" + num); catch(arrayindexoutofboundsexception e) System.out.println(" 例外發生 : 存取超過陣列大小範圍!"); catch(numberformatexception e) // 外層 catch 區塊 System.out.println(" 例外發生 : 索引應為數值!"); System.out.println("... 程式即將結束..."); 82

10.7 巢狀例外 執行結果 : 第一次執行結果 請問要讀取陣列第幾個元素 :r 例外發生 : 索引應為數值!... 程式即將結束... 第二次執行結果 請問要讀取陣列第幾個元素 :15 例外發生 : 存取超過陣列大小範圍!... 程式即將結束... 範例說明 : (1) 第 14~32 行是外層的 try catch 敘述 而第 19~27 行 ( 位於外層 try 區塊內 ) 是內層的 try catch 敘述 (2) 本範例如果在正常執行狀況下, 實際有用的程式碼在於第 16,17,21,22 行 而第 17 行可能發生轉型例外, 第 21 行可能發生陣列索引例外 故我們將它分為兩層看待 首先將第 16~17 行放在外層的 try 區塊中, 若發生轉型例外, 就會在直接跳到外層的 catch 區塊處理, 此時, 內層的 try catch 敘述完全不會被執行 ( 第 21,22 行也不會被執行 ) 83 10.7 巢狀例外 範例說明 : (3) 如果程式能夠執行到內層的 try catch 敘述, 代表並未發生轉型例外, 此時, 我們將例外處理集中在陣列索引的例外 (4) 這個範例當然也可以用單一層的 try catch 敘述來改寫, 只要將第 16,17,21,22 行放在同一個 try 區塊內即可, 因為這兩種例外是不同種類的例外, 互相並不影響 (5) 本範例也可以將第 16,17 行放在內層的 try 區塊, 若產生轉型例外時, 由於在內層的 catch 區塊未抓取到例外 ( 因為例外型態不符 ), 此時會保留給外層的 catch 區塊繼續抓取 84

10.8 重新丟出例外 有些時候為了特殊的應用, 我們可能會希望將捕捉到的例外再次丟出此時, 我們可以利用 throw 例外物件 重新丟出例外 在上一個範例的第 5 點說明中, 我們提及若內層的 catch 區塊未抓取到例外, 則例外還會交由外層的 catch 區塊來抓取 但如果內層的 catch 區塊已經抓取到例外, 則外層例外同型態的 catch 區塊就抓取不到了, 因為該例外已經被處理完畢 如果要讓內外層同型態的 catch 區塊都能抓取到例外, 則必須在內層重新丟出例外, 請見下列範例 85 10.8 重新丟出例外 觀念範例 10-13 : 重新丟出例外 範例 10-13:ch10_13.java( 隨書光碟 myjava\ch10\ch10_13.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 /* 檔名 :ch10_13.java 功能 : 重新丟出例外 */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_13 // 主類別 public static void main(string args[]) Console console=system.console(); int ary[]=new int[]0,5,10,15,20,25,30,35,40,45; int x=0,num=0; 86

10.8 重新丟出例外 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 try // 外層 try 區塊 try System.out.print(" 請問要讀取陣列第幾個元素 :"); x=integer.parseint(console.readline()); num=ary[x]; System.out.println("ary[" + x + "]=" + num); catch(arrayindexoutofboundsexception e) System.out.println(" 例外發生 : 存取超過陣列大小範圍!"); catch(numberformatexception e) System.out.println(" 捕捉到轉型例外, 準備交由外層處理 "); throw e; 重新丟出例外, 第 34 行才能抓取到 NumberFormatException 類型的例外 catch(numberformatexception e) // 外層 catch 區塊 System.out.println(" 例外發生 : 索引應為數值!"); System.out.println("... 程式即將結束..."); 87 10.8 重新丟出例外 執行結果 : 範例說明 : 請問要讀取陣列第幾個元素 :r 捕捉到轉型例外, 準備交由外層處理例外發生 : 索引應為數值!... 程式即將結束... 雖然第 28 行抓取到轉型例外, 但由於在第 30 行重新丟出例外, 因此, 第 34 行的外層 catch 仍可以抓取到同一類型的例外 我們不能將第 34 行的外層 catch 區塊移到內層, 因為同一層級不允許出現類型相同的 catch 區塊 ( 無法通過編譯 ) 小試身手 10 4 請將範例 10 13 的第 30 行改為註解, 然後重新編譯與執行, 看看有何不同的結果? 88

10.8 重新丟出例外 最後重新丟出例外細分類別 (Precise rethrow with a final Exception) JDK7 提供了一個關於最後丟出例外的類別判定功能, 稱之為 Precise rethrow with a final Exception 假設我們有一個 method, 裡面的敘述包含了 try-catch 敘述, 而通常我們會在最後一個 catch 中, 以 catch(exception e) 為一些尚未捕捉的例外做一個保險的機制 假設這個 method 是要被其他程式碼所呼叫, 而我們又未在最後一個 catch(exception e) 中將其他類的例外進行處理, 而是打算交由呼叫端來處理, 則此時應該在方法宣告列右方加上 throws Exception 程式碼如下: 89 10.8 重新丟出例外 public void test() throws Exception try // 此方法主要內容, 可能會發生例外類別 1,2 或其他種類的例外 catch( 例外類別 1 e) // 此方法自己處理的例外 catch( 例外類別 2 e) // 此方法自己處理的例外 catch(exception e) throw e; // 重新丟出例外給呼叫端 90

10.8 重新丟出例外 對於呼叫 test() 方法的程式碼而言, 它必須處理 test() 丟出的例外, 通常程式碼如下 : public void caller() try Obj1.test(); // 呼叫這個方法可能會產生一些非例外類別 1,2 的例外 catch(exception e) // 應該在這邊處理 test() 回丟的例外 問題是對於呼叫者而言, 它想要呼叫一個 test() 方法, 但 test() 方法的規格書中只跟它講會產生例外, 卻不講會產生哪一種例外, 它也很難針對各種例外進行處理, 所以只好使用上述語法使得程式能夠通過編譯 說實話, 這樣的 test() 方法是不合格的 91 10.8 重新丟出例外 一般的標準類別庫中, 一定會告知使用者應該要處理哪一類型的例外, 如此使用者才能針對該例外進行處理 例如 java.io.bufferedinputstream 類別的 read() 方法在規格書中的格式為 public int read() throws IOException 它很負責的告訴使用者, 這個方法可能會產生 IOException 例外, 而非以 throws Exception 告訴使用者會產生不知哪一類的例外 92

10.8 重新丟出例外 設計 test() 的設計師如果負責任, 他應該把可能且未處理的例外明確標註, 但總還是會有一些是設計師想不到的例外, 所以設計師會將程式碼如下撰寫 : public void test() throws 例外類別 3, 例外類別 4,Exception 同之前的 test() 內容 上述程式碼代表呼叫 test() 可能會產生例外類別 3 或例外類別 4 等種類的例外需要呼叫者來處理, 還會產生其他種類的例外需要呼叫者來處理 其中, 例外類別 3 或例外類別 4 是設計者想得到的可能例外 當設計者如此宣告 test() 後, 呼叫者則應該將程式改寫如下 : 93 10.8 重新丟出例外 public void caller() try Obj1.test(); // 呼叫這個方法可能會產生一些非例外類別 1,2 的例外 catch( 例外類別 3 e) // 若 test() 回丟的例外屬於例外類別 3, 應該在此處理 catch( 例外類別 4 e) // 若 test() 回丟的例外屬於例外類別 4, 應該在此處理 catch(exception e) // 若 test() 回丟的例外若非例外類別 3 或 4, 應該在此處理 94

10.8 重新丟出例外 以上是 JDK6 之前的常見程式碼, 而 JDK7 做了什麼改變呢? 簡單來說,JDK7 允許設計 test() 的設計師只標註想得到的例外類別, 至於想不到的例外類別就不用標註了因此程式可以改寫如下 而之所以可以如此做, 是因為 JDK7 紀錄了最後由 test() 的 throw e 中 e 的真實型別, 因此稱之為 Precise rethrow with a final Exception public void test() throws 例外類別 3, 例外類別 4 //JDK7 允許如此做 同之前的 test() 內容 95 10.8 重新丟出例外 以下我們透過一個範例來示範何謂 Precise rethrow with a final Exception, 我們將使用 JDK6 與 JDK7 分別來編譯, 讓讀者理解當中的差別 96

10.8 重新丟出例外 觀念範例 10-14 : 最後重丟例外 範例 10-14:ch10_14.java( 隨書光碟 myjava\ch10\ch10_14.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /* 檔名 :ch10_14.java 功能 :Precise rethrow with a final Exception */ package myjava.ch10; import java.lang.*; import java.io.console; public class ch10_14 // 主類別 public static void main(string args[]) Console console=system.console(); System.out.print(" 請輸入陣列大小 :"); int lottosize=0; CmyClass1 obj1=new CmyClass1(); try lottosize=obj1.check(console.readline()); // 可能會產生例外 97 10.8 重新丟出例外 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 catch(cmyexception1 e) System.out.println(" 例外訊息 : 球數過大, 重設為 6"); lottosize=6; catch(cmyexception2 e) System.out.println(" 例外訊息 : 球數過小, 重設為 6"); lottosize=6; catch(numberformatexception e) System.out.println(" 球數不為數字, 將被設定為 6 球 "); lottosize=6; catch(exception e) System.out.println(" 例外訊息 : 產生其他例外未處理 "); finally System.out.println(" 樂透開獎球數為 " + lottosize); int lottoary[] = new int[lottosize]; System.out.println(" 存放樂透開獎的陣列實體產生完畢 "); 98

10.8 重新丟出例外 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class CmyClass1 private int lottosize; public void check(string inputstr) throws CmyException1,CmyException2,NumberFormatException try lottosize=integer.parseint(inputstr); if(lottosize>48) throw new CmyException1(" 例外訊息 : 陣列太大 "); else if(lottosize<6) throw new CmyException2(" 例外訊息 : 陣列太小 "); return lottosize; catch(final Exception e) throw e; 加不加 final, 都會被編譯器認為是 final JDK6 後面要加上,Exception 99 10.8 重新丟出例外 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 class CmyException1 extends Exception public CmyException1() super(); // 建構子 public CmyException1(String msg) // 建構子 super(); System.out.println(msg); class CmyException2 extends Exception public CmyException2() super(); // 建構子 public CmyException2(String msg) // 建構子 super(); System.out.println(msg); 100

10.8 重新丟出例外 執行結果 : JDK6 的編譯結果 ( 編譯不通過 ) C:\myJava\ch10>javac ch10_14.java ch10_14.java:67: unreported exception java.lang.exception; must be caught or declared to be thrown throw e; ^ 1 error JDK7 編譯結果無誤, 兩次的執行結果如下第一次執行第二次執行 請輸入陣列大小 :5 例外訊息 : 陣列太小例外訊息 : 球數過小, 重設為 6 樂透開獎球數為 6 存放樂透開獎的陣列實體產生完畢 請輸入陣列大小 :r 球數不為數字, 將被設定為 6 球樂透開獎球數為 6 存放樂透開獎的陣列實體產生完畢 101 10.8 重新丟出例外 範例說明 : (1) 第 18 行呼叫 check(), 經查第 52 行的規格可以發現, 呼叫 check() 可能會產生例外, 例外有可能是 CmyException1, CmyException2, NumberFormatException 等三種, 也可能是未知的 Exception 之例外子類別 所以在第 20~38 行補捉這些例外來處理 (2) 假設我們是設計 CmyClass1 的設計師, 當我們在設計 check() 時, 很清楚知道應該將過大或過小的球數回傳一個例外, 但還有非數值的情況以及其他情況, 為了保險起見, 我們在最後 ( 第 63~66 行 ) 將其他類型的例外直接回傳給呼叫者 由於我們知道所謂其他類型包含了非數值的情況, 因此可以預期重新丟出的例外可能包含 NumberFormatException 種類, 故在第 52 行也列出此類例外, 如此呼叫者就知道應該要處理這類例外了 102

10.8 重新丟出例外 事實上, 最完整的列法應該是 throws CmyException1, CmyException2, NumberFormatException, Exception, 但 JDK7 允許我們省略, Exception 而 JDK6 不允許省略, Exception, 否則就不可以出現第 63~66 行 (3) 第 63 行的 final 可以省略, 即使省略不寫, 編譯器也會自動幫我們加上 final 然後才進行編譯 延伸學習 :try with resources JDK7 針對 try catch 一共有三個大改進, 其一是 multi catch, 其二是 Precise rethrow with a final Exception, 另外還有一個是關於資源是否可用的測試, 我們留到檔案處理一章再做說明 103 10.9 本章回顧 例外狀況是無法由編譯器檢查出來的錯誤, 它是一種幾乎無法避免的程式執行期錯誤 程式於執行時發生無法執行而中斷的狀況可以分為例外與錯誤 其中錯誤 (Error) 指的是非常嚴重而無法處理的狀況, 例如硬碟故障 而例外 (Exception) 則屬於較輕微可被處理的狀況, 例如欲開啟的檔案不存在, 陣列索引為負值等等 本章所介紹的執行期錯誤屬於例外狀況 (Exception) 104

10.9 本章回顧 (1)Java 的每個例外都是一個物件, 並且當例外產生時, 代表它產生某個例外類別的物件, 我們可以適當地處理這個物件, 以達到例外處理的目的 (2) 例外類別包含兩大類, 分別是 (1)Java 內建的例外類別及 (2) 自定例外類別 (3) 繼承自 java.lang.exception 的類別, 代表的是不太嚴重的錯誤, 我們將之稱為例外, 這些類別則稱為例外類別 例如 java.lang.arithmeticexception (4) 自定的例外類別至少必須繼承 Throwable 類別, 才能作為例外類別, 而一般常見的做法則是將自定的例外類別繼承自 Exception 類別 (5) 例外的引發可以分為兩大類, 一類是由系統自動引發, 另一類則是在程式中透過 throw 敘述自行引發 105 10.9 本章回顧 (6)Java 處理例外狀況一共分為四項動作, 分別是 try( 測試 ) throw( 丟出 ) catch( 抓取並處理 ) 與 finally (7)try catch [finally ] 敘述是例外處理敘述, finally 部分不一定存在, 而它的功用則是 不論例外是否發生, 都一定會執行的敘述區塊 (8) 當 try 區塊丟出例外時, 將由 catch 區塊進行捕捉與處理, 而眾多 catch 區塊只有第一個捕捉到例外的區塊會被執行 其餘則不會被執行 (9) 將 catch(exception 物件名稱 ) 寫在最後一個 catch 區塊, 可以捕捉前面遺漏的所有例外 (10) 當執行某個方法而可能產生例外時, 我們可以在宣告方法時, 後面加上 throws 例外類別, 代表此方法執行時可能發生的例外 並且 throws 可以註明多個例外類別 而若例外是繼承自 RuntimeException 類別, 則可以省略 106

10.9 本章回顧 (11) 在 try catch [finally] 的任何一個區塊中, 另外包含一層 try catch [finally] 敘述就形成巢狀例外 但一般較常將內層的例外處理敘述放在 try 區塊中 (12)JDK7 針對 try-catch 提供了一些語法的改進, 包含可將多個相同 catch 內容但條件不同者合併在一起, 稱之為 multi-catch 並且在將例外丟回呼叫者時, 若最後一個為 catch(final Exception e) throw e;, 也不必將 Exception 列於方法宣告列的 throws 項目, 設計者只要列上已知的例外類別即可, 此功能稱為 Precise rethrow with a final Exception 107 本章結束 Q&A 討論時間 108