Download presentation
Presentation is loading. Please wait.
1
物件導向程式設計 參考書目: 1. 洪維恩,C++ 教學手冊 第三版,旗標出版圖書公司。 2. 陳錦揮,Java 初學指引,博碩文化 3
物件導向程式設計 參考書目: 1. 洪維恩,C++ 教學手冊 第三版,旗標出版圖書公司。 2. 陳錦揮,Java 初學指引,博碩文化 3. 施威銘研究室著,最新 Java 2 程式語言 第 2 版,旗標出版社。 Java 繼承 Chih-Hung Wang
2
類別繼承的目的 物件導向是為了在開發大型程式時,能夠更有效率而發 展的一套技術。因此在物件導向程式設計中,如何讓相 同的程式碼重複被利用,是相當重要的。在Java程式裡, 為了讓每一行程式都具有高利用價值,而採用了類別繼 承的觀念來實現此一精神。並且,類別繼承能夠讓程式 的模組化更便於管理和閱讀。
3
類別繼承的目的 舉例來說,假設我們有一個滑鼠類別,當中包含了滑鼠 的元件及行為模式(例如移動method、按一下method等 等),假設它是傳統的滾球滑鼠, 則我們可以在設計新型態的滾輪及光學滑鼠時採用繼承技巧 假設命名為光學滾輪滑鼠類別,則該類別可以先繼承滑鼠類別 然後加上新的成員-『光學元件』、『滾輪元件』 並且修改原本的移動method 以及新增滾輪瀏覽method。 而其他如按一下method則不需要變動 這樣一來,我們就省去了開發按一下method的時間,因為它已經是 父類別(滑鼠類別)的既有Method。
4
一般類別的繼承 Java的類別繼承可以分為一般類別的繼承與介面類別的繼 承,由於尚未介紹介面類別,因此本節先介紹一般類別 的繼承。 單一繼承
【註】 C++支援多重繼承(一個類別擁有多個父類別稱為多重繼承),但Java不 直接支援多重繼承。
5
一般類別的繼承 單一繼承 衍生類別與基底類別
類別的繼承非常類似類別的複製,但並不完全是複製,所以我們不將 之稱為類別的複製,而稱之為類別的繼承(inheritance)。 事實上,不論是『單一繼承』或『多層繼承』,『繼承』都正如其字 面上的意義一般,因此具有階層特性。 就『繼承』的字面意義而言,一般人很容易聯想到『誰繼承了誰』這個問 題,就現實社會而言,一般都是『子繼承父』;在Java程式設計的單一繼 承中,也是相同的道理。 我們將被繼承者稱為父類別,繼承者稱為子類別。『子』為何會繼承 『父』呢?當然是因為『父』生『子』的緣故。而在本書中,我們將 『生』這個動作特別稱為『衍生』(derive)。
6
一般類別的繼承 單一繼承 在Java中,宣告『單一繼承』的子類別定義語法如下:
因此,在Java的單一繼承中,我們通常稱被衍生的類別為父類別 (parent class)或基底類別(base class)。而繼承衍生而來的類別稱之為子 類別(child class)或衍生類別(derived class),父類別和子類別有其先後 和相似的關係。 在Java中,宣告『單一繼承』的子類別定義語法如下: 語法說明: (1)父類別可以是使用者自訂的類別,也可以是Java提供的類別,但使用Java 提供的類別時,必須將其import進來。 class 衍生(子)類別名稱 extends 基底(父)類別名稱 { 子類別實作區 }
7
一般類別的繼承 單一繼承 (2)宣告『單一繼承』的子類別時,必 須指定父類別的名稱。並且其中使用 了關鍵字extends,extends的原文具 延伸之意,故繼承之後,除了父類別 的既有成員之外,子類別還可以定義 更多的成員。 (3)子類別除了繼承父類別,也可以另 外再定義一些屬於自己的成員變數和 函式(只要記錄於上述語法的子類別 實作區中即可),因此父子類別之間 的關係如圖8-1所示。 (4)繼承自父類別的成員其封裝等級同 父類別原本之封裝等級(含package, public, protected)。但private等級則 無法由子類別直接取用,必須透過父 類別其他等級的成員取用。 父子類別之間的關係
8
一般類別的繼承 單一繼承 (5)如果有某些成員不希望公開給外部程式取用,但希望繼承者仍可使用, 應該宣告為protected等級,但仍需搭配不同package來限制。 (6)建構子無法被繼承。 透過繼承機制,當我們需要重複使用某個類別裡的一些成員時,我們 不需要重複撰寫類似的程式碼,我們只需要衍生出一個新的子類別, 取得其基本功能和成員,再針對需求加以新增或修改即可。這也就是 重複程式碼的精神所在。
9
一般類別的繼承 單一繼承 假設有一個類別CX為父類別,我們想要利用這個父類別去衍 生出一個子類別CY,則如下片段程式:
類別CY欲繼承類別CX的成員,則類別CY的定義應該如下: class CX { //CX類別的成員定義 } class CY extends CX { //CY類別新定義的成員 }
10
一般類別的繼承 單一繼承 【觀念範例8-1】:單一繼承語法的基本練習。
範例8-1:ch8_01.java(隨書光碟 myJava\ch08\ch8_01.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* 檔名:ch8_01.java 功能:單一繼承 */ package myJava.ch08; import java.lang.*; import java.io.Console; //載入Console類別,JDK6新增功能 public class ch8_ //主類別 { public static void main(String args[]) CA objA = new CA(); CB objB = new CB();; //objB是子類別物件 System.out.println("輸入類別CA的objA物件資料"); objA.inputX(); objA.inputY(); objA.inputZ();
11
一般類別的繼承 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 System.out.print("類別CA的objA物件:"); objA.show(); System.out.println(" "); System.out.println("輸入類別CB的objB物件資料"); objB.inputX(); objB.inputY(); objB.inputZ(); objB.varX++; //objB.varY++; //外部程式無法存取protected等級資料,目前可以 objB.addOne(); System.out.print("類別B的objB物件:"); objB.show(); } class CA { Console console = System.console(); public int varX; protected int varY; private int varZ; public void inputX() System.out.print("varX:"); varX=Integer.parseInt(console.readLine());
12
一般類別的繼承 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 } public void inputY() { System.out.print("varY:"); varY=Integer.parseInt(console.readLine()); public void inputZ() System.out.print("varZ:"); varZ=Integer.parseInt(console.readLine()); public void show() System.out.println("varX=" + varX + " varY=" +varY + " varZ=" + varZ); class CB extends CA // 類別CB繼承自類別CA public int varW; //新增的成員變數 public void addOne() //新增的成員函式
13
一般類別的繼承 執行結果: 輸入類別CA的objA物件資料 varX:10 varY:10 varZ:10
69 70 71 72 73 varX++; varY++; //varZ++; //無法存取父類別private等級資料 } 輸入類別CA的objA物件資料 varX:10 varY:10 varZ:10 類別CA的objA物件:varX=10 varY=10 varZ=10 輸入類別CB的objB物件資料 varX:20 varY:20 varZ:20 類別B的objB物件:varX=22 varY=21 varZ=20
14
Practice 1 定義一個類別 CA,包含一個成員函式 prime(int X), 用來求出小於 X 的質數。另定義一個 CA 的子類別 CB,在類別 CB 中另外新增一個輸出成員函式 output(),該函式可將 prime (int X) 求出的質數輸出。
15
一般類別的繼承 單一繼承 範例說明: (1)第34~62行是父類別的定義。第64~73行是子類別的定義。繼承之 後,子類別又新增了兩個成員varW與addOne()。下表是CB子類別的 成員及其等級。 等級 CA父類別的成員 CB子類別的成員 package Console console public int varX protected int varY private int varZ 無法直接存取 void inputX() void inputY() void inputZ()
16
一般類別的繼承 單一繼承 下表是CB子類別的成員及其等級。(續)
(2)第27行是透過外部程式存取objB的varY成員變數,若外部程式與宣 告protected成員的CA類別位於不同的package,則無法存取。不過此 時是可以存取的,您若將註解取消就可以發現仍舊是可以編譯的,我 們將於第11章講解類別庫時,重新探討這個案例。 等級 CA父類別的成員 CB子類別的成員 public 無 int varW void addOne() CA()預設建構子 無法繼承 CB()預設建構子
17
一般類別的繼承 單一繼承 父子類別的建構子執行順序
(3)varZ在父類別中,被宣告為private等級,所以在被繼承後無法被直 接存取,因此第71行無法透過子類別的成員函式存取varZ成員變數( 註解不可取消)。但varZ仍舊可以被繼承而來的父類別inputZ、show 等函式所存取。 父子類別的建構子執行順序 在建立子類別物件時,會先執行父類別的建構子,然後才執行子類別 的建構子。並且如果您未建立建構子時,將會執行預設建構子。 通常,我們會將父類別成員的初始化放在父類別建構子中完成,因為 建立子類別物件時,會先自動執行父類別建構子。
18
一般類別的繼承 單一繼承 【觀念範例8-2】:父子類別的建構子執行順序。
範例8-2:ch8_02.java(隨書光碟 myJava\ch08\ch8_02.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* 檔名:ch8_02.java 功能:繼承的建構子執行順序 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CB objB = new CB();; //objB是子類別物件 objB.show(); } class CA
19
一般類別的繼承 執行結果: 父類別建構子執行中.... 子類別建構子執行中.... varX=10 19 20 21 22 23 24 25
26 27 28 29 30 31 32 33 34 35 36 37 protected int varX; public CA() { System.out.println("父類別建構子執行中...."); varX=10; } class CB extends CA // 類別CB繼承自類別CA public CB() System.out.println("子類別建構子執行中...."); public void show() System.out.println("varX=" + varX); 父類別建構子執行中.... 子類別建構子執行中.... varX=10
20
一般類別的繼承 單一繼承 super關鍵字 範例說明: 事實上,子類別繼承父類別後,新成員命名時,也可以和父類 別成員同名
由執行結果中,可以看出在建立子類別物件時,會先執行父類別的建 構子,然後才執行子類別的建構子,同時,我們在父類別內對成員變 數作了初始化動作,所以在子類別物件中的varX也是10。 super關鍵字 事實上,子類別繼承父類別後,新成員命名時,也可以和父類 別成員同名 如果是函式同署名,則稱為改寫(override),我們後面將會介紹。
21
一般類別的繼承 super關鍵字 在Java中,this代表的是參考物件本身,super代表的則是參考 父類別物件本身
如果是變數同名,則不會將父類別的變數覆蓋,而是將繼承而來的父 類別同名成員變數隱藏(hide),所以是兩者獨立的兩個變數。 要取用子類別的同名變數,就如同一般撰寫程式般取用即可。 而要取用父類別物件的變數,可以透過super關鍵字來達成。 在Java中,this代表的是參考物件本身,super代表的則是參考 父類別物件本身 如果要讀取父類別某個成員,可以使用「super.父類別成員」語法來 完成,但不可以使用這種方式在子類別中讀取父類別的private成員。 而如果想要在子類別的建構子中執行父類別的建構子,則可以使用「 super(引數串)」來完成,不過它必須放在子類別建構子的第一行才行 。
22
一般類別的繼承 super關鍵字 【觀念範例8-3】:子類別變數與父類別變數名稱相同。
範例8-3:ch8_03.java(隨書光碟 myJava\ch08\ch8_03.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* 檔名:ch8_03.java 功能:super的練習 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CB objB = new CB(10); //objB是子類別物件 objB.show(); }
23
一般類別的繼承 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class CA { public int varX; public CA() System.out.println("父類別無引數建構子執行中..."); } public CA(int i) varX=i; class CB extends CA // 類別CB繼承自類別CA public int varX; //新增的成員變數,與父類別成員同名 public CB(){} public CB(int i) super(i); //呼叫父類別建構子public CA(int i),必須放在第一行 varX=100; public void show() //新增的成員函式
24
一般類別的繼承 執行結果: 範例說明: (1)CB繼承CA,並且新增一個同名的變數varX。第37、42行取用的是 子類別的變數varX,第41、43行取用的是父類別的varX。 (2)第36行是呼叫第24~27行的父類別建構子。由執行結果中,可以發 現,無引數的父類別建構子並不會被執行,因為編譯器發現您在子類 別的建構子中,已經呼叫父類別的建構子。如果將第36行刪除,則第 20~23行的父類別建構子會自動被執行。 41 42 43 44 45 super.varX++; System.out.println("子類別varX=" + varX); System.out.println("父類別varX=" + super.varX); } 子類別varX=100 父類別varX=11
25
一般類別的繼承 super關鍵字 範例說明:
(3)如果您同時將第36行刪除,也將第20~23行的父類別建構子刪除, 則編譯時會發生錯誤,因為編譯器想要執行一個無引數的建構子,卻 發現找不到。還記得上一章曾經提及,一但我們建立的任何一個建構 子,則預設的建構子將不會出現嗎!
26
一般類別的繼承 super關鍵字 改寫(Override)
在範例8-3中,成員變數在被繼承後仍舊可以另行宣告一個專屬於子 類別的同名成員變數,那麼如果是同「署名」成員函式的話,情況又 會是如何呢? 答案是發生改寫(override)現象。當子類別欲改寫父類別的成員函式時,有 以下幾點必須注意: 1.署名雖然不包含方法的封裝等級,但在改寫時,只能擴大封裝等級,而不能縮 小封裝等級。例如protected可在改寫時,改為public,但相反則否。 2.改寫的回傳值型態只能是相同或原本資料型態的子類別型態。(在JDK5.0之前 ,則完全不允許改變回傳值型態) 3.父類別的static方法無法被改寫。但我們確實可以在子類別中定義一個新的同名 static方法,但那並非改寫,而是兩個不相關的函式。
27
一般類別的繼承 super關鍵字 改寫(override)與多載(overload)
1.多載的成員函式必須位於同一個類別視野。而改寫的兩個成員則分處於 不同的類別視野,一個位於父類別,另一個位於子類別。 2.多載必須是同名但不同署名。而改寫則必須是同署名。 例如,如果類別A有一個成員函式func(),而類別B繼承自類別A,同時又定義了 一個成員函式func(int X),則兩者並沒有多載或改寫現象,但這還是允許的,因 為編譯器可以輕鬆地辨別所呼叫的對象。 【註】: 事實上,改寫最常發生在abstract函式,我們將於後面章節說明。
28
一般類別的繼承 super關鍵字 改寫(override)與多載(overload) 【觀念範例8-4】:改寫(Override)的練習。
在改寫的狀況下,我們必須告知編譯器,目前要呼叫的是子類別物件 的同名成員函式還是父類別物件的同名成員函式 若是呼叫子類別物件的同名函式,則只要直接呼叫即可 若是呼叫父類別物件的同名函式,則必須透過super來完成。 請看下一個範例的示範。 【觀念範例8-4】:改寫(Override)的練習。 範例8-4:ch8_04.java(隨書光碟 myJava\ch08\ch8_04.java)
29
一般類別的繼承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch8_04.java 功能:改寫(override)的練習 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CB objB = new CB(); //objB是子類別物件 objB.show(); objB.show(10); objB.show(10.0); } class CA public void show() System.out.println("目前執行父類別的show()");
30
一般類別的繼承 執行結果: 目前執行子類別的show() 目前執行子類別的show(10) 目前執行子類別的show(double)
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 } class CB extends CA // 類別CB繼承自類別CA { public void show() System.out.println("目前執行子類別的show()"); public void show(int x) System.out.println("目前執行子類別的show(" + x +")"); public void show(double x) System.out.println("目前執行子類別的show(double)"); super.show(); 目前執行子類別的show() 目前執行子類別的show(10) 目前執行子類別的show(double) 目前執行父類別的show()
31
一般類別的繼承 super關鍵字 範例說明: (1)第30~33行的show()改寫了第21~24行的show()成員函式。
(2)第30行與第21行的成員函式為改寫(Override)的關係。第30行、第 34行、第38行的成員函式為多載(Overload)的關係。第34行與第21行 的成員函式既非多載也非改寫的關係。 (3)第41行為執行父類別物件的show()函式,故由此可以看出改寫並非 將原本的函式覆蓋,可以將其視為隱藏的現象。 (4)您可以將第21行的public改寫為protected,程式仍是合法的,因為 改寫時,允許擴大封裝等級。
32
Practice 2 定義一個類別 CA,包含一個成員函式 func(int X) 用 來印出 X 的 3 次方,另外定義一個 CA 的子類別 CB ,在類別 CB 中改寫 func(int X)函式,將函式功能改成 求得並印出 X!。
33
一般類別的繼承 繼承機制之物件實體建構順序 由之前所介紹的建構子執行順序,我們可以將單一繼承的物件 實體建構順序繪製如下圖:
子類別物件實體產生過程
34
一般類別的繼承 繼承機制之物件實體建構順序
在前一章我們介紹物件實體的內容時,曾經提及,對於非static的成 員變數而言,物件實體產生後,將有一個記憶體存放專屬於該物件實 體的成員變數,而對於非static的成員函式而言,則會存放成員函式 的程式進入點。 如果類別在定義時,採用的是繼承機制,則產生類別的物件實體時, 如圖8-2所示,物件實體內會包含父類別的物件實體,這也是為何父 類別的成員變數即使與子類別的成員變數同名,也不會被覆蓋的原因 , 因為兩者在物件實體產生後,會在物件實體內佔據不同的記憶體位置。所 以當變數同名時,仍可以由super存取到其中的父類別物件實體的變數。
35
一般類別的繼承 多層繼承 多層繼承屬於垂直性的層代繼承,例如,子類別繼承父類別, 父類別繼承祖父類別。由於多層繼承的機制,使得Java在開發 大型程式時,能夠更有效率並且更容易管理。 前面所介紹的單一繼承,僅討論父類別與子類別的兩代繼承關 係,若將其層代擴充到三代以上,就形成了多層類別的繼承 在多層繼承中,類別的繼承具有先後關係,所有下層的類別都具有最 底層基底類別的功能,而越下層的類別將繼承越多的成員(因為每一 層都可以新增一些成員),如下圖所示。
36
一般類別的繼承 多層繼承 多層繼承示意圖
37
一般類別的繼承 多層繼承 當進行多層繼承時,越下層的物件實體將會越變越大,因為, 下層物件實體內將包含所有上層的物件實體。而建構子的執行 順序,也是由最上層的建構子開始執行。 事實上,您可以將建構子的執行看作是一種引發的關係 在兩層繼承之時,子類別的建構子會先引發父類別的建構子(引發可 分為自動及手動,手動時super(引數串)呼叫必須放在第一行),所以 父類別的建構子會先執行,然後才執行子類別的建構子(手動時,則 執行子類別建構子第2行以後的敘述)。
38
一般類別的繼承 多層繼承 我們透過一個範例來說明多層類別的建構子執行順序。 【觀念範例8-5】:多層繼承的建構子執行順序。
在三層繼承時,則子類別的建構子會先引發父類別的建構子,而父類 別的建構子則會引發祖父類別的建構子,所以祖父類別的建構子會先 被執行,然後是父類別的建構子,最後是子類別的建構子。 我們透過一個範例來說明多層類別的建構子執行順序。 【觀念範例8-5】:多層繼承的建構子執行順序。 範例8-5:ch8_05.java(隨書光碟 myJava\ch08\ch8_05.java)
39
一般類別的繼承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch8_05.java 功能:多層繼承之建構子呼叫 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CC objC1 = new CC(); CC objC2 = new CC(10); CC objC3 = new CC(3,5); objC1.show(); objC2.show(); objC3.show(); } class CA protected int varA; public CA() { varA=1;} public CA(int i) { varA=i;}
40
一般類別的繼承 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 } class CB extends CA // 類別CB繼承自類別CA { protected int varB; public CB() { varB=1;} public CB(int i) super(i); varB=i; class CC extends CB // 類別CC繼承自類別CB protected int varC; public CC() { varC=1;} public CC(int i){varC=i;} public CC(int i,int j) super(i*j); varC=i*j; public void show()
41
一般類別的繼承 執行結果: 範例說明: CC類別繼承CB類別,CB類別繼承CA類別。本範 例一共有三個CC類別的物件,objC1,objC2,objC3 ,當物件建立時,其建構子的呼叫順序如下圖, 如果底層的建構子內,在第一行未註明super執行 某個特定的上層建構子,則會自動先執行上層無 引數的建構子。 51 52 53 54 55 System.out.print("varA=" + varA + "\t"); System.out.print("varB=" + varB + "\t"); System.out.println("varC=" +varC); } varA=1 varB=1 varC=1 varA=1 varB=1 varC=10 varA=15 varB=15 varC=15
42
一般類別的繼承 多層繼承
43
一般類別的繼承 父類別型態變數參考子類別物件實體 類別型態的物件參考可以指向相容類別的物件
此處所謂相容(compatible)類別代表的是其衍生類別,例如子類別或 孫類別…等等。 所以我們可以宣告一個物件變數,宣告型態時採用父類別型態 ,但卻將它指向子類別物件實體。例如下列範例: class CA{.....} class CB extends CA{.....} CA obj= new CB(); //父類別CA型態變數參考子類別CB物件實體
44
一般類別的繼承 父類別型態變數參考子類別物件實體 【觀念範例8-6】:父類別型態變數參考子類別物件實體。
當父類別型態的物件變數指向子類別的物件實體後,我們可以透過該 變數執行子類別中改寫(override)的函式或其他未改寫的繼承函式, 但不能執行子類別的新增函式。 【觀念範例8-6】:父類別型態變數參考子類別物件實體。 範例8-6:ch8_06.java(隨書光碟 myJava\ch08\ch8_06.java)
45
一般類別的繼承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch8_06.java 功能:父類別型態變數參考子類別物件實體 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CA obj = new CB(); //父類別型態變數參考子類別物件實體 obj.show1(); //可執行子類別改寫的函式 obj.show2(); //可執行繼承的函式 //obj.show3(); //不能執行子類別新增的函式 } class CA protected int varA; public void show1() System.out.println("父類別show1()執行中");
46
一般類別的繼承 執行結果: 子類別改寫的show1()執行中 父類別show2()執行中 26 27 28 29 30 31 32 33
34 35 36 37 38 39 40 41 42 43 44 } public void show2() { System.out.println("父類別show2()執行中"); class CB extends CA // 類別CB繼承自類別CA protected int varB; public void show1() System.out.println("子類別改寫的show1()執行中"); public void show3() System.out.println("子類別新增的show3()執行中"); 子類別改寫的show1()執行中 父類別show2()執行中
47
一般類別的繼承 父類別型態變數參考子類別物件實體 範例說明:
(1)父類別CA定義了show1()與show2()兩個成員函式。子類別CB定義 了show1()與show3()兩個成員函式,由於CB繼承了CA,因此show1() 為改寫的函式。 (2)第13行,宣告變數型態為父類別,但new產生實體時為子類別,因 此,父類別變數obj將指向子類別物件實體。 (3)第14行,obj可以執行子類別改寫的函式。第15行,obj可以執行子 類別繼承自父類別的函式。第16行,obj不能執行子類別新增的函式 。
48
一般類別的繼承 父類別型態變數參考子類別物件實體
【實用範例8-7】:假設我們有一個圖形類別,當中包含area變 數以作為存放圖形面積之用。由於計算面積按照圖形的種類而 有各種不同的公式,故我們將計算面積的函式保留交由繼承的 子類別改寫來完成。請建立一個圖形類別的物件陣列,計算面 積後,將陣列依照面積排序,最後輸出陣列內各物件的面積。 範例8-7:ch8_07.java(隨書光碟 myJava\ch08\ch8_07.java)
49
一般類別的繼承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch8_07.java 功能:父類別型態變數參考子類別物件實體的應用 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CShape objArr[] = new CShape[4]; objArr[0] = new CRect(10.0,20.0);//父類別型態變數參考子類別物件實體 objArr[1] = new CRect(5.0,15.0); objArr[2] = new CCircle(3.0); objArr[3] = new CCircle(6.0); for(int i=0;i<objArr.length;i++) objArr[i].computeArea(); //執行子類別改寫的計算面積公式 System.out.println("物件的面積如下"); CShape.show(objArr); //顯示面積 CShape.sortByArea(objArr); //進行排序 System.out.println("物件依面積排序後如下");
50
一般類別的繼承 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 CShape.show(objArr); //顯示面積 } class CShape { protected double area; public static void show(CShape objArr[]) for(int i=0;i<objArr.length;i++) System.out.print(objArr[i].area + "\t"); System.out.println(); public static void sortByArea(CShape objArr[]) //排序 int k,times; CShape temp; k=objArr.length-1; while(k!=0) times=0; for(int i=0;i<=k-1;i++) if(objArr[i].area > objArr[i+1].area)
51
一般類別的繼承 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 { temp=objArr[i]; objArr[i]=objArr[i+1]; objArr[i+1]=temp; times=i; } k=times; public void computeArea() {} class CRect extends CShape // 類別CRect繼承自類別CShape protected double length,width; public CRect(double i,double j) length=i; width=j; public void computeArea() area=length*width;
52
一般類別的繼承 執行結果: 物件的面積如下 200.0 75.0 28.26 113.04 物件依面積排序後如下
76 77 78 79 80 81 82 83 84 85 86 87 88 class CCircle extends CShape // 類別CCircle繼承自類別CShape { protected double radius; protected final double pi=3.14; public CCircle(double i) radius=i; } public void computeArea() area=radius*radius*pi; 物件的面積如下 物件依面積排序後如下
53
一般類別的繼承 父類別型態變數參考子類別物件實體 範例說明:
(1)父類別CShape定義一個area變數用來存放面積。並定義了三個函式 ,其中兩個為static類別函式,可以由類別直接執行,sortByArea()是 排序函式,同第六章所介紹之氣泡排序,只不過此處依據的是物件的 area欄位。另一個非static的函式為計算面積函式computeArea(),內容 為空,它可以被繼承。 (2)子類別CRect繼承了父類別CShape,並且改寫computeArea()函式。 另一個子類別CCircle亦如此。但兩者的函式內容則按照圖形計算面 積公式而有所不同。 (3)第13~17行,宣告時使用父類別型態宣告物件陣列,陣列元素內為 物件的參考,應該指向父類別物件實體,但也可以指向子類別物件實 體。
54
一般類別的繼承 父類別型態變數參考子類別物件實體 範例說明:
(4)第19~20行,使用迴圈計算面積,當中,i=0,i=1時,執行的是第 70~73行的函式。而當i=2,i=3時,執行的則是第84~87行的函式。
55
final修飾字的用途 final是一個修飾字,它可以出現在下列三個位置,在不同 位置有不同的意義:
1.宣告變數時:代表該變數為只能設定一次然後就不可改變的 數值。(若為物件變數,則參考只能指向第一次指向的物件實 體) 使用於區域變數時,只能設定一次然後就不可改變的數值。 使用於傳遞引數時,則引數傳遞後,接收端函式內不可改變其值。 使用於定義成員變數時,除了第一次設定之外,包含繼承後都不能再 改變其值。
56
final修飾字的用途 2.宣告於類別的成員函式時:代表繼承後,不可被改寫 (override)。
3.宣告於類別時:代表該類別不可被繼承(終止繼承)。 例如Java的String內建類別就被宣告為final,因此我們無法繼承String類 別(所幸,String類別已經非常完整,應該提供的字串相關函式功能 大多已提供了)。
57
final修飾字的用途 範例:使用final宣告變數 class CA { //宣告於成員變數
public final int var; // var只能設定一次,以後不可以再改變 public void func(final int i) //宣告於引數 //i不可以再被設定 final int j=10; //宣告於區域變數 //j不可以再被設定 }
58
final修飾字的用途 範例:使用final宣告類別的成員函式 範例:使用final宣告類別 class CA {
public final void show() //不可以被改寫 ……………………… } final class CA //不可以被繼承 { ……………………… }
59
final修飾字的用途 【觀念範例8-8】:final在不同位置的修飾用途
範例8-8:ch8_08.java(隨書光碟 myJava\ch08\ch8_08.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /* 檔名:ch8_08.java 功能:final在不同位置的修飾用途 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) final String str = new String ("final修飾字的示範"); //str = new String("....."); //上一行錯誤,因為str參考已被設定過,不可指向其他物件實體 System.out.println(str); CB obj = new CB(); obj.add(); obj.show(); //執行繼承自父類別的show() }
60
final修飾字的用途 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 class CA { public final int var1; public int var2; public CA() var1=10; var2=10; } public void add() //var1++; //錯誤,因為var1已經在建構子內設定過一次 var2++; public final void show() //不可被改寫的函式 System.out.println("var1=" + var1 + "\tvar2=" + var2); class CB extends CA // 類別CB繼承自類別CA
61
final修飾字的用途 執行結果: final修飾字的示範 var1=10 var2=20 46 47 48 49 50 51 52 53
54 55 56 57 //var1=var1+10; //錯誤,因為var1已經在父類別建構子內設定過一次 var2=var1+var2; } //public void show(){} //錯誤,因為show不可被改寫 final class CC //類別不可被繼承 { public int var3; //class CD extends CC{} //錯誤,因為CC不可被繼承 final修飾字的示範 var1=10 var2=20
62
final修飾字的用途 範例說明: (1)第52~55行的CC類別不可被繼承,故第57行不合法。
(2)第36~39行的show()不可被改寫,故第49行不合法。 (3)第24行的成員變數var1被宣告為final,並且在建構子內已經設定過 第一次,故之後不能再被設定,即使是被繼承後也不允許再被設定。 (4)第12行的str是一個物件參考,它指向一個字串物件實體。由於被 宣告為final,故不能再指向另一個字串物件實體。
63
Object類別 Java之所以被稱為純物件導向程式語言,除了它支援物件 導向的三大特色之外,還有兩個重要的原因
第一個原因是所有的函式必定屬於某一個類別(在C++中,則 由於也支援C,故有些函式不一定屬於某一個類別)。 第二個原因是Java的所有類別都有父類別。
64
Object類別 嚴格來說,在Java中,除了巢狀類別及Object類別之外, 所有的類別都有父類別。
或許您會覺得奇怪,由使用者定義的類別,常常並未宣告繼承 自哪一個類別(如範例8-8的CA類別),那麼誰是它的父類別 呢? 其實,當編譯Java程式時,編譯器若發現某個類別沒有宣告繼 承自哪一個類別的話,它會自動將Object類別當作該類別的父 類別。 故所有的類別都是Object類別的子類別或孫類別或…更多層繼 承之後的衍生類別。故範例8-8的類別繼承可如下圖所示:
65
Object類別 所有類別都繼承自Object類別(範例8-8類別繼承圖)
66
Object類別 Object類別的完整名稱為java.lang.Object類別
Object類別中沒有final成員函式,故所有的類別都擁有Object 類別的成員,並且可以改寫,下表是Object類別的成員函式:
67
Object類別 函式宣告 回傳值說明 說明 回傳布林值,用以判定該物件參考是否與obj參考同一個物件實體 見後面說明
boolean equals(Object obj) 回傳布林值,用以判定該物件參考是否與obj參考同一個物件實體 見後面說明 Class<?> getClass() 回傳一個泛型類別,可使用Class類別的物件變數接收回傳值 String toString() 回傳字串物件 protected Object clone() Object代表一個物件變數 複製物件,但須先實作Cloneable介面 int hashCode() 回傳整數,代表hashcode 須了解資料結構之雜湊表 protected void finalize() 無回傳值 垃圾回收使用 void notify() 多執行緒使用 void notifyAll() void wait() void wait(long timeout) void wait(long timeout, int nanos)
68
Object類別 由於讀者尚未建立介面、記憶體垃圾回收機制與多執行 緒觀念,故我們僅介紹以下三種成員函式的使用方法:
equals (Object obj)物件相等判斷 在範例7-13中,我們曾介紹如何判定兩個物件是否相等,事實 上,除了使用雙等號來判斷之外,也可以用equals()來判斷。 如果執行該方法的物件變數與引數的物件變數是參考同一個物 件實體,則回傳true,否則回傳false,請見下列範例8-9。
69
Object類別 【觀念範例8-9】: Object類別的equals()的練習。
範例8-9:ch8_09.java(隨書光碟 myJava\ch08\ch8_09.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* 檔名:ch8_09.java 功能:物件的比較 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CMyClass X = new CMyClass(5); CMyClass Y = new CMyClass(5); CMyClass Z = X; System.out.print("物件X與物件Y "); if(X.equals(Y)) System.out.println("兩物件相等"); else
70
Object類別 執行結果: 物件X與物件Y 兩物件不相等 物件X與物件Z 兩物件相等 19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36 System.out.println("兩物件不相等"); System.out.print("物件X與物件Z "); if(X.equals(Z)) System.out.println("兩物件相等"); else } class CMyClass { private int Var; public CMyClass(){} public CMyClass(int i) Var=i; 物件X與物件Y 兩物件不相等 物件X與物件Z 兩物件相等
71
Object類別 這個範例和範例7-13非常類似,只不過我們在判 定物件參考是否相等時,採用的是繼承而來的 equals()函式。
範例說明: 這個範例和範例7-13非常類似,只不過我們在判 定物件參考是否相等時,採用的是繼承而來的 equals()函式。 getClass()取得物件所屬類別 getClass會回傳物件所屬的類別,它回傳的是一個Class型態的 泛型,我們可以用一個內建Class類別的物件變數來接收,並 且該物件變數也可以直接由println()函式輸出內容,請見下列 的範例
72
Object類別 【觀念範例8-10】: Object類別的getClass()的練習。
範例8-10:ch8_10.java(隨書光碟 myJava\ch08\ch8_10.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* 檔名:ch8_10.java 功能:getClass()函式 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CMyClass X = new CMyClass(); Class Y; Y=X.getClass(); System.out.println("X屬於" + Y +"的物件"); String str = new String(".."); Y=str.getClass(); System.out.println("str屬於" + Y +"的物件");
73
Object類別 執行結果: 範例說明: 不論是自訂類別或Java的內建類別,都可以使用getClass()函式,因為 它是繼承自Object類別。 19 20 21 22 23 24 25 } class CMyClass { private int var; C:\>java myJava.ch08.ch8_10 X屬於class myJava.ch08.CMyClass的物件 str屬於class java.lang.String的物件
74
Object類別 toString()轉換為字串函式
toString可以將物件內容轉換為字串,不過這個字串通常沒有 什麼用途,所以大多數的類別都會改寫toString()函式 例如第三章介紹的Integer,Float等等的Java內建類別都改寫了這個函式 ,使得能夠傳回所需要的字串。 請見下列的範例。 【觀念及實用範例8-11】:toString()的改寫。 範例8-11:ch8_11.java(隨書光碟 myJava\ch08\ch8_11.java)
75
Object類別 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch8_11.java 功能:toString()的改寫 */ package myJava.ch08; import java.lang.*; public class ch8_ //主類別 { public static void main(String args[]) CRect X = new CRect(); CCircle Y = new CCircle(); String str = new String(); str=X.toString(); System.out.println("CRect類別物件執行toString():\t" + str); str=Y.toString(); System.out.println("CCircle類別物件執行toString():\t" + str); } class CRect private int length,width;
76
Object類別 執行結果: 範例說明: 本範例的CRect沒有改寫toString()函式,所以印出的資訊與類別有關 ,而CCircle則改寫了toString()函式,使得該函式有些用途,您可以視 需要改寫toString()函式以符合類別轉字串的用途。 26 27 28 29 30 31 32 33 34 class CCircle { private int radius; public String toString() return "CCircle類別是用來存放圓形"; } C:\>java myJava.ch08.ch8_11 CRect類別物件執行toString(): CCircle類別物件執行toString(): CCircle類別是用來存放圓形
77
補充資料 Java 視窗與繼承
78
Java的視窗元件類別 Java的視窗元件類別庫有AWT與Swing兩種
其中AWT為早期的設計,而Swing則是基於AWT基礎所設計的 新視窗元件。 在JDK 1.1時,Java就已經提供了AWT(Abstract Window Tookit) 類別庫,全名是java.awt package。 而在JDK1.2(Java2)之後,Java則新增了Swing類別庫,全名是 javax.swing package。
79
Java的視窗元件類別 Java的視窗元件類別庫有AWT與Swing兩種 這兩個系列的某些元件具有相同功能,但Swing的元件種類較 多。
區分的方式非常簡單,Swing的元件一般都以「J」為開頭,例 如標籤元件在AWT中,以Label類別來負責,而Swing則是JLabel 類別。 AWT的元件是利用作業系統現有的元件,故與作業系統有關 ,並且AWT所提供的元件較單調,也缺乏樹狀元件、目錄元 件等較複雜功能的元件。
80
Java的視窗元件類別 Java的視窗元件類別庫有AWT與Swing兩種 收納器(Container)
Swing是針對AWT的缺失所發展的元件,因此擁有樹狀元件等 較複雜的元件,並且Swing強調是由Java程式碼仿照視窗元件所 開發而成,故與作業系統無關,但也因此顯示速度較慢。 收納器(Container) AWT的元件分為一般元件與收納器(Container)兩類,其中收納 器元件可以用來容納其他視窗元件 例如視窗本身就是一個收納器元件,所有呈現在視窗中的元件都被收 納在視窗元件之中,這樣的物件稱之為收納器物件 以java.awt.Container類別為基礎的,都是收納器物件。
81
Java的視窗元件類別 收納器(Container) 【收納器】
收納器就像是盒子一般,它可以放入元件,也可以放入另一個收納器 ,您可以將外部的收納器看作是較大的盒子,而內部的收納器看作是 放在大盒子中的較小盒子,兩者都可以收納元件。 除了選單(Menu)以外,AWT所有的類別基礎皆為 java.awt.Component 類別 而Swing大部分的類別基礎為javax.swing.JComponent ,而 javax.swing.JComponent由於繼承自java.awt.Container,故所有 的Swing元件幾乎都具有容納其他視窗元件的功能,這也使得 Swing元件看起來比較美觀。 下圖是AWT與Swing的部分類別繼承圖(底色部分為Swing類別庫), 完整的繼承圖請參閱JDK說明文件。
82
Java的視窗元件類別 收納器(Container)
83
Java的視窗元件類別 【其他的Java圖形化元件】
除了Sun提供的AWT與Swing圖形化元件之外,IBM也曾經開發 過一套Java圖形化元件SWT(Standard Widget Toolkit),不過由於 在文件說明方面不易讓開發人員了解,故一直未形成主流。
84
Java視窗應用程式的基礎架構 一個視窗基本上是靠Frame或JFrame物件所構成,它是視 窗程式的基礎,由於它是一個收納器,故其他視窗元件 可納入其中,首先我們先來看幾個陽春的Java AWT視窗 程式。 【觀念範例15-1】:使用AWT的Frame類別設計一個陽春的視 窗程式。 範例15-1:ch15_01.java(隨書光碟 myJava\ch15\ch15_01.java)
85
Java視窗應用程式的基礎架構 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /* 檔名:ch15_01.java 功能: AWT Frame-視窗程式 (主類別繼承方式) */ package myJava.ch15; import java.lang.*; import java.awt.*; //載入AWT類別庫 public class ch15_01 extends Frame //主類別繼承Frame類別 { public ch15_01() this.setTitle("這是AWT視窗"); //設定視窗的標題列 this.setSize(300,200); //設定視窗的寬與高 this.setVisible(true); //設定視窗是否可被看見 } public static void main(String args[]) new ch15_01();
86
Java視窗應用程式的基礎架構 執行結果 範例說明: (1)在第9行,我們宣告了主類別繼承Frame類別,因此,主類別本身 就是視窗類別。
(3)本範例執行後,在螢幕左上角會出現視窗,這是因為我們並未設 定視窗的位置,故預設出現在左上角。
87
Java視窗應用程式的基礎架構 事實上,一個Java程式可以產生多個視窗,每一個視窗以 Frame物件來表示即可,請見下一個範例。
範例說明: (4)本範例執行後,無法按下 ic_01.tif關閉鈕來關閉視窗,這是因為我 們還沒有設定按下關閉鈕的事件處理方式。若要關閉視窗,可以於執 行ch15_01類別的Dos視窗中,按下【Ctrl】+【C】鍵結束執行程式 即可。 事實上,一個Java程式可以產生多個視窗,每一個視窗以 Frame物件來表示即可,請見下一個範例。 【觀念範例15-2】:使用AWT的Frame類別設計一個產生兩個 視窗的程式。 範例15-2:ch15_02.java(隨書光碟 myJava\ch15\ch15_02.java)
88
Java視窗應用程式的基礎架構 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* 檔名:ch15_02.java 功能:AWT Frame-視窗程式 (類別成員與函式變數方式) */ package myJava.ch15; import java.lang.*; import java.awt.*; //載入AWT類別庫 public class ch15_02 extends Frame //主類別繼承Frame類別 { static ch15_02 frm1 = new ch15_02(); //類別成員 public static void main(String args[]) Frame frm2 = new Frame(); //函式變數 frm1.setTitle("這是第一個AWT視窗"); //設定視窗的標題列 frm1.setSize(300,200); //設定視窗的寬與高 frm1.setVisible(true); //設定視窗是否可被看見 frm2.setTitle("這是另一個AWT視窗"); //設定視窗的標題列 frm2.setSize(200,300); //設定視窗的寬與高 frm2.setLocation(300,300); //設定視窗的位置 frm2.setVisible(true); //設定視窗是否可被看見 }
89
Java視窗應用程式的基礎架構 執行結果 frm1 桌面 frm2
90
Java視窗應用程式的基礎架構 範例說明:
(1)在第11行,我們使用類別成員的方式宣告了一個成員為ch15_02類 別的實體frm1,由於ch15_02類別繼承了Frame類別,故也是一個視窗 。在第15行,我們使用物件變數的方式宣告了一個成員為Frame類別 的物件frm2。 (2)frm1與frm2都是視窗,所以執行結果有兩個視窗。 (3)frm2.setLocation(300,300);設定了視窗位置,所以第二個視窗不是 位於螢幕左上角。 (4)第11行的static是因為該實體要在static main中使用,故宣告為static ,若不是在static方法中使用,則成員不需要宣告為static。
Similar presentations