0
第 12 章

抽象類別 (Abstract Class)
與介面 (Interface)
本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老
師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用...
學習目標
 認識抽象類別 (Abstract Class)
 瞭解抽象方法 (Abstract Method)
 瞭解多重繼承
 認識介面 (Interface)
 認識 4 種內部類別 (Inner Class)
 使用 Enum...
前言
 在前面的章節中 , 相信你對於物件導向的觀念已經有
了相當的認識 , 本章將從繼承這個主題衍生 , 繼續探
討兩個重要的主題 - - 抽象類別與介面。
12-1 抽象類別 (Abstract Class)
 回頭看看上一章中提到多層的繼承時 , 所與的圖形類
別範例 , 在 Shape 、 Circle 、 Cylinder 、
Rectangle 這幾個類別中 , Shape 這個類別的物...
12-1-1 甚麼是抽象類別?
 為了解決上述的問題 , Java 提供抽象類別
(Abstract Class) 的機制 , 其用途即是讓我們標註某
個類別僅是抽象的概念 , 不應該用以產生物件。
 只要在類別的名稱之前加上 abstra...
甚麼是抽象類別?
 舉例來說 , 在上述範例中的 Shape 類別就可以改成
這樣:

 由於抽象類別不能建立物件 , 因此只要程式中有任何
地方想要建立抽象類別的物件 , 在編譯時 , 就會出現
錯誤 , 例如以下的範例。
甚麼是抽象類別?
甚麼是抽象類別?

 如上所示 , 一旦在類別定義前加上 abstract 將其宣
告為抽象類別後 , 在程式中要建立該類別的物件就會
被視為錯誤。
12-1-2 抽象方法 (Abstract Method)
 同樣的道理 , 在上一章所舉的計算地價的程式範例中
, Land 類別也可視為是一個抽象的概念 , 表示某種
形狀的土地 , 真正能用來計算地價時 , 都是使用其子
類別 Circ...
抽象方法 (Abstract Method)
 不過讓我們仔細思考一下 , Land 這個抽象類別和前
述圖形類別 Shape 有些微的不同。
 在 Shape 類別中所定義的建構方法或其它方法 , 是
其子類別所共同需要且會實際呼叫使用的...
抽象方法 (Abstract Method)
 在 Java 中 , 對於這種性質的方法 , 可以將之標註為
抽象方法 (Abstract Method), 如此一來 , 就只需要
定義方法的名稱以及所需要的參數及傳回值的型別即
可 , 而不...
抽象方法 (Abstract Method)
抽象方法 (Abstract Method)
抽象方法 (Abstract Method)
 第 2 行就是加上 abstract 標註的 area() 方法 , 由
於不需要定義其主體區塊 , 所以要記得在右括號之候
補上一個分號 ( ; ), 表示這個敘述的結束。
 使用抽象方法時...
抽象方法 (Abstract Method)
 但是反過來說 , 一個抽象類別卻未必要擁有抽象方法
, 像是前面介紹的 Shape 類別就未定義抽象方法。
▪ 相同的道理 , 標註為 final 的類別不能擁有抽象方法 ,
因為 final ...
12-1-3 抽象類別、抽象方法與繼承關係
 對於一個擁有抽象方法的抽象類別來說 , 如果其子類
別並沒有實作其中的所有抽象方法 , 那麼這個子類別
也必須定義為抽象類別。例如:
抽象類別、抽象方法與繼承關係
抽象類別、抽象方法與繼承關係
 由於 Child 並沒有實作繼承而來的 show() 抽象方
法 , 所以編譯的錯誤訊息就是說 Child 也必須是一
個抽象類別。
 如果要正確編譯這個程式 , 除了必須為 Child 類別
加上 abst...
抽象類別、抽象方法與繼承關係
抽象類別、抽象方法與繼承關係
抽象類別、抽象方法與繼承關係
▪ 抽象類別的用途是在於該類別必須衍生子類別 , 才能
產生物件的場合。如果只是要防止建立特定類別的物
件 , 應該改成只為該類別定義一個 private 存取控制且
不需參數的建構方法。這在該類別僅是作為提供
s...
12-2 介面 ( Interface)
 前面提過 , 撰寫物件導向程式的第一步 , 就是分析出
程式中需要哪些類別 , 以及類別之間的繼承關係。
 不過就像我們在現實世界中所看到的 , 許多『不同
類』的事物 , 其間又通常會具有一些相...
介面 ( Interface)
 類似這樣的情況 , 在設計程式時也會遇到:一些在繼
承架構中明顯不同的類別 , 它們卻有具有一些相似的
行為 ( 特性 ), 而造成設計類別時的困擾 , 例如將明
顯不同性質的類別 ( 例如飛機和鳥 ) , ...
介面 ( Interface)
 為了讓這樣的設計也能系統化 , 不會造成不同性質的
類別 , 在實作它們應有的共通行為時造成遺漏或命名
不一致 , 在 Java 中特別提供了介面 (Interface) 來
描述這個共通的行為。
Java 不支援多重繼承
 有些物件導向程式語言會支援多重繼承 (Multiple
Inheritance), 也就是讓單一類別同時繼承自多個父
類別 , 如此一來可解決上述不同性質類別有共通行為
的問題。
 不過多重繼承也會使語言複雜化 ...
12-2-1 定義介面
 由於介面代表的是一群共通的行為 , 感覺上和類別好
像有些類似之處 , 但其實兩者具有相當的差異 , 只是
外觀上定義介面也是用大括號來描述此介面的方法 ,
而開頭則要改用 interface 來表示:
定義介面
 由於類別是用以描述實際存在的物件 , 而介面則僅是
用以描述某種行為方式 ( 例如『會飛』這件事 ) , 所
以兩者本質上有許多差異 , 以下是定義介面時要注意
的重點:
▪ 介面的命名也和類別一樣 , 通常都是以首字母大寫的
方...
定義介面
▪ 在介面中只能定義方法的型別 ( 傳回值 ) 及參數型別 ,
不可定義方法本體 ( 和抽象方法相同 ), 這些方法在編
譯時會自動成為 public abstract 的公開抽象方法 ,
請記得在右括號之後加上分號。
▪ 由於介面通...
定義介面
 舉例來說 , 前一章最後我們舉了計算地價的範例 , 要
計算地價時 , 當然要算出土地的面積 , 然而計算面積
這件事 , 可能是很多類別需要的功能 , 所以我們可以
定義一個計算面積的介面:
定義介面
 如前所述 , 不可定義方法本體 , 因為每種不同形體 ,
其面積的計算方式也都不同 , 我們也無法預知有哪些
類別需要計算面積。
 因此此處 surfacing 介面只規定了計算面積的方式名
稱為 area 、沒有參數、但傳回值...
12-2-2 介面的實作
 定義好介面之後 , 需要使用該介面的類別 , 就必須實
作該介面 , 實作介面時 , 必須在類別名稱之後 , 使用
implements 保留字 , 再加上要實作的介面名稱。
 此外 , 前面提過 , 介面中所定...
介面的實作
介面的實作
 我們先用上一章 Shape 類別及 Circle 類別的繼承
架構 , 並讓 Circle 實作 Surfacing 介面:
介面的實作
介面的實作
介面的實作
介面的實作
 第 18 行的 Circle 類別定義 , 先繼承了 Shape 類
別再以 implements 保留字表示此類別要實作
Surfacing 。
 因此 Circle 類別必須定義於 Surfacing 介面中宣告
的 ar...
12-2-3 介面中的成員變數
 介面也可以擁有成員變數 , 不過在介面中宣告的成員
會自動擁有 public static public final 的存取控制 ,
而且必須在宣告時即指定初值。
 換句話說 , 在介面中僅能定義由所有實作...
介面中的成員變數
介面中的成員變數
介面中的成員變數
介面中的成員變數

 在第 3 行於介面中定義了成員變數 PI, 並在第 28
