第 6章 Java 卡例外和例外處理
一個例外就是在一個程式的執行過程中中斷指令正常流程的事件。在 Java 語言中,例外是很重要
的,因為它們提供了一種處理常式錯誤的極好方法。
Java 卡平台支援全部關於例外的 Java 語言編程結構。Java卡 applet 能夠利用關鍵字 throw、try、
catch,或 finally,並且它們像在 Java平台一樣地運行。
當檢測到內部運行時的問題時, JCRE 或 Java 卡虛擬機器就拋出例外,或者由 applets 通過程式拋出。
儘管 Java 卡平台具有對 Java-風格例外的全部支援,但是在用法上有所不同,這是由於智慧卡的有限
制的環境所造成的。這一章介紹 Java 卡平台中的一些例外,並討論 applet 如何拋出和處理例外。
6.1. java.lang 包中的例外
一般來說,Java 卡平台並不支援在 Java 技術核心包中可見的全部例外類型,因為其中有許多在智
能卡環境中並不能用。例如,在 Java 卡平台中不支援線程,並且因此和線程有關的例外就都不支援。
然而,的確 Java 卡的 java.lang 包支援該包的 Java版本中的某些例外類。在所有支援的例外類中,
僅提供從根類 Object 中繼承的 equals 方法和一個無參數的構造方法。表 6.1 列出了在java 卡平台上的
java.lang 包中的全部例外類。
類Throwable定義了Java卡平台中所有例外類的共同祖先。這個類還保證java卡例外和它們的Java
平台的對應者具有相同的語意。例如,applets只能拋出和捕獲從 throwable 類繁衍下來的對象。
類 Exception是從 Throwable 類擴充而來的。正如在 Java 平台那樣,它是 Java 卡平台中所有可檢
測的例外的根類。類 RuntimeException 是從 Exception 類衍生來的,它是 Java 卡平台中所有非檢測例
外的根類。檢測與非檢測例外的概念已在 java語言規範中予以定義。我們在下一節中也給出它們的定
義。
表 6.1 中的其它類都是非檢測例外。java.lang 包中的例外類提供了關於 Java 例外架構的基礎語言
支援。當因 Java 語言違例而發生錯誤時,由 Java卡虛擬機器拋出這些例外。
6.2.Java 卡例外
Java 卡平台提供了關於檢測例外與非檢測例外的類繼承層次, 6.1所示。
待檢測的例外(Checked exception)是 Exception類的子類並且必須要麼在 throwing 方法中捕獲,
或者在方法頭的 throws 子句中聲明。這個要求是 Java 編譯器強制的。所有的 Java 卡待檢測例外都
是從類 CardException 擴充而來的,而後者是從類 Exception 派生的。
由於兩個原因,所有待檢測的例外最終必須由 applet 捕獲。第一,待檢測的例外表示 applet 中的
一個編程錯誤,因此必須由該 applet 糾正。第二,待檢測的例外是對一個方法的介面的重要部分。這
就是為什麼 applet API 方法要在 throws 子句中指出待檢查的例外(留給 applet 去處理)。所以如果
applet 沒有捕獲待檢測的例外,Java編譯器就發出一個出錯指示。
非檢查例外(Unchecked exception)(常稱為運行時例外)是類 RuntimeException 的子類,並且既
不需要在程式中捕獲,也不用在 theows 子句中聲明。非檢測例外通常表示不可預料的運行時問題、編
程錯誤,或錯誤的 APDU處理狀態。這樣的例外是由 JCRE的最外層捕獲的。Java卡平台中的所有非
檢測例外都應從類 CardRuntimeException 擴充而來,後者是從類 RuntimeException 擴充來的。
我們為什麼需要類CardException和CardRuntimeException呢?因為它們能夠使一種節省資源的機
製成為可能,即一個例外對象能被重用多次,如 6.2.1 和 6.2.2 節所述。
6.2.1.Java 卡例外原因碼
Java 例外類提供指示具體錯誤的“訊息”串。在 Java 卡平台中不支援 String 類,所以在例外中不
能提供訊息串。作為把一些額外的資訊附加到例外上的另一種方法,Java 卡例外類採用一種數字型的
原因碼。原因碼被用來描述與該例外拋出有關的任選細節。原因碼的類型為 short。
原因碼被定義為類 CardException 和 CardRuntimeException中的一個域,並因此由它們的子類所繼
承。另外,這兩個類都定義了兩個 public 的 accessor方法(getReason和 setReason),以索取與設定原
因碼。
6.2.2.在 Java卡平台中拋出例外
為了拋出一個 Java 系統的例外,applet 要建立一個例外類的一個執行個體,代碼編寫如下:
throw new MyException(“a specific error message”);
當然,每當在 Java 卡平台中拋出一個例外,你就可能要建立一個新的例外對象。不過,儲存空間
的經濟利用總是智慧卡中的一個值得關心的問題。如果每當拋出一個例外,applet 就要建立一個對象,
那麼隨著時間的推移,這個 applet 就會在珍貴的 EEPROM 儲存空間中積攢很多不用的例外執行個體。為了
最佳化儲存空間的使用,所有的例外對象都應當在初始化時建立,並把它們的引用永久地儲存起來。當一
個例外事件發生的時候,就不用再建立新的例外對象了。applet 可照下面這樣作:
1. 索取並重用關於所希望例外對象的引用
2. 在該對象中填入原因碼
3. 拋出該對象
為了支援可重用的例外對象,JCRE已經為 Java卡 APIs 中的每一種例外類型建立了一個執行個體。類
CardException 和 CardRuntimeException 和它們的每一個子類,為使 applet 能重用該例外執行個體,提供
了一個靜態方法 throwIt:
public static void throwIt(short reason)
每當調用方法 throwIt 的時候,它就拋出一個 JCRE 已經建立的例外執行個體。Applet 向 throwIt 方
法指出一個原因碼。例如,為了拒絕一個 APDU命令,applet 可拋出一個 ISOException 例外並指出原
因碼為“命令不允許”:
ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
一個 applet也可以建立它自己的例外對象。在初始化過程中,applet 執行個體化這樣的一個例外對象,
並將其引用儲存在一個永久域中。以後,只要它需要拋出該例外時,就重用這個執行個體。
6.2.3.ISOException.
ISOException 是 Java 卡平台中一個特別的非檢測例外。它是在運行期間發生的,指出一個警告或
卡片中的一個錯誤狀態。ISOException 在原因碼中封裝了一個 ISO7816相應狀態字。
ISOException 允許 applet 高效地處理錯誤。當一條命令被成功地處理了的時候,方法正常返回。
但是如果發生錯誤,該方法就簡單地拋出一個具有適當狀態字的 ISOException 例外。
一般地,applet 不處理 ISOException 例外。JCRE 最終捕獲 ISOException 例外並返回一個它所包
含的原因碼,作為向 host應用返回的 ISO狀態字。這就使為什麼該例外類在其名字中含有 ISO的原因。
ISO 狀態字是 APDU 協議的一部分。它被用作一個智慧卡應用向 host 應用返回對一條 APDU 命
令處理的狀態。Java 卡平台提供了一個介面 javacard.framework.ISO7816,定義了最通用的關於 ISO
7816-3和 ISO 7816-4 的狀態字常數。一個 applet能夠定義它自己的狀態字並可利用 ISOException 把它
們傳送給 host 應用。
6.2.4.UserException.
當一個 applet 遇到一個需要該 applet糾正的程式錯誤的時候,它就拋出一個 UserException。不
像 ISOException,UserException 例外是從 CardException 衍生出來的待檢測的例外,因此必須由 applet
處理。如果 applet 需要建立另外的例外類型,它就建立從 UserException 派生的類。