物件導向OOP

Ting

把真實世界的事物抽象化為物件。以物件為主體,透過物件方法與其他物件進行互動

物件導向只是一種『設計概念 (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
abstract void eat();

錯誤範例:

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 關鍵字來創造物件時被呼叫,用來初始化物件的資料欄位。

建構子有幾個特點:
  1. 必須與類別名稱同名。
  2. 不可以有回傳值。
  3. 可以帶入引數(arguments)。
  4. 主要功能為初始化物件,搭配new關鍵字被呼叫。
  5. 可以有多個建構子,但引數型態及個數不可以相同。
Comments