把真實世界的事物抽象化為物件。以物件為主體,透過物件方法與其他物件進行互動
物件導向只是一種『設計概念 (design concept)』,而不是一種程式語法
三大特性
封裝(Encapsulation)
繼承(Inheritance)
多型(Polymorphism)
多載(Overload)
覆寫(Override)
封裝(Encapsulation)
把物件隱藏在類別裡,通過設定存取權限,只開放公開的方法讓別人可以使用,把內部實作細節隱藏,做到保護資料的目的
詳細資料
例如說"提款機"
你不需要知道內部還有多少錢,也不需要知道實際運作流程,你只要插入卡片,輸入密碼即可,接著就可以透過螢幕上按鈕互動
1 2 3 4 5 6 7 8 9 10 11 12 class ATM{ private int 剩餘金額; private boolean 身分確認(密碼){ ... }; public void 提款(){ ... } public void 查詢餘額(密碼){ ... } }
常見修飾子
private
開放權限最低,只有同類別的成員可存取
public
可被所有類別存取
protected
可被不同package的子類別(繼承)存取
default
只能被同個package存取
繼承 (Inheritance)
子類別會擁有父類別全部的成員及方法,可以再透過覆寫(override)實作專屬自己的屬性
詳細資料
多型(Polymorphism)
Java備忘筆記
肉豬工程師
指父類別為子類別的通用型態,再透過子類別可覆寫父類別的方法來達到多型的效果,
利用父類別提供的方法呼叫,執行子類別自己的行為。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Animal { void move() { System.out.println("move...move..."); } } class Dog extends Animal { void move() { System.out.println("跑...跑..."); } } class Bird extends Animal { void move() { System.out.println("飛...飛..."); } } class Fish extends Animal { void move() { System.out.println("游...游..."); } }
多載(Overload)
指在一個類別(class)中,定義多個名稱相同,但參數(Parameter)不同的方法(Method)。
例如Java String的indexOf(...)即為多載的例子。
以indexOf為名稱的方法分別為:
1 2 3 4 public int indexOf(int ch) public int indexOf(int ch, int fromIndex) public int indexOf(String str) public int indexOf(String str, int fromIndex)
所以只要方法的參數型態或數目不同,則允許多個相同名稱的方法存在。但要注意多載並不包含回傳型態不同,也就是如果方法的名稱相同,參數型態及數目相同,而只有回傳型態不同,仍有命名衝突的錯誤。例如下面範例的public String hello()即與public void hello()發生命名衝突。
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface HelloWorld { void hello(); // 與最下面的 String hello() 衝突 <!-- ------------------------------------------------------------------------------------- --> void hello(int i); <!-- ------------------------------------------------------------------------------------- --> void hello(int i, int j) ; <!-- ------------------------------------------------------------------------------------- --> void hello(int i, String s); <!-- ------------------------------------------------------------------------------------- --> void hello(String s) ; <!-- ------------------------------------------------------------------------------------- --> String hello(); // 回傳型態不同並無多載,與最上面的 void hello() 衝突 }
覆寫(Override)
通常用於繼承子類別,指子類別可以覆寫父類別的方法內容,使該方法擁有不同於父類別的行為。
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 26 27 28 29 30 31 32 33 public class Main { public static void main(String[] args) { // Animal為Dog及Bird的通用型態 Animal dog = new Dog(); // 子類別Dog物件分派至Animal型別變數dog Animal bird = new Bird(); // 子類別Bird物件分派至Animal型別變數bird act(dog); // run act(bird); // fly } <!-- ------------------------------------------------------------------------------------- --> private static void act(Animal animal) { // 因為多型,所以參數以通用的父類別傳入 animal.move(); // 因為多型及覆寫,所以實際執行的方法為子類別的方法內容 } } <!-- ------------------------------------------------------------------------------------- --> class Animal { public void move() { System.out.println("move"); } } <!-- ------------------------------------------------------------------------------------- --> class Dog extends Animal { @Override public void move() { System.out.println("run"); // 覆寫父類別Animal.move()的內容 } } <!-- ------------------------------------------------------------------------------------- --> class Bird extends Animal { @Override public void move() { System.out.println("fly"); // 覆寫父類別Animal.move()的內容 } }
抽象(Abstract)
詳細資料
抽象就是用來隱藏細節,可以想像成一種概念,並不存在於世界上。
在『多型』的章節中,我們有一個Animal的類別,但現實生活中有這個東西嗎? 動物只是一個概念,並沒有一種生物叫作動物。 狗、鳥、魚都是實際存在於世界上的實體,他們都是動物,現實中卻沒有一個實體叫作動物。
如果聽起來卡卡的,再反覆讀幾次。 所以我們可以把動物視為一種『概念』,他是抽象的,不存在於世界上的。 而Dog、Bird、Fish都具備這個概念的特質,所以繼承Animal。
抽象類別 abstract class
特性:
抽象類別不能直接被實例化。
可以包含抽象方法(沒有實現的部分)和具體方法(有實現的部分)。
子類別需要實現抽象方法。
利用abstract來修飾類別,可以使類別變成抽象類別,使用方法:
1 2 3 abstract class 類別名稱{ // 成員定義... }
抽象是一個概念,而不是一個存在的實體。同樣的,抽象類別不能被實體化。
可使用繼承去實作抽象類別
抽象方法 abstract method
利用abstract,可以使方法變成抽象方法,抽象方法只能寫方法的原型,無法定義方法的本體(不能有{})。
使用格式:
1 abstract <修飾子> 回傳型態 方法名稱(<參數...>);
範例:
錯誤範例:
1 2 3 abstract void eat(){ // 編譯錯誤:Abstract methods do not specify a body // 不管有沒有寫東西,都編譯錯誤,抽象方法不能定義方法本體 }
注意事項
抽象方法只能定義在抽象類別中。 (否則會編譯錯誤)
繼承抽象類別的類別必須實作(override)全部抽象方法。 (否則會編譯錯誤)
如果子類別沒有實作抽象方法,則子類別必須定義為抽象類別。
1 2 3 class Cat extends Animal { // 沒有實作 eat() 方法,會導致編譯錯誤 }
將 Cat 定義為抽象類別,則不需要實作 eat()
1 2 3 abstract class Cat extends Animal { // 不需要實作 eat() }
介面(Interface)
詳細資料
介面,描述不同類別的共通行為。只要能夠實作介面方法,就可以實作該介面。
介面裡面定義方法原型,不能有具體實作方法,用來規範實作介面的類別,就必須實做介面方法,確保遵守一致的設計。
假設有"烤箱" 跟"冷氣",他們共同屬性是"電器設備",都需要插電才會運作
於是可以把插座設計成介面,方法就是插電這個行為
然後抽象類別110v、220v分別實作介面,然後"烤箱" "冷氣"再分別繼承抽象類別,去實作自己的方法內容
實際例子說明
1. 問題背景
烤箱和冷氣都是「電器設備」,它們有一個共同的特徵:需要插電才能運作。
我們可以抽象出這個共同的特徵,並設計成一個介面。
2. 設計方案
介面(Interface):定義「插電」這個行為,保證所有電器設備都需要實作這個功能。
抽象類別(Abstract Class):因為不同的電器使用不同的電壓,我們可以將「110V」和「220V」抽象化,並實作「插電」行為,對電壓類型做區分。
具體類別(Concrete Class):烤箱繼承 110V 抽象類別,冷氣繼承 220V 抽象類別,並在各自的具體類別中實現專屬的功能。
程式碼範例
定義介面
1 2 3 4 5 // 插座介面,定義插電的行為 public interface Socket { void connect(string str); }
抽象類別實作介面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public abstract class v110 : Socket { public virtual void connect(string str) { Console.WriteLine(str + "is using 110v"); } } public abstract class v220 : Socket { public virtual void connect(string str) { Console.WriteLine(str + "is using 220v"); } }
具體類別繼承抽象類別
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //繼承抽象類別 public class Refrigerator : v110 { private string str = ""; public void refrigeration(string str) { connect(str); Console.WriteLine(str + "開始冷藏"); } } class AirConditioning : v220 { public void Operation(string str) { connect(str); System.Console.WriteLine(str + "運作中..."); } }
主程式執行範例
1 2 3 4 5 6 7 8 9 10 11 internal class Program { static void Main(string[] args) { Refrigerator refrigerator = new Refrigerator(); refrigerator.refrigeration("冰箱"); <!-- ------------------------------------------------------------------------------------- --> AirConditioning air_conditioning = new AirConditioning(); air_conditioning.Operation("冷氣"); } }
抽象類別實作介面的特性
1. 抽象類別可以選擇實作介面的方法:
抽象類別在實作介面時,可以選擇完全實作介面中的方法,也可以不實作,或者部分實作。
如果抽象類別中沒有實作某些介面的方法,那麼子類別必須實作這些方法。
下面有範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface A{ void a(); void b(); } abstract class AbsClass implements A{ public void b(){ System.out.println("hello b~"); } abstract void c(); } class MyClass extends AbsClass{ public void a() { } // 方法b()已經在AbsClass實作,MyClass不需要再實作,當然也可以再覆寫b() public void c() { } }
2. 抽象類別的目的:
抽象類別通常用於定義共同的基底行為(部分實作)或邏輯,並讓子類別繼承和擴展。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public interface Socket { void connect(string str); } //抽象類別實作介面 abstract class v110 : Socket { public void connect(string str) { Console.WriteLine(str + "is 110v"); } } abstract class v220 : Socket { public void connect(string str) { Console.WriteLine(str + "is 220v"); } } class Refrigerator : v110 { private string str = ""; public void refrigeration(string str) { this.str = str; connect(str); System.Console.WriteLine(str + "開始冷藏"); } } class AirConditioning : v220 { private string str = ""; public void Operation(string str) { this.str = str; connect(str); System.Console.WriteLine(str + "運作中..."); } }
原則就是,非抽象類別要實作所有未定義的方法。
實作介面方法必須為公開(public)
介面的設計規則:
當一個類別或抽象類別實作介面時,這些方法的存取修飾符必須至少符合介面的可見性要求,也就是 public
1 2 3 4 5 6 7 8 abstract class v110 : Socket { // 錯誤:介面成員必須是 public protected void connect(string str) { Console.WriteLine(str + " is 110v"); } }
1. 介面的用途是什麼?
提供多型(Polymorphism)的基礎。
達到鬆耦合設計,方便程式碼擴展與維護。
支援依賴反轉原則(Dependency Inversion Principle),在設計模式(如 DI)中非常重要。
2. 抽象類別與介面的差異
特性
抽象類別
介面
多重繼承
不支援多重繼承,但可實現多個介面
支援多重實現
實現內容
可以包含抽象方法和具體方法
只能包含抽象方法(在部分語言中允許預設方法,例如 C# 的 default methods)
用於
提供部分實現,讓子類別共享
定義標準和規範
靈活性
用於類別間有明確繼承關係的情況
用於完全不相關的類別之間共享行為
建構子
詳細資料
在工程師使用 new 關鍵字來創造物件時被呼叫,用來初始化物件的資料欄位。
建構子有幾個特點:
必須與類別名稱同名。
不可以有回傳值。
可以帶入引數(arguments)。
主要功能為初始化物件,搭配new關鍵字被呼叫。
可以有多個建構子,但引數型態及個數不可以相同。