Managing Inheritance Lecturer:楊昌樺
Outline 存取控制(Access Control) static 關鍵字 實體成員與類別成員 static 初值設定區塊 Your Turn 物件導向語言三大特性 封裝 (Encapsulation) 繼承(Inheritance) 同名異型( Polymorphism ) Overriding Overloading super 關鍵字 Java 物件祖先:Object 類別 Final Classes and Methods
存取控制(Access Control) 控制存取的關鍵字 public、protected、private 置於類別或成員宣告之前 若沒有指定任何存取權限,表示此類別或成員僅供相同類別庫的其他類別使用,稱為 package access 層級 將類別或成員設定為 package access 層級的好處是可使同在一個類別庫下的每個類別互相使用成員
存取控制(Access Control) public default protected private 任何類別皆可存取 default packetage access protected 允許宣告的類別、子類別與同一個套件中的類別使用 private 只有在類別內部可以存取 對於成員變數,通常設定為 private 權限,再透過 methods,如 get/set 來存取資料
存取控制(Access Control) Class Access 類別不可設定為 private 或 protected 若為了避免他人任意使用 constructors 建立物件 可將 constructors 設定為 private 另外提供 static 的 methods 建立物件、複製物件 main() 必須由程式外部的程式碼來呼叫(Java 執行時期系統),所以 main() 必須為 public 設計存取秘訣: 設定適合但最嚴苛的存取等級。因此,盡量使用 private。 如此在修改成員變數內容時,只能透過 method。除非,在評估設為 public後會對於程式效能有顯著提升的情況。
Package與示範存取的範例 用eclipse功能 new->package, 建立Package “One” 在One裡面新增類別 “Alpha” Alpha類別只是一個 單純的物件類別 在One裡面新增類別 “DeltaOne” DeltaOne類別用同Package 的Alpha類別建立物件 新增Package “Two” 新增類別“Delta Two” DeltaTwo類別用別Package 的Alpha類別建立物件
存取控制(Access Control)範例 Example: Alpha.java private int iamprivate = 1; int iampackage = 2; //package access protected int iamprotected = 3; public int iampublic = 4; private void privateMethod() { System.out.println("iamprivate Method"); } void packageMethod() { // default: package access System.out.println("iampackage Method"); protected void protectedMethod() { System.out.println("iamprotected Method"); public void publicMethod() { System.out.println("iampublic Method");
存取控制(Access Control)範例 Example: DeltaOne.java package One; public class DeltaOne { public static void main(String[] args) { Alpha a = new Alpha(); //a.privateMethod(); //illegal a.packageMethod(); //legal a.protectedMethod(); //legal a.publicMethod(); //legal //System.out.println("iamprivate: “ + a.iamprivate); //illegal System.out.println("iampackage: “ + a.iampackage); //legal System.out.println("iamprotected: “ + a.iamprotected); //legal System.out.println("iampublic: “ + a.iampublic); //legal }
存取控制(Access Control)範例 Example: DeltaTwo.java package Two; import One.*; public class DeltaTwo { public static void main(String[] args) { Alpha alpha = new Alpha(); //alpha.privateMethod(); //illegal //alpha.packageMethod(); //illegal //alpha.protectedMethod(); //illegal alpha.publicMethod(); //legal //System.out.println("iamprivate: “ + alpha.iamprivate); //illegal //System.out.println("iampackage: “ + alpha.iampackage); //illegal //System.out.println("iamprotected: “ + alpha.iamprotected); //illegal System.out.println("iampublic: “ + alpha.iampublic); //legal }
static 關鍵字 實體成員與類別成員 變數或 methods 若宣告為 static,則此變數或 methods 即為類別變數(class variables)或類別方法(class methods) 宣告為 static 的變數或 methods 不屬於任何此類別的物件,乃此類別所有物件共同擁有 宣告 static DataType VarName; static ReturnType MethodName(Arg List) 使用 ClassName.VarName ClassName.MethodName(Arg List)
static 關鍵字 使用時機 無論此類別擁有多少物件,這份資料只需要一份 某種方法在實行時,與個別的物件無關 即便是沒有任何物件被產生,依舊可以使用被宣告成 static 的變數與 methods;相反的,instance variables 與 methods 必須透過物件才能實施 在 static methods 裡,無法直接呼叫 instance methods 或直接使用 instance variables 因為 instance variables 與 methods 必須透過物件才能實施
static 關鍵字 被用 static 宣告的類別方法和類別變數,意思上就像是其他程式語言中的全域函數(global functions)和全域變數(global variables),例如:C語言 因此,如果要很多宣告成 static 的方法或變數的話,必須要謹慎使用!
static 關鍵字- 實體成員與類別成員
static 關鍵字 - 實體成員與類別成員 Example: StaticDemo.java public class StaticDemo { public int instanceInteger = 0; public int instanceMethod() { return instanceInteger; } public static int classInteger = 0; public static int classMethod() { return classInteger; public static void main(String[] args) { …
static 初值設定區塊 class 變數或是實體變數,可以直接設定初值 限制: 不能用 if-else 來設定初值 public class BedAndBreakfast { public static final int MAX_CAPACITY = 10; //initialize to 10 private boolean full = false; //initialize to false } 限制: 不能用 if-else 來設定初值 設定初值時,不可以處理 exceptions 若發生 exceptions,也無法做錯誤處理
static 初值設定區塊 static 初值設定區塊 Java 允許將 static 變數集合起來進行初始化動作 語法: 因為 x 和 isOK 都是 class 變數,無法在建構元中設定初值,且利用 static 初設設定區塊可以處理 exceptions。 class TestClass{ static int x; static boolean isOK; static{ x = 100; //static int x; isOK = false; // static isOK; }
建構元(Constructors) 修飾建構元的關鍵字 private 如果一個類別裡所有的建構元都是宣告成 private 的話,那這個類別應該會有 public 的類別方法(class method),讓其他類別建立此類別之物件 protected 只有該類別的 subclass,或是屬同一個 package 裡的類別,才可以使用此建構元 public 所有類別都可以使用此建構元 預設 只有同一個 package 裡的類別,才能使用此建構元
Your Turn 整理上次寫的 Rectangle 類別 此類別必須完成下列要求 此類別不提供 constructor 供外部存取(為 private) 要提供三個 class methods 來建立 Rectangle 物件 第一個是使用預設長寬:長 8,寬 4(createRect) 第二個是可以讓使用者自行指定(createRect) 第三個複製矩形(cloneRect) 顯示目前的長、寬之值(showLW) 取得目前矩形之面積(getArea) 畫矩形,由 * 構成邊長(drawRect)
Your Turn Hint // 建構元 private Rectangle(){ … } // 另一個建構元 private Rectangle(int length, int width){ … } // 此建構元提供給 cloneObj 用,用以複製 private Rectangle(Rectangle obj){ … } // 提供一個可以製造長方形的 class method static Rectangle createRect() { … } // 提供一個可以製造且自行設定長方形的 class method static Rectangle createRect(int len, int width) { … } // 複製此長方形 static Rectangle cloneRect(Rectangle obj) { … } // 顯示現在的長、寬 void showLW() { … } // 取得現在的面積 double getArea(){ … } // 畫出此長方形 void drawRect(){ … }
物件導向三大特性 封裝 (Encapsulation) 繼承 (Inheritance) 同名異型 (Polymorphism) 設計 Class,決定要將哪些屬性,方法,事件封入類別中的動作叫做封裝。 讓程式碼可以以Class為單位分類,並讓文件撰寫可以用物件導向模式撰寫 (e.g., class diagram)。 繼承 (Inheritance) 設計 Class 時,先利用現存 Class 作為 “祖先”,再加以增加或修改功能的動作叫繼承。 讓程式碼可以輕易地重複使用,並形成樹狀結構之class diagram。 同名異型 (Polymorphism) 呼叫相同的函式,卻會出現不同的行為的現象,稱為同名異型。分為 “overriding” 及 “overloading”。 擴充既有程式碼之功能。
物件導向三大特性 封裝 (Encapsulation) 設計一個 Class 的屬性,方法,事件,稱為 “封裝” 一個類別。 人類 屬性: 身高 體重 年齡 方法: 走路 跑步 事件: 被打 驚嚇 開心
物件導向三大特性 繼承 (Inheritance) 人類 音樂家 設計 Class 時,先利用現存 Class 作為 “祖先”, 再加以增加或修改功能的動作叫繼承。 讓程式碼可以輕易地重複使用,並形成樹狀結構之class diagram 人類 屬性: 身高 體重 年齡 方法: 走路 跑步 事件: 被打 驚嚇 開心 = 165 = 80 = 45 音樂家 貝多芬 作曲 發表
物件導向三大特性 同名異型 (Polymorphism) Overriding 人類 人類.走路() { 約一分鐘三十步 } 音樂家 屬性: 身高 體重 年齡 方法: 走路 跑步 事件: 被打 驚嚇 開心 人類.走路() { 約一分鐘三十步 } 音樂家 音樂家.走路() { 約一分鐘十步 } 作曲 同樣是呼叫 “走路”, 宣告成人類與音樂家 就是不一樣。 發表
物件導向三大特性 同名異型 (Polymorphism) Overloading 人類 音樂家 屬性: 身高 體重 年齡 方法: 走路 跑步 事件: 被打 驚嚇 開心 音樂家.作曲(“王先生”) 音樂家 作曲 作曲 (委託人) 自動判斷
封裝 (Encapsulation) Example: TimeDemo.java 將資料(屬性)與方法(行為)封裝在一個物件裡頭,物件裡頭的資料與方法被緊緊的綁在一起,並擁有資訊隱藏(Information hiding)的特性。 Example: TimeDemo.java class Time { private int hour; private int minute; private int second; public Time() { … } public void setTime(int hh,int mm,int ss) { … } public String toString() { … } }
繼承(Inheritance) 繼承概念圖
繼承(Inheritance) 語法: class ClassName extends BaseClass 例如:class Line extends GraphicsObject Base Class(SuperClass):基底類別、父類別 Derived Class(Subclass):衍生類別、子類別 Java 理論上不支援多重繼承,也就是說,一個子類別只能有一個父類別。 子類別將會繼承到父類別中所有可以存取的成員,包括:變數以及方法 注意:建構元(constructor)無法被繼承 Java 中每個物件的總祖先:Object 類別( java.lang.Object )
繼承(Inheritance) 可繼承成員 不可繼承成員 Superclass 中宣告為 public 或 protected 的成員。 如果 Subclass 與 Superclass 在同一個 package 中,會繼承未做任何存取控制宣告的成員。 不可繼承成員 如果 Subclass 與 Superclass 在不同 package,所有未宣告有效範圍的成員全部不繼承。(因為預設式 package access) Superclass 中宣告成 private 的成員
繼承(Inheritance) Example: Dog.java public class Dog { // 屬性 (Variables) private String name; private String color; private int age; // 建構元 (Constructor) public Dog(String name, String color, int age){ this.name = name; this.color = color; this.age = age; } // 方法 (Methods) public void bark() { … } public void handshake() { … } public void rollover(int theTimes) { … } …
繼承(Inheritance) Example: Pomer.java public class Pomer extends Dog { // 建構元 public Pomer(String name, String color, int age) { super(name, color, age); } // 新的方法 public void proud() { System.out.println("哼...");
同名異型( Polymorphism ) 覆蓋 Overriding 可覆蓋成員 任何與 Superclass 同名的成員 必覆蓋成員 Subclass 一定要覆蓋 superclass 中宣告為 abstract 的 methods,除非 subclass 本身也是 abstract 類別 不可覆蓋成員 Subclass 不可覆蓋 superclass 的 final methods
同名異型( Polymorphism ) 覆蓋 Overriding 將 父類別 Dog 的 Handshake 方法 Override 掉 class Pomer extends Dog { // 建構元 public Pomer (String name, String color, String age) { super(name, color, age); } // 將父類別 Dog 中原有的 handshake() 方法 Override 掉 public void handshake() { System.out.println("你的手洗了嗎?"); public void proud() { System.out.println(“哼...");
同名異型( Polymorphism ) 過載 Overloading
同名異型( Polymorphism ) 建構元的 Overloading class Pomer extends Dog { public Pomer() { super(“GoodDog”, “Red”, 12); } public Pomer (String name, String color, String age) { super(name, color, age); // override 父類別 Dog 中的 handshake() 方法 public void handshake() { System.out.println("你的手洗了嗎?"); public void proud() { System.out.println(“哼..."); }
同名異型( Polymorphism ) 方法的 Overloading class Pomer extends Dog { // 建構元 public Pomer (String name, String color, String age) { super(name, color, age); } // 具有 overloading 的 rollover() public void rollover() { for (int i=1; i<=5; i++) System.out.println("我滾 " + i + " 次"); public void rollover(int thetimes) { for (int i=1; i<=thetimes; i++)…
super 關鍵字 this & super 關鍵字 this:指的是目前的 class super:指的是父類別 語法:super.methodName(argList); super.Bark(); super.Rollover(5); 若子類別中沒有建構元(constructor),可單用 super 來呼叫父類別中的建構元 語法:super(argList);
Java 物件祖先:Object 類別 Java 中的所有物件,全部繼承自 java.lang.Object 只要程式師沒有以 extends 指定 繼承之物件,Java 會自動用 Object 作為所有物件的父物件。
Java 物件祖先:Object 類別 以下是 Object 中的方法,可能是您想 Overriding 的: clone() equals() toString() finalize() 以下是 Object 中,宣告為 final 的方法,不可以 Overriding: getClass() hashCode() notify() notifyAll() wait()
Java 物件祖先:Object 類別 clone() 原始宣告: protected Object clone() throws CloneNotSupportedException 作用:複製一份物件本身,並傳回去。 要求: 要讓類別成為可複製類別的最簡單方法:在類別宣告最後加上 implements Cloneable Object 提供的 clone() 功能很適合,但有些類別必須要覆蓋 clone(),才能提供正確的複製功能
Java 物件祖先:Object 類別 equals() hashCode() 原始宣告: public boolean equals(Object obj); 作用:比較兩個物件的內含值是否相等。 要求:無 hashCode() 回傳的 int 數值,代表物件在 hash table 裡對應位置。 傳回物件在 “雜湊表” (Hash Table) 中的索引值。通常配合 java.util.Hashtable 使用
Java 物件祖先:Object 類別 finalize() 原始宣告: protected void finalize() throws Throwable 作用:物件離開其有效範圍時,一定會被叫用的函式。用來清除本物件以 new 霸佔的記憶體。 系統會自動呼叫 finalize(),因此大部分的類別都不需要覆蓋 finalize() 所以在 Java 中,要拋棄一個記憶體,只要: ObjA = null; ObjB = null;
Java 物件祖先:Object 類別 toString() 原始宣告: public String toString(); 作用:傳回一個此物件的描述字串 toString() 對除錯(debug)很有幫助 System.out.println(new Double(Math.PI).toString());
Java 物件祖先:Object 類別 改寫從Object物件繼承來的toString()方法 Example: TimeDemo.java class Time { … public String toString() { return (hour+":"+(minute<10? "0" : "")+minute+ ":"+(second<10? "0" : "")+second); }
Java 物件祖先:Object 類別 getClass() 原始宣告: public final Class getClass(); 要求:不准覆寫 (Overriding) 傳回值:傳回 java.lang.Class。傳回後,您就可以使用所有 java.lang.Class 的屬性與方法。
Final Classes and Methods 定義 一個不准被別人繼承的類別稱為 final class 宣告 語法:AccessLevel final class ClassName 如:final class ColorPoint 使用時機 安全性 駭客最常用來破壞系統的方式,建立某個類別的 subclass,然後將原來類別的主體置換成自己的主體。 當一個類別已經十分完美時
Final Classes and Methods final class A { //… } // The following class is illegal. class B extends A //錯誤!無法形成A的子類別 { //… }
Final Classes and Methods Final Methods 定義 一個不准被 subclass 覆寫(Overriding)的函式稱為 final method 宣告 語法:final 傳回值型態 函式名稱 (參數, …); 使用時機 當覆寫(Overriding)會出現問題時。
Final Classes and Methods Final Methods class A { final void meth() { System.out.println("This is a final method."); } } class B extends A { void meth( ) //這裡開始會出現錯誤!不能覆蓋。 { System.out.println("Illegal!"); } }
實例講解 程式:InheritanceDemo.java 類別成員(Class Method) 繼承(Inheritance) Overriding Overloading super
Your Turn 試用 Dog 類別產生一個 如GoldenRetriever (黃金獵犬)類別 具有兩個建構元(Overloading) GoldRetriever() 預設:如流浪狗、金色、5 歲 不傳任何參數時、呼叫overloading的建構元 呼叫方式如 this(“流浪狗”,“金色”,5); GoldRetriever(String theName, String theColor, int theAge) 初始化其實跟Dog父類別一樣, 呼叫方式如 super(theName,theColor,theAge); 多一個方法 smile(),如印出 “^_^” Override 原本 Dog 類別中的 bark() 方法,如印出 “嘻嘻~” Override Object 中的 toString(),如印出“我是一隻可愛的黃金獵犬!”