行 Circle 類別的 area() 方法用以計算圓面積。
 雖然定義 PI 時未加上任何存取控制字 , 但如前述 ,
介面中的成員變數會自動成為 publi...
12-3 介面的繼承
 介面之間也可以依據其相關性 , 以繼承的方式來架構
, 就像是在上一章中對於類別的分類一樣。
 不過介面的繼承與類別的繼承最大的差別 , 就在於介
面可以繼承多個父介面 , 類別無法繼承多個父類別。
 也正由於這樣...
12-3-1 簡單的繼承
 介面的繼承最簡單的形式就是使用 extends 保留字
從指定的介面延伸 , 例如:
簡單的繼承
簡單的繼承
1. 第 7 行中就宣告了一個繼承自介面 P 的介面 C 。
由於有繼承關係 , 所以介面 C 的內容除了自己所定
義的 getI() 方法之外 , 也繼承了由 P 而來的成員變
數 i 以及 show() 方法。
因此 , 在第 ...
簡單的繼承
3. 第 16 行則是實作介面 C 中的 getI() 方法 , 它會
把繼承自介面 P 的成員變數 i 傳回。
 透過這樣的繼承關係 , 就可以將複雜的介面依據其
結構性 , 設計成多層的介面 , 以便讓個別的介面都
能夠彰顯出...
12-3-2 介面的多重繼承

 介面也可同時繼承多個父介面 , 將多項特性併在一起:
介面的多重繼承
介面的多重繼承
介面的多重繼承
 在第 13 行中就宣告了 C 要繼承 P1 和 P2, 透過
繼承的關係 , 現在介面 C 就等於定義有 showI() 、
showJ() 、 show() 三個方法以及 i 、 j 兩個成員變
數了。
 因此在 Mul...
繼承多個同名的方法
 前面提過 , 介面和類別在繼承上最大的差異 , 就是介
面可以繼承多個父介面。
 既然可以繼承多個介面 , 就可能會發生不同的父介面
卻擁有相同名稱的方法 , 例如:
繼承多個同名的方法
繼承多個同名的方法
繼承多個同名的方法
 在這個程式中 , P1 與 P2 介面都定義有同樣的
show() 方法 , 這就代表了只要是要實作 P1 或是
P2 介面的類別 , 都必須實作 show() 方法。
 因此 , 當 C 繼承 P1 以及 P2 後 ...
繼承多個同名的成員變數
 如同剛剛所看到 , 繼承多個同名的方法並不會有問題
, 但是繼承多個同名的成員變數就有點問題了。例如
:
繼承多個同名的成員變數
繼承多個同名的成員變數
 由於介面 P1 與介面 P2 都確確實實有一個同名的
成員變數 i, 所以第 23 行究竟要顯示的是介面 P1
還是介面 P2 的 i 呢?
 答案是無法決定 , 因此在編譯程式時 , 會發生如下的
錯誤:
繼承多個同名的成員變數
 您必須在程式中明確的冠上介面的名稱 , 才能讓編譯
器知道您所指的到底是哪一個 i :
繼承多個同名的成員變數
繼承多個同名的成員變數
實作多重介面
 單一類別也可以同時實作多個介面 , 這時會引發的問
題就如同單一介面繼承多個介面時一樣 , 請參考前面
的討論即可。
介面與類別的關係
 就實務面來看 , 介面其實就是一種特殊的抽象類別 ,
因此假設 A 類別實作了 B 介面 , 那麼我們也可將
B 介面當成是 A 的父類別來使用 , 例如:
介面與類別的關係
 而這二者的差異 , 則在於介面的設計比抽象類別嚴格
許多:
▪ 介面的方法均為 public abstract, 因此只能定義沒有實
作內容的公開抽象方法。
▪ 介面的變數均為 public static final, 因...
12-4 內 部類別 (Inner Class)


內 部類別 (Inner Class) 也稱為巢狀 類別 (Nested

Class), 主要是指『定義在類別之內』的類別。依照
內部類別的用途可分為以下 4 種:
1. 一般內 部類別...
內 部類別 (Inner Class)
3. 匿名類別 (Anonymous Inner Class) :有點類似匿名
陣列的寫法 , 主要是用來臨時定義一個特定類別 ( 或
介面 ) 的匿名子類別 , 並用以產生物件來使用。
4. 靜態內部類...
12-4-1 一般內 部類別
 如果 A 類別只需配合 B 類別來使用 , 那麼就可將
A 定義為 B 的內部類別。
 其最大的好處 , 就是內部類別可以直接存取外部類別
的所有成員 , 包括 private 成員在內。
 底下來看範例。
一般內 部類別
一般內 部類別
一般內 部類別
 由於在內部及外部類別都宣告了 j 、 k 變數 , 因此
在內部類別中要存取外部的 j 、 k 時 , 必須加上外
部類別的名稱才行。
 不過只有靜態變數才能直接以類別名稱 (Outter.k)
來存取 , 實體變數則必須...
一般內 部類別
 請注意 , 內部及外部類別仍為 2 個不同的類別 , 因
此在外部類別的方法中 , 必須先建立內部物件 , 然後
才能存取內部物件中的變數及方法 , 例如以上的 16
行。
 此時所建立的內部物件 , 會附屬在呼叫此方法的...
一般內 部類別
 然而 , 若是在其他類別的方法中 ( 或在外部類別的靜
態方法中 ) , 由於並不存在外部物件 , 因此必須先建
立外部物件 , 然後再利用外部物件來建立內部物件才
行 , 例如以上的 23 、 24 行。
 如果這二行所...
一般內 部類別
 以上是先用 new Outter() 建立外部物件 , 然後再串
接 .new Inner() 來建立內部物件;若還要再呼叫內
部物件的方法 , 則再以 . 來串接。
 值得注意的是 , 內部類別由於在其他類別中看不到 ,...
內 部類別在編譯後所產 生的 .class
檔
 Java 程式在編譯之後 , 每個類別都會產生一個與類
別同名的 .class 檔。
 內部類別也不例外 , 但會在檔名前加上外部類別的名
稱以及 $ 符號 , 例如前面的範例會產生以下 3...
內 部類別在編譯後所產 生的 .class
檔
 此外 , 一個類別之中可以有多個內部類別 , 而內部類
別之內也還可以再有內部類別 , 這些類別在編譯之後
, 每個類別都會產生一個 .class 檔案。
 如果是多層的內部類別 , 則編譯...
12-4-2 方法內 部類別 (Method Inner Class)
 如果類別只在某個方法中臨時需要 , 那麼就可將之定
義在方法中 , 而成為方法內 部類別。例如:
方法內 部類別 (Method Inner Class)

 以上程式在 main() 中定義了一個方法內部類別
Inner, 然後用它來建立物件並呼叫其 print() 方法
。
方法內 部類別 (Method Inner Class)
 在使用方法內部類別時 , 有以下幾點要注意:
1.

必須先定義好類別 , 然後再用它來建立物件 ( 就是
定義要寫在 new 之前 ) 。

2. 在方法內部類別中 , 可以直接存...
方法內 部類別 (Method Inner Class)
3. 在方法內部類別中若要存取所屬方法的區域變數 , 則
只能存取以 final 宣告的區域變數 ( 參見範例第
6、7 行)。
這是因為一般區域變數在方法結束時即消失 , 而內部
物件...
方法內 部類別 (Method Inner Class)
▪ 在任何情況下 , abstract 或 final 都不可一起使用 , 因
為二者的意義完全相反:前者代表必須被更改 , 而後
者代表不允許被更改。
▪ 以上程式的 Inner 類別...
12-4-3 匿名類別 (Anonymous Inner
Class)
 匿名類別主要是用來臨時定義一個某類別 ( 或介面 )
的子類別 , 並用以產生物件;由於該子類別用完即丟
, 所以不需要指定名稱。
 底下是一個簡單的範例。
匿名類別 (Anonymous Inner Class)
匿名類別 (Anonymous Inner Class)

 在第 7 行 Face c = new Face( ); 最後的分號之前 ,
我們插入了一段 { ... } 的類別定義 , 如此就會建立一
個匿名子類別的物件 , 並讓父類別的變...
匿名類別 (Anonymous Inner Class)
 使用匿名類別的時機 , 通常是在想要臨時變更 ( 重
新定義 ) 某新物件的方法 , 但只要變更一次的情況
;以本例來說 , 就是改變 smile() 方法所輸出的字
串。
 匿名...
匿名類別 (Anonymous Inner Class)
2. 抽象類別或介面也可用來建立匿名類別 , 但在匿名類
別中必須實作所有未實做的方法。
3. 在匿名類別中也可以宣告新的變數或方法 , 但由於所
產生的物件會交由父類別的變數來參照 ,...
匿名類別 (Anonymous Inner Class)
匿名類別 (Anonymous Inner Class)
▪ 雖然抽象類別及介面都不能用來建立物件 ( 例如 new
Movable(); 會編譯錯誤 ), 但卻可用來宣告變數以參照
其子物件 ( 例如上面的第 5 行 ) 。

 匿名類別除...
匿名類別 (Anonymous Inner Class)
匿名類別 (Anonymous Inner Class)
 以上在第 8 行呼叫 myMove 時 , 就利用匿名類別
臨時建立一個 Movable 的子物件做為參數。
 而在 myMove() 方法中執行 m.move() 時 , 就會執...
12-4-4 靜態內 部類別 (Static Inner Class)
 靜態內 部類別 (Static Inner Class) 就是加上 static
的內部類別。其特色就和一般的 static 成員一樣 ,
可以直接透過『外部類別名稱 ...
靜態內 部類別 (Static Inner Class)
靜態內 部類別 (Static Inner Class)
 在以上的靜態內部類別中 , 分別宣告了實體及靜態的
成員 , 然後在 main() 中即可利用類別名稱
『 Outter.Inner 』來直接建立內部物件 , 或以此名稱
來存取內部...
12-5 enum 列舉型別
 列舉型別是指該型別的值只能是列舉出來的幾種 , 而
不可以是其他未列出的值。
 例如我們可以建立一個 Week 列舉型別 , 並讓其值
只允許有 Sunday 、 Monday...Saturday 等 7 ...
12-5-1 列舉的宣告與使用
 enum 是 Java 5.0 版才新增的功能 , 專門用來定義
一種稱為『列舉型別』的特殊類別。
 例如我們需要一個代表紙杯大小的類別 , 而其尺寸只
有 Small 、 Medium 、 Large 三...
列舉的宣告與使用
列舉的宣告與使用
 在第 1 行定義了只允許 3 種值的列舉型別
CupSize, 並在 main() 中用它來宣告 2 個列舉變數
。
 當要指定列舉值時 , 則必須加上型別名稱或物件參照
, 例如 CupSize.Medium 或 c....
12-5-2 列舉型別其實是類別
 enum 列舉型別其實就是一種特殊的類別 , 因此前面
程式中的 enum CupSize 型別 , 在編譯後會產生
CupSize.class 類別檔。
 事實上 , 編譯器在編譯『 enum CupS...
列舉型別其實是類別
列舉型別其實是類別
 由此可知 , 每個列舉值其實都是一個實體物件 , 其內
容包含了列舉名稱字串 ( 例如 "Small") 及列舉順序
( 由 0 開始 ) 的資訊。而以下程式:

 在執行後 , 變數 a 會就參照到內容為 ("Sma...
列舉型別其實是類別
 在了解列舉型別與類別的關係之後 , 再來看看幾點
注意事項:
1. 在定義列舉型別時 , 敘述的後面不用加分號 ( 要加當
然也可以 ), 例如以下都是正確的寫法:

2. 列舉型別不可使用 new 來建立新物件 ( 因...
列舉型別其實是類別
3. 列舉型別也可以定義在類別之內 , 而成為內部類別 ,
此時編譯器會自動加上 static final 而成為靜態內部
類別 ( 這樣就可以和外部物件脫離關係了 ) 。請注意
, 列舉型別不允許定義在方法之內。底下是個簡...
列舉型別其實是類別
4. 列舉型別若定義在其他類別之外 , 就只能宣告為
public 或預設存取控制 , 而不能是 private 或
protected 。但若是定義在其他類別的內部 , 則不受
此限制。
▪

此條規則其實也適用於所有的類...
列舉型別其實是類別
5. 列舉型別不能再繼承其他的類別了 , 因為編譯器會自
動將之改為繼承 Enum 類別。
▪

列舉型別可以實做介面 , 例如 enum CupSize
implements Sealable { ... }, 但必須在 ...
12-5-3 列舉型別內 建的幾個方法
 編譯器在改寫列舉型別時 , 還特別提供了以下幾個實
用的方法:
列舉型別內 建的幾個方法
列舉型別內 建的幾個方法
 以上的 CupSize.values() 會傳回所有列舉值的陣列 ,
您也可宣告一個變數來參照 , 例如:『 CupSize[] cs
= CupSize.values(); 』。
12-5-4 用 switch 來判斷列舉值
 在第 5 章介紹 switch 時 , 曾說 switch 只能用來判
斷整數類的資料 ( 只支援
byte 、 char 、 short 、 int, 而且在比對時一律會先
轉型為 int) ...
用 switch 來判斷列舉值
用 switch 來判斷列舉值
用 switch 來判斷列舉值
 以上程式定義了一個可將 CupSize 轉為 cc 數的靜
態方法 , 然後在 main() 中用來進行轉換並輸出結果
。
 請注意第 6~8 行的 case 後面要使用不加型別名稱
的列舉值 , 例如 S...
12-5-5 在 enum 中加入自訂方法與變數
 由於 enum 也是一種類別 , 因此在必要時也可以加
入一些自訂的建構方法、一般方法、或變數。
 就以前面將尺寸轉換為 cc 數的例子來說 , 如果能將
cc 數的資料內建在 CupSi...
在 enum 中加入自訂方法與變數
在 enum 中加入自訂方法與變數
在 enum 中加入自訂方法與變數
 首先請看第 2 行 , 在 enum 中必須將列舉值串列寫
在最前面 , 並以分號結束。列舉值之後若有加小括號
及參數 , 則會呼叫自訂的建構方法來建立物件。
 第 6 行就是我們自訂的建構方法 , 可...
在 enum 中加入自訂方法與變數
 另外 , 在 enum 中還定義了 getCc() 方法 ( 第 7
行 ), 以便在 main() 中呼叫以取得每一個列舉值的
cc 數。
 最後 , enum 還有一個絕招 , 就是可以在列舉值之後...
在 enum 中加入自訂方法與變數
 由於在 Medium 後的 { } 中重新定義了 getCc() 方
法 , 因此輸出結果會變成:

▪ 列舉值的專屬方法會自動設為靜態方法 , 因此在方法
內只能存取靜態成員。
 12-A 撰寫通用於多種類別的程式
 12-B 擔負物件之間的溝通角色
1. Given:

Which class that use Animal class will compile? (Choose
four. )
A.
B.
C.
D.
E.
F.
G.

abstract class Dog implements Animal { private int Age; }
abstract class Dog extends Animal { public...
2. Given :

Which code inserted individually at line 6, will make use of
polymorphism? (Choose all that apply.)
A. void ca...
3. Drag and drop code s into empty boxes to complete the C
interface .
4. Given:
Which statements are true ? (Choose three . )
A. This code will output A.
B. This code will output B.
C. If remove line 6 ...
5. Given:

Which statement, insert at line 3, will compile?
A. CupSize c = Medium;
B. A.CupSize c = CupSize.Medium;
C. A.C...
6. Which statement is true? (Choose all that apply. )
A. A protected static method can be overridden by it's subclass.
B. ...
7. Given:

Which one, inserted at line 5, will create an Tire object?
A. Tire t = new Tire();
B. Tire t = new Car().new Ti...
8. Given:

Which two statements are true?
A. Compilation will fail at line 1.
B. Compilation will fail at line 2.
C. Compi...
9. Given:

Which code, inserted at line 7, will cause an Exception at
runtime?
A. A x = b;
B. A x = (A)b;
C. A x = (B)(A) ...
10. Given:
Which statement is true?
A. Compilation fails at line 3.
B. Compilation fails at line 5.
C. Compilation fails at line 7.
D...
SCJP ch12
SCJP ch12
SCJP ch12
SCJP ch12
SCJP ch12
Upcoming SlideShare
Loading in...5
×

SCJP ch12

72

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
72
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "SCJP ch12"

  1. 1. 第 12 章 抽象類別 (Abstract Class) 與介面 (Interface) 本投影片(下稱教用資源)僅授權給採用教用資源相關之旗標書籍為教科書之授課老師(下稱老師)專用,老 師為教學使用之目的,得摘錄、編輯、重製教用資源(但使用量不得超過各該教用資源內容之 80% )以製作為 輔助教學之教學投影片,並於授課時搭配旗標書籍公開播放,但不得為網際網路公開傳輸之遠距教學、網路教 學等之使用;除此之外,老師不得再授權予任何第三人使用,並不得將依此授權所製作之教學投影片之相關著 作物移作他用。 著作權所有 © 旗標出版股份有限公司
  2. 2. 學習目標  認識抽象類別 (Abstract Class)  瞭解抽象方法 (Abstract Method)  瞭解多重繼承  認識介面 (Interface)  認識 4 種內部類別 (Inner Class)  使用 Enum 列舉型別
  3. 3. 前言  在前面的章節中 , 相信你對於物件導向的觀念已經有 了相當的認識 , 本章將從繼承這個主題衍生 , 繼續探 討兩個重要的主題 - - 抽象類別與介面。
  4. 4. 12-1 抽象類別 (Abstract Class)  回頭看看上一章中提到多層的繼承時 , 所與的圖形類 別範例 , 在 Shape 、 Circle 、 Cylinder 、 Rectangle 這幾個類別中 , Shape 這個類別的物件其 實並未被主程式用到 , 而其存在的目的只是為了讓整 個繼承結構更完善。  實際上 Shape 只是一個抽象的概念 , 程式中並不會 用到 Shape 物件 , 而只會使用它的衍生類別如 Circle 、 Rectangle 等 , 來建立物件。  因此 , 我們需要一種方法 , 可以讓類別的使用者知道 , Shape 這個類別並不能用來產生物件。
  5. 5. 12-1-1 甚麼是抽象類別?  為了解決上述的問題 , Java 提供抽象類別 (Abstract Class) 的機制 , 其用途即是讓我們標註某 個類別僅是抽象的概念 , 不應該用以產生物件。  只要在類別的名稱之前加上 abstract , 該類別就會 成為抽象類別 , Java 編譯器將會禁止任何產生此物 件的動作。
  6. 6. 甚麼是抽象類別?  舉例來說 , 在上述範例中的 Shape 類別就可以改成 這樣:  由於抽象類別不能建立物件 , 因此只要程式中有任何 地方想要建立抽象類別的物件 , 在編譯時 , 就會出現 錯誤 , 例如以下的範例。
  7. 7. 甚麼是抽象類別?
  8. 8. 甚麼是抽象類別?  如上所示 , 一旦在類別定義前加上 abstract 將其宣 告為抽象類別後 , 在程式中要建立該類別的物件就會 被視為錯誤。
  9. 9. 12-1-2 抽象方法 (Abstract Method)  同樣的道理 , 在上一章所舉的計算地價的程式範例中 , Land 類別也可視為是一個抽象的概念 , 表示某種 形狀的土地 , 真正能用來計算地價時 , 都是使用其子 類別 Circle 、 Square 等所建立的物件。  因此 , Land 也是標註為抽象類別的好對象:
  10. 10. 抽象方法 (Abstract Method)  不過讓我們仔細思考一下 , Land 這個抽象類別和前 述圖形類別 Shape 有些微的不同。  在 Shape 類別中所定義的建構方法或其它方法 , 是 其子類別所共同需要且會實際呼叫使用的;可是在 Land 類別中 , 其定義的 area() 方法 , 本身並不執 行任何動作 , 它的存在只是為了確保所有 Land 的 衍生類別都會有 area() 方法而已 , 至於各衍生類別 area() 方法實際要進行什麼動作 , 則是由各衍生類別 自行依本身的特性定義之。  但是因為 area() 方法的傳回值為 double , 所以在 Land 類別中就定義成很無聊的傳回 0 。
  11. 11. 抽象方法 (Abstract Method)  在 Java 中 , 對於這種性質的方法 , 可以將之標註為 抽象方法 (Abstract Method), 如此一來 , 就只需要 定義方法的名稱以及所需要的參數及傳回值的型別即 可 , 而不需要定義其主體區塊的內容。  標註的方法就和抽象類別一樣 , 只要在方法名稱之前 加上 abstract 即可 , 同樣以 Land 類別為例。
  12. 12. 抽象方法 (Abstract Method)
  13. 13. 抽象方法 (Abstract Method)
  14. 14. 抽象方法 (Abstract Method)  第 2 行就是加上 abstract 標註的 area() 方法 , 由 於不需要定義其主體區塊 , 所以要記得在右括號之候 補上一個分號 ( ; ), 表示這個敘述的結束。  使用抽象方法時 , 要特別注意 , 擁有抽象方法的類別 一定要標註為抽象類別。  這道理很簡單 , 抽象方法代表的意義是這個方法要到 子類別才會真正實作 , 既然如此 , 就表示其所屬的類 別並不完整 , 自然就不應該拿來產生物件使用 , 所以 必須為抽象類別。
  15. 15. 抽象方法 (Abstract Method)  但是反過來說 , 一個抽象類別卻未必要擁有抽象方法 , 像是前面介紹的 Shape 類別就未定義抽象方法。 ▪ 相同的道理 , 標註為 final 的類別不能擁有抽象方法 , 因為 final 類別無法被繼承 , 當然也就無法實作其中 的抽象方法了。
  16. 16. 12-1-3 抽象類別、抽象方法與繼承關係  對於一個擁有抽象方法的抽象類別來說 , 如果其子類 別並沒有實作其中的所有抽象方法 , 那麼這個子類別 也必須定義為抽象類別。例如:
  17. 17. 抽象類別、抽象方法與繼承關係
  18. 18. 抽象類別、抽象方法與繼承關係  由於 Child 並沒有實作繼承而來的 show() 抽象方 法 , 所以編譯的錯誤訊息就是說 Child 也必須是一 個抽象類別。  如果要正確編譯這個程式 , 除了必須為 Child 類別 加上 abstract 存取控制 , 同時第 17 行也不能用它 建立物件 , 請參考以下的例子。
  19. 19. 抽象類別、抽象方法與繼承關係
  20. 20. 抽象類別、抽象方法與繼承關係
  21. 21. 抽象類別、抽象方法與繼承關係 ▪ 抽象類別的用途是在於該類別必須衍生子類別 , 才能 產生物件的場合。如果只是要防止建立特定類別的物 件 , 應該改成只為該類別定義一個 private 存取控制且 不需參數的建構方法。這在該類別僅是作為提供 static 方法給其他類別使用、或是需在初始化時自動 產生物件時特別有用。
  22. 22. 12-2 介面 ( Interface)  前面提過 , 撰寫物件導向程式的第一步 , 就是分析出 程式中需要哪些類別 , 以及類別之間的繼承關係。  不過就像我們在現實世界中所看到的 , 許多『不同 類』的事物 , 其間又通常會具有一些相似的行為。  舉例來說 , 飛機和小鳥很顯然不會是同性質的類別 , 而其飛的方式也不同 , 但不可否認它們都具有會飛行 的行為。
  23. 23. 介面 ( Interface)  類似這樣的情況 , 在設計程式時也會遇到:一些在繼 承架構中明顯不同的類別 , 它們卻有具有一些相似的 行為 ( 特性 ), 而造成設計類別時的困擾 , 例如將明 顯不同性質的類別 ( 例如飛機和鳥 ) , 湊成在同一繼 承架構下 , 使得類別的繼承關係不合常理。  因此為了不打亂原有的繼承架構 , 我們可能要選擇分 別在各自的類別中定義各自有的行為。
  24. 24. 介面 ( Interface)  為了讓這樣的設計也能系統化 , 不會造成不同性質的 類別 , 在實作它們應有的共通行為時造成遺漏或命名 不一致 , 在 Java 中特別提供了介面 (Interface) 來 描述這個共通的行為。
  25. 25. Java 不支援多重繼承  有些物件導向程式語言會支援多重繼承 (Multiple Inheritance), 也就是讓單一類別同時繼承自多個父 類別 , 如此一來可解決上述不同性質類別有共通行為 的問題。  不過多重繼承也會使語言複雜化 , 而在第一章我們就 提過 , 『簡單』是 Java 語言的主要特色之一 , 因此 當初開發 Java 語言的小組就決定讓 Java 語言不 支援多重繼承 , 以保持其簡單易學的特色。  雖然如此 , Java 的實用性並不因此而減少 , 需要使 用到多重繼承的場合 , 幾乎也都可透過 Java 的介面 功能達成。
  26. 26. 12-2-1 定義介面  由於介面代表的是一群共通的行為 , 感覺上和類別好 像有些類似之處 , 但其實兩者具有相當的差異 , 只是 外觀上定義介面也是用大括號來描述此介面的方法 , 而開頭則要改用 interface 來表示:
  27. 27. 定義介面  由於類別是用以描述實際存在的物件 , 而介面則僅是 用以描述某種行為方式 ( 例如『會飛』這件事 ) , 所 以兩者本質上有許多差異 , 以下是定義介面時要注意 的重點: ▪ 介面的命名也和類別一樣 , 通常都是以首字母大寫的 方式 , 使得其在程式中容易被識別。有些人習慣在介 面的名稱前加上一個大寫字母 ' I" , 以特別標示這個名 稱是個介面。
  28. 28. 定義介面 ▪ 在介面中只能定義方法的型別 ( 傳回值 ) 及參數型別 , 不可定義方法本體 ( 和抽象方法相同 ), 這些方法在編 譯時會自動成為 public abstract 的公開抽象方法 , 請記得在右括號之後加上分號。 ▪ 由於介面通常代表某種特性 , 因此介面的名稱一般都 是一個形容詞 , 表示可以如何的意思 , 例如可用 Flying 表示『會飛』的意思。
  29. 29. 定義介面  舉例來說 , 前一章最後我們舉了計算地價的範例 , 要 計算地價時 , 當然要算出土地的面積 , 然而計算面積 這件事 , 可能是很多類別需要的功能 , 所以我們可以 定義一個計算面積的介面:
  30. 30. 定義介面  如前所述 , 不可定義方法本體 , 因為每種不同形體 , 其面積的計算方式也都不同 , 我們也無法預知有哪些 類別需要計算面積。  因此此處 surfacing 介面只規定了計算面積的方式名 稱為 area 、沒有參數、但傳回值為 double 型別。  如前所述 , 介面中的方法一定都是公開的抽象方法 , 所以通常 public 、 abstract 也都省略不寫。
  31. 31. 12-2-2 介面的實作  定義好介面之後 , 需要使用該介面的類別 , 就必須實 作該介面 , 實作介面時 , 必須在類別名稱之後 , 使用 implements 保留字 , 再加上要實作的介面名稱。  此外 , 前面提過 , 介面中所定義的方法會自動成為抽 象方法 , 因此實作介面時就必須完全實作介面中的所 有方法。
  32. 32. 介面的實作
  33. 33. 介面的實作  我們先用上一章 Shape 類別及 Circle 類別的繼承 架構 , 並讓 Circle 實作 Surfacing 介面:
  34. 34. 介面的實作
  35. 35. 介面的實作
  36. 36. 介面的實作
  37. 37. 介面的實作  第 18 行的 Circle 類別定義 , 先繼承了 Shape 類 別再以 implements 保留字表示此類別要實作 Surfacing 。  因此 Circle 類別必須定義於 Surfacing 介面中宣告 的 area() 方法 , 所以在第 27 行定義了計算圓面積 的 area() 方法。  如果宣告了要實作某個介面 , 但類別中未定義該介面 所宣告的方法 , 編譯時將會出現錯誤。  另外 , 要記住介面所提供的方法都是 public, 因此實 作介面的方法時也要將之宣告為 public, 不可將之設 為其他的存取控制 , 否則會造成編譯錯誤。
  38. 38. 12-2-3 介面中的成員變數  介面也可以擁有成員變數 , 不過在介面中宣告的成員 會自動擁有 public static public final 的存取控制 , 而且必須在宣告時即指定初值。  換句話說 , 在介面中僅能定義由所有實作該介面的類 別所共享的常數。  舉例來說 , 在剛才的例子中 , 將圓周率常數定義在介 面中 , 而實作該介面的類別 , 亦可存取該常數。
  39. 39. 介面中的成員變數
  40. 40. 介面中的成員變數
  41. 41. 介面中的成員變數
  42. 42. 介面中的成員變數  在第 3 行於介面中定義了成員變數 PI, 並在第 28 行 Circle 類別的 area() 方法用以計算圓面積。  雖然定義 PI 時未加上任何存取控制字 , 但如前述 , 介面中的成員變數會自動成為 public static final, 所 以在程式第 41 行也能如同存取類別 static 成員變 數一般 , 用介面名稱存取其值。
  43. 43. 12-3 介面的繼承  介面之間也可以依據其相關性 , 以繼承的方式來架構 , 就像是在上一章中對於類別的分類一樣。  不過介面的繼承與類別的繼承最大的差別 , 就在於介 面可以繼承多個父介面 , 類別無法繼承多個父類別。  也正由於這樣的差異 , 因而衍生出幾個必須注意的主 題 , 這些都要在這一小節中討論。
  44. 44. 12-3-1 簡單的繼承  介面的繼承最簡單的形式就是使用 extends 保留字 從指定的介面延伸 , 例如:
  45. 45. 簡單的繼承
  46. 46. 簡單的繼承 1. 第 7 行中就宣告了一個繼承自介面 P 的介面 C 。 由於有繼承關係 , 所以介面 C 的內容除了自己所定 義的 getI() 方法之外 , 也繼承了由 P 而來的成員變 數 i 以及 show() 方法。 因此 , 在第 11 行實作介面 C 時 , 就必須實作 getI() 以及 show() 方法。 2. 第 12 行就是實作介面 P 中的 show() 方法。 再次提醒:介面中的方法會自動成為 public abstract 存取控制 , 因此要記得為實作的方法加上 public 的 存取控制。 在這個方法中 , 只是簡單的把變數 i 顯示出來 , 這 裡的 i 就是經由介面 C 從介面 P 繼承而來的成員 變數。
  47. 47. 簡單的繼承 3. 第 16 行則是實作介面 C 中的 getI() 方法 , 它會 把繼承自介面 P 的成員變數 i 傳回。  透過這樣的繼承關係 , 就可以將複雜的介面依據其 結構性 , 設計成多層的介面 , 以便讓個別的介面都 能夠彰顯出其特性。
  48. 48. 12-3-2 介面的多重繼承  介面也可同時繼承多個父介面 , 將多項特性併在一起:
  49. 49. 介面的多重繼承
  50. 50. 介面的多重繼承
  51. 51. 介面的多重繼承  在第 13 行中就宣告了 C 要繼承 P1 和 P2, 透過 繼承的關係 , 現在介面 C 就等於定義有 showI() 、 showJ() 、 show() 三個方法以及 i 、 j 兩個成員變 數了。  因此在 MultipleInheritance 類別中就必須實作所有 的方法。
  52. 52. 繼承多個同名的方法  前面提過 , 介面和類別在繼承上最大的差異 , 就是介 面可以繼承多個父介面。  既然可以繼承多個介面 , 就可能會發生不同的父介面 卻擁有相同名稱的方法 , 例如:
  53. 53. 繼承多個同名的方法
  54. 54. 繼承多個同名的方法
  55. 55. 繼承多個同名的方法  在這個程式中 , P1 與 P2 介面都定義有同樣的 show() 方法 , 這就代表了只要是要實作 P1 或是 P2 介面的類別 , 都必須實作 show() 方法。  因此 , 當 C 繼承 P1 以及 P2 後 , 也繼承了這個 意義 , 而對實作 C 的類別來說 , 則只需實作一份 show() 方法 , 就可滿足實作 P1 與 P2 介面中同名 方法的需求。  在呼叫時 , 並不會有要呼叫的是 P1 還是 P2 中所 定義方法的混淆。甚至於在介面 C 中 , 還可以多重 定義 (Overloading) 同名的方法 , 而在實作介面 C
  56. 56. 繼承多個同名的成員變數  如同剛剛所看到 , 繼承多個同名的方法並不會有問題 , 但是繼承多個同名的成員變數就有點問題了。例如 :
  57. 57. 繼承多個同名的成員變數
  58. 58. 繼承多個同名的成員變數  由於介面 P1 與介面 P2 都確確實實有一個同名的 成員變數 i, 所以第 23 行究竟要顯示的是介面 P1 還是介面 P2 的 i 呢?  答案是無法決定 , 因此在編譯程式時 , 會發生如下的 錯誤:
  59. 59. 繼承多個同名的成員變數  您必須在程式中明確的冠上介面的名稱 , 才能讓編譯 器知道您所指的到底是哪一個 i :
  60. 60. 繼承多個同名的成員變數
  61. 61. 繼承多個同名的成員變數
  62. 62. 實作多重介面  單一類別也可以同時實作多個介面 , 這時會引發的問 題就如同單一介面繼承多個介面時一樣 , 請參考前面 的討論即可。
  63. 63. 介面與類別的關係  就實務面來看 , 介面其實就是一種特殊的抽象類別 , 因此假設 A 類別實作了 B 介面 , 那麼我們也可將 B 介面當成是 A 的父類別來使用 , 例如:
  64. 64. 介面與類別的關係  而這二者的差異 , 則在於介面的設計比抽象類別嚴格 許多: ▪ 介面的方法均為 public abstract, 因此只能定義沒有實 作內容的公開抽象方法。 ▪ 介面的變數均為 public static final, 因此只能宣告公開 常數 , 而且必須在宣告時即指定初值。 ▪ 介面只能繼承其他介面 , 而不能繼承類別。
  65. 65. 12-4 內 部類別 (Inner Class)  內 部類別 (Inner Class) 也稱為巢狀 類別 (Nested Class), 主要是指『定義在類別之內』的類別。依照 內部類別的用途可分為以下 4 種: 1. 一般內 部類別:就是一般直接定義在類別之內的類別 。 2. 方法內 部類別 (Method Inner Class) :定義在方法 內的內部類別。
  66. 66. 內 部類別 (Inner Class) 3. 匿名類別 (Anonymous Inner Class) :有點類似匿名 陣列的寫法 , 主要是用來臨時定義一個特定類別 ( 或 介面 ) 的匿名子類別 , 並用以產生物件來使用。 4. 靜態內部類別 (Static Inner Class) :就是加上 static 的內部類別 , 其特色就和一般的 static 成員一樣 , 可以直接透過外部類別的名稱來存取 , 而不用先建立 外部類別的物件。
  67. 67. 12-4-1 一般內 部類別  如果 A 類別只需配合 B 類別來使用 , 那麼就可將 A 定義為 B 的內部類別。  其最大的好處 , 就是內部類別可以直接存取外部類別 的所有成員 , 包括 private 成員在內。  底下來看範例。
  68. 68. 一般內 部類別
  69. 69. 一般內 部類別
  70. 70. 一般內 部類別  由於在內部及外部類別都宣告了 j 、 k 變數 , 因此 在內部類別中要存取外部的 j 、 k 時 , 必須加上外 部類別的名稱才行。  不過只有靜態變數才能直接以類別名稱 (Outter.k) 來存取 , 實體變數則必須以 Outter.this.j 的寫法來 存取 ( 參見第 9 、 10 行 ) 。 ▪ this 是參照到目前物件 , super 是參照到父物件 , 而 『 外部類別名稱 .this 』 則可用來參照到外部物件。
  71. 71. 一般內 部類別  請注意 , 內部及外部類別仍為 2 個不同的類別 , 因 此在外部類別的方法中 , 必須先建立內部物件 , 然後 才能存取內部物件中的變數及方法 , 例如以上的 16 行。  此時所建立的內部物件 , 會附屬在呼叫此方法的外部 物件之中 ( 二者會自動建立從屬的連結關係 ) 。
  72. 72. 一般內 部類別  然而 , 若是在其他類別的方法中 ( 或在外部類別的靜 態方法中 ) , 由於並不存在外部物件 , 因此必須先建 立外部物件 , 然後再利用外部物件來建立內部物件才 行 , 例如以上的 23 、 24 行。  如果這二行所建立的物件不會再用到 , 那麼也可使用 以下 2 種懶人寫法:
  73. 73. 一般內 部類別  以上是先用 new Outter() 建立外部物件 , 然後再串 接 .new Inner() 來建立內部物件;若還要再呼叫內 部物件的方法 , 則再以 . 來串接。  值得注意的是 , 內部類別由於在其他類別中看不到 , 因此必須加上外部類別的名稱來指明 , 例如 Outter.Inner 。
  74. 74. 內 部類別在編譯後所產 生的 .class 檔  Java 程式在編譯之後 , 每個類別都會產生一個與類 別同名的 .class 檔。  內部類別也不例外 , 但會在檔名前加上外部類別的名 稱以及 $ 符號 , 例如前面的範例會產生以下 3 個檔 案: ▪ Outter.class 、 Outter$Inner.class 、 TestInner1.class
  75. 75. 內 部類別在編譯後所產 生的 .class 檔  此外 , 一個類別之中可以有多個內部類別 , 而內部類 別之內也還可以再有內部類別 , 這些類別在編譯之後 , 每個類別都會產生一個 .class 檔案。  如果是多層的內部類別 , 則編譯後的檔名就會由外而 內一層層以 $ 串接起來 , 不過一般很少人會寫得這 麼複雜。
  76. 76. 12-4-2 方法內 部類別 (Method Inner Class)  如果類別只在某個方法中臨時需要 , 那麼就可將之定 義在方法中 , 而成為方法內 部類別。例如:
  77. 77. 方法內 部類別 (Method Inner Class)  以上程式在 main() 中定義了一個方法內部類別 Inner, 然後用它來建立物件並呼叫其 print() 方法 。
  78. 78. 方法內 部類別 (Method Inner Class)  在使用方法內部類別時 , 有以下幾點要注意: 1. 必須先定義好類別 , 然後再用它來建立物件 ( 就是 定義要寫在 new 之前 ) 。 2. 在方法內部類別中 , 可以直接存取外部類別的成員 , 這點和一般內部類別相同。 但如果是靜態方法中的內部類別 , 則只能存取外部 類別中的靜態成員 ( 參見範例第 2 行 ) ;這是因為 靜態方法在執行時並沒有類別的實體 ( 物件 ) , 因此 也只能存取和物件無關的靜態成員。
  79. 79. 方法內 部類別 (Method Inner Class) 3. 在方法內部類別中若要存取所屬方法的區域變數 , 則 只能存取以 final 宣告的區域變數 ( 參見範例第 6、7 行)。 這是因為一般區域變數在方法結束時即消失 , 而內部 物件則可能繼續存在 ( 例如將參照傳到其他方法中 ), 因此只能存取不會消失的 final 變數。 4. 方法內部類別只能用 abstract 或 final 修飾 , 而不 可使用其他字符 ( 如 private 、 protected 等 ) 。
  80. 80. 方法內 部類別 (Method Inner Class) ▪ 在任何情況下 , abstract 或 final 都不可一起使用 , 因 為二者的意義完全相反:前者代表必須被更改 , 而後 者代表不允許被更改。 ▪ 以上程式的 Inner 類別 , 編譯後的檔名為 InnerMethod$1Inner.class, 其中的 1 是流水號。如 果外部類別的其他方法中也有 Inner 類別 , 則會編譯 成不同的流水號。
  81. 81. 12-4-3 匿名類別 (Anonymous Inner Class)  匿名類別主要是用來臨時定義一個某類別 ( 或介面 ) 的子類別 , 並用以產生物件;由於該子類別用完即丟 , 所以不需要指定名稱。  底下是一個簡單的範例。
  82. 82. 匿名類別 (Anonymous Inner Class)
  83. 83. 匿名類別 (Anonymous Inner Class)  在第 7 行 Face c = new Face( ); 最後的分號之前 , 我們插入了一段 { ... } 的類別定義 , 如此就會建立一 個匿名子類別的物件 , 並讓父類別的變數 c 來參照 。 ▪ 匿名類別在編譯後的檔名為『外部類別 $ 流水 號 .class 』 , 例如在本例中為 InnerAnony$1.class 。
  84. 84. 匿名類別 (Anonymous Inner Class)  使用匿名類別的時機 , 通常是在想要臨時變更 ( 重 新定義 ) 某新物件的方法 , 但只要變更一次的情況 ;以本例來說 , 就是改變 smile() 方法所輸出的字 串。  匿名類別的使用規範就和定義一般子類別相同 , 只 不過匿名類別沒有名字而已 , 底下再補充幾點注意 事項: 1. 在敘述中使用匿名類別時 , 最後必須加上分號才行 ( 參見上面程式第 10 行 ) 。
  85. 85. 匿名類別 (Anonymous Inner Class) 2. 抽象類別或介面也可用來建立匿名類別 , 但在匿名類 別中必須實作所有未實做的方法。 3. 在匿名類別中也可以宣告新的變數或方法 , 但由於所 產生的物件會交由父類別的變數來參照 , 所以透過該 參照也只能存取父類別所擁有的成員 , 而不能存取子 類別新增的成員 , 否則會編譯錯誤 , 例如。
  86. 86. 匿名類別 (Anonymous Inner Class)
  87. 87. 匿名類別 (Anonymous Inner Class) ▪ 雖然抽象類別及介面都不能用來建立物件 ( 例如 new Movable(); 會編譯錯誤 ), 但卻可用來宣告變數以參照 其子物件 ( 例如上面的第 5 行 ) 。  匿名類別除了可使用在一般的敘述中 , 也可用在傳遞 參數上 , 例如。
  88. 88. 匿名類別 (Anonymous Inner Class)
  89. 89. 匿名類別 (Anonymous Inner Class)  以上在第 8 行呼叫 myMove 時 , 就利用匿名類別 臨時建立一個 Movable 的子物件做為參數。  而在 myMove() 方法中執行 m.move() 時 , 就會執 行到匿名陣列中所實做的 move() 方法。 ▪ 由於 main() 是靜態方法 , 而在靜態方法中只能存取 靜態的變數或方法 , 因此 myMove() 也必須宣告為 static 方法 , 才能在 main() 中被呼叫。 ▪ 匿名類別若是用在參數傳遞上 , 則會放在呼叫方法的 () 之中 , 因此最後別忘了加上 ");" 做為結束 ( 例如上 面第 11 行 ) 。
  90. 90. 12-4-4 靜態內 部類別 (Static Inner Class)  靜態內 部類別 (Static Inner Class) 就是加上 static 的內部類別。其特色就和一般的 static 成員一樣 , 可以直接透過『外部類別名稱 . 內部類別名稱』來存 取內部類別中的靜態成員 , 或是直接建立內部物件 , 而不用先建立外部物件。例如:
  91. 91. 靜態內 部類別 (Static Inner Class)
  92. 92. 靜態內 部類別 (Static Inner Class)  在以上的靜態內部類別中 , 分別宣告了實體及靜態的 成員 , 然後在 main() 中即可利用類別名稱 『 Outter.Inner 』來直接建立內部物件 , 或以此名稱 來存取內部類別的靜態成員。  而這些操作都完全不需要透過外部物件 , 這就是 static 的特性。 ▪ 請注意 , 只有在靜態內部類別中才能宣告靜態成員 ( 變數或方法 ), 在其他非靜態的內部類別中則不可以 宣告靜態成員。
  93. 93. 12-5 enum 列舉型別  列舉型別是指該型別的值只能是列舉出來的幾種 , 而 不可以是其他未列出的值。  例如我們可以建立一個 Week 列舉型別 , 並讓其值 只允許有 Sunday 、 Monday...Saturday 等 7 種列 舉值。
  94. 94. 12-5-1 列舉的宣告與使用  enum 是 Java 5.0 版才新增的功能 , 專門用來定義 一種稱為『列舉型別』的特殊類別。  例如我們需要一個代表紙杯大小的類別 , 而其尺寸只 有 Small 、 Medium 、 Large 三種 , 就可使用 enum 來定義 , 並限制其值只能有 3 種:
  95. 95. 列舉的宣告與使用
  96. 96. 列舉的宣告與使用  在第 1 行定義了只允許 3 種值的列舉型別 CupSize, 並在 main() 中用它來宣告 2 個列舉變數 。  當要指定列舉值時 , 則必須加上型別名稱或物件參照 , 例如 CupSize.Medium 或 c.Large 。列舉值在輸 出為字串時 , 則會自動轉換為列舉的名稱 , 例如 CupSize.Medium 就會輸出為 "Medium" 。
  97. 97. 12-5-2 列舉型別其實是類別  enum 列舉型別其實就是一種特殊的類別 , 因此前面 程式中的 enum CupSize 型別 , 在編譯後會產生 CupSize.class 類別檔。  事實上 , 編譯器在編譯『 enum CupSize { Small, Medium, Large } 』時 , 會自動將之改為繼承 Enum 抽象類別的 public final 類別 , 如下所示。
  98. 98. 列舉型別其實是類別
  99. 99. 列舉型別其實是類別  由此可知 , 每個列舉值其實都是一個實體物件 , 其內 容包含了列舉名稱字串 ( 例如 "Small") 及列舉順序 ( 由 0 開始 ) 的資訊。而以下程式:  在執行後 , 變數 a 會就參照到內容為 ("Small", 0) 的 CupSize 物件了。  此時 a 、 a.Small 、及 CupSize.Small 的值都相等 , 因為都是參照到同樣的物件。
  100. 100. 列舉型別其實是類別  在了解列舉型別與類別的關係之後 , 再來看看幾點 注意事項: 1. 在定義列舉型別時 , 敘述的後面不用加分號 ( 要加當 然也可以 ), 例如以下都是正確的寫法: 2. 列舉型別不可使用 new 來建立新物件 ( 因為其建構 方法為 private, 只能在類別內部建構 ) , 所以列舉型 別的變數只能參照現有的列舉物件。
  101. 101. 列舉型別其實是類別 3. 列舉型別也可以定義在類別之內 , 而成為內部類別 , 此時編譯器會自動加上 static final 而成為靜態內部 類別 ( 這樣就可以和外部物件脫離關係了 ) 。請注意 , 列舉型別不允許定義在方法之內。底下是個簡單的 範例:
  102. 102. 列舉型別其實是類別 4. 列舉型別若定義在其他類別之外 , 就只能宣告為 public 或預設存取控制 , 而不能是 private 或 protected 。但若是定義在其他類別的內部 , 則不受 此限制。 ▪ 此條規則其實也適用於所有的類別 ( 所有定義在最外 層的類別都不能是 private 或 protected , 因為沒有 意義 ) 。
  103. 103. 列舉型別其實是類別 5. 列舉型別不能再繼承其他的類別了 , 因為編譯器會自 動將之改為繼承 Enum 類別。 ▪ 列舉型別可以實做介面 , 例如 enum CupSize implements Sealable { ... }, 但必須在 enum 中實做 介面的方法 (12-5-5 節 TTIIPP 會介紹如何在 enum 中加入自訂的方法及變數 ) 。
  104. 104. 12-5-3 列舉型別內 建的幾個方法  編譯器在改寫列舉型別時 , 還特別提供了以下幾個實 用的方法:
  105. 105. 列舉型別內 建的幾個方法
  106. 106. 列舉型別內 建的幾個方法  以上的 CupSize.values() 會傳回所有列舉值的陣列 , 您也可宣告一個變數來參照 , 例如:『 CupSize[] cs = CupSize.values(); 』。
  107. 107. 12-5-4 用 switch 來判斷列舉值  在第 5 章介紹 switch 時 , 曾說 switch 只能用來判 斷整數類的資料 ( 只支援 byte 、 char 、 short 、 int, 而且在比對時一律會先 轉型為 int) 。  由於列舉資料非常適合用 switch 來判斷 , 而且也可 用 ordinal() 方法取得其整數值 , 因此 Java 特別加 以支援 , 請看以下的範例。
  108. 108. 用 switch 來判斷列舉值
  109. 109. 用 switch 來判斷列舉值
  110. 110. 用 switch 來判斷列舉值  以上程式定義了一個可將 CupSize 轉為 cc 數的靜 態方法 , 然後在 main() 中用來進行轉換並輸出結果 。  請注意第 6~8 行的 case 後面要使用不加型別名稱 的列舉值 , 例如 Small 而非 CupSize.Small, 否則 會編譯錯誤。
  111. 111. 12-5-5 在 enum 中加入自訂方法與變數  由於 enum 也是一種類別 , 因此在必要時也可以加 入一些自訂的建構方法、一般方法、或變數。  就以前面將尺寸轉換為 cc 數的例子來說 , 如果能將 cc 數的資料內建在 CupSize 列舉物件中 , 是不是 比較符合物件導向的精神呢?  底下我們就來實做。
  112. 112. 在 enum 中加入自訂方法與變數
  113. 113. 在 enum 中加入自訂方法與變數
  114. 114. 在 enum 中加入自訂方法與變數  首先請看第 2 行 , 在 enum 中必須將列舉值串列寫 在最前面 , 並以分號結束。列舉值之後若有加小括號 及參數 , 則會呼叫自訂的建構方法來建立物件。  第 6 行就是我們自訂的建構方法 , 可接受一個 cc 的參數 ( 其實在編譯時還會偷偷加上「列舉值名稱」 及「順序」參數 , 這些就不用我們操心了 ) 。 ▪ 在 enum 中的建構方法一律會設為 private, 以防止 我們使用 new 來建立新的列舉值。
  115. 115. 在 enum 中加入自訂方法與變數  另外 , 在 enum 中還定義了 getCc() 方法 ( 第 7 行 ), 以便在 main() 中呼叫以取得每一個列舉值的 cc 數。  最後 , enum 還有一個絕招 , 就是可以在列舉值之後 定義其專屬的方法 , 以便重新定義 enum 中的方法。 例如我們將前面程式的第 2 行改為:
  116. 116. 在 enum 中加入自訂方法與變數  由於在 Medium 後的 { } 中重新定義了 getCc() 方 法 , 因此輸出結果會變成: ▪ 列舉值的專屬方法會自動設為靜態方法 , 因此在方法 內只能存取靜態成員。
  117. 117.  12-A 撰寫通用於多種類別的程式  12-B 擔負物件之間的溝通角色
  118. 118. 1. Given: Which class that use Animal class will compile? (Choose four. )
  119. 119. A. B. C. D. E. F. G. abstract class Dog implements Animal { private int Age; } abstract class Dog extends Animal { public void run() { }; } abstract class Dog extends Animal { private int Age; } class Dog extends Animal { private int Age; } class Dog extends Animal { public void run() { ;; } } class Dog extends Animal { public void setName() { } } abstract class Dog extends Animal { public void setName() { } } H. abstract class Dog extends Animal { public void run(); }
  120. 120. 2. Given : Which code inserted individually at line 6, will make use of polymorphism? (Choose all that apply.) A. void call(I x) { x.set(); } B. void call(A x) { x.set(); } C. void call(B x) { x.set(); } D. void call(C x) { x.set(); } E. void call(A x, B y) { x.set(); y.set(); } F. void call(C x, C y) { x.set(); y.set(); }
  121. 121. 3. Drag and drop code s into empty boxes to complete the C interface .
  122. 122. 4. Given:
  123. 123. Which statements are true ? (Choose three . ) A. This code will output A. B. This code will output B. C. If remove line 6 and 7, This code will not compile. D. If remove line 9 and 10, This code will not compile. E. If remove line 6 and 7, This code will output B. F. If remove line 9 and 10, This code will output A.
  124. 124. 5. Given: Which statement, insert at line 3, will compile? A. CupSize c = Medium; B. A.CupSize c = CupSize.Medium; C. A.CupSize c = A.CupSize.Medium; D. A.CupSize c = new Medium(); E. A.CupSize c = new CupSize.Medium(); F. A.CupSize c = new A.CupSize.Medium();
  125. 125. 6. Which statement is true? (Choose all that apply. ) A. A protected static method can be overridden by it's subclass. B. A protected final method can be overridden by it's subclass. C. A private final method can be re-defined by it's subclass with the same signature. D. A private static method can be called only within the static method in the same class. E. A non-static method can NOT be called within any static method. F. A abstract final method can be implemented only within abstract class. G. A public static method can be called by it's subclass without explicitly referencing the superclass.
  126. 126. 7. Given: Which one, inserted at line 5, will create an Tire object? A. Tire t = new Tire(); B. Tire t = new Car().new Tire(); C. Car.Tire t = new Car.Tire(); D. Car.Tire t = new Car().new Tire(); E. It's impossible to instanciate Tire class at line 5.
  127. 127. 8. Given: Which two statements are true? A. Compilation will fail at line 1. B. Compilation will fail at line 2. C. Compilation will fail at line 4. D. Compilation will fail at line 8. E. Compilation will fail at line 9.
  128. 128. 9. Given: Which code, inserted at line 7, will cause an Exception at runtime? A. A x = b; B. A x = (A)b; C. A x = (B)(A) b; D. I x = (A) b; E. I x = (C) b;
  129. 129. 10. Given:
  130. 130. Which statement is true? A. Compilation fails at line 3. B. Compilation fails at line 5. C. Compilation fails at line 7. D. ((D)new E( )).b( ) will invokes the version defined at line 4. E. ((D)new E()).b() will invokes the version defined at line 7.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×