Java 學習筆記 10 – 類別與物件 Part 3(方法重載 / 繼承 / 抽象)


Posted by vickyh1315 on 2024-05-14

這篇會記錄:

  • 方法重載(Overload)
  • 繼承(Inheritance)
  • 抽象(Polymorphism)

方法重載(Overload)

當編譯器建立物件和方法,它會個別賦予獨特的標籤讓自己識別,但我們寫程式,如果方法明明功能一樣,但都要給上不同名稱,開發反而不方便;方法重載提供編譯器以命名、參數列表去區分方法,我們也能用同樣名字定義相同功能方法,達到雙贏的情況。

重載也被稱作「多載、覆載、超載」,指的是允許類別建立相同名稱,但有不同實現的方法。做到方法重載需滿足幾個條件:

  • 定義在類別之中
  • 方法命名相同
  • 傳入參數的型別或個數不同(才能讓編譯器識別該執行哪個方法)

    public class GetVolumeRunner() {
    
      // 取得正方體體積
      public int getVolume(int s) {
          return s * s * s;
      }
    
      // 取得長方體體積
      public int geVolume(int w1, int w2, int h) {
          return w1 * w2 * h;
      }
    
      // 取得圓柱體體積
      public double getVolume(double r, int h) {
          double pi = Math.PI;
          return pi * r * r * h;
      }
    }
    

繼承(Inheritance)

物件導向強調模組化的概念,降低程式之間的耦合,提升維護的容易性,特性除了封裝以外,接著要來討論 繼承

class Cat {
    static int count = 0;
    String name;
    int weight;

    Cat() {
        count++;
        this.name = "defaultName";
        this.weight = -1;
    }

    Cat(String name, int weight) {
        count++;
        this.name = name;
        this.weight = weight;
    }

    public void sayHi() {
        System.out.println("Hi");
    }
}

class GingerCat {
    static int count = 0;
    String name;
    int weight;

    GingerCat() {
        count++;
        this.name = "defaultName";
        this.weight = -1;
    }

    GingerCat(String name, int weight) {
        count++;
        this.name = name;
        this.weight = weight;
    }

    public void sayHi() {
        System.out.println("Hi");
    }
}

我希望讓 GingerCat 類別擴展 Cat 類別,但觀察以上程式,這樣寫有很多重疊部分,這時就能依賴繼承特性簡化建構流程。

定義子類別時,會使用 extends 關鍵字,且在父類別後定義,子類別將會繼承父類別的 public、protected 屬性與方法。

class 父類別 {
    // code ...
}

class 子類別 extends 父類別 {
    // code ..
}

初步重構上面例子,GingerCat 繼承 Cat 屬性、方法與預設建構子,另外,子類別可不加 @override 註釋,直接覆寫父類別同名、同引數方法。

class GingerCat extends Cat {
    // 繼承父類別的 public、protected 屬性與方法之外,也能定義自己的屬性及方法
    String interest;

    // 可直接覆寫父類別方法,但要注意重新定義時,修飾子權限只能更小
    public void sayHi() {
        System.out.println("Meow");
    }
}

super 關鍵字

this 代表自身類別,用來呼叫自身建構式跟自身成員,super 則是子類別用來呼叫父類別的建構式與成員,語法如下。

class 子類別 {

    // 方法可選擇是否帶引數
    子類別() {
        super([引數陣列]); 
    }

    void 方法一() {
        System.out.println("呼叫父類別屬性: " + super.父類別屬性);
        super.父類別方法([引數陣列]);
    }

}

但定義 GingerCat 時並沒有寫 super(),還是能繼承預設建構子,是因為 Java 預設會幫子類別加上去,所以在 GingerCat 裡其實背後發生兩件事:

  1. 加上預設建構子
  2. 預設建構子加上 super()
class GingerCat extends Cat {

    String interest;

    // 實際有預設建構子與 super()
    // 但要留意父類別本身有沒不帶引數的建構子,否則會出錯
    GingerCat() {
        super();
    }

    public void sayHi() {
        System.out.println("Meow");
    }
}

預設只會使用不帶引數的 super 建構子,想使用帶引數版本,就需要手動加入。

class GingerCat extends Cat {

    String interest;

    GingerCat() {
        super();
    }

    public void sayHi() {
        System.out.println("Meow");
    }
}

初始化過程

Java 的類別初始化過程是層層發生的:子類別是透過「擴展」父類別才能建構,所以指定子類別在初始化前,會先初始化上一層父類別,才能繼承屬性與方法,而所有方法最上層,會是 Object 類別,這點從類別可以繼承並 @Override toString()equal() 看出。

static 成員的限制

static 方法只能使用 static 屬性跟呼叫 static 方法,且 static 成員被放在 global 記憶體區塊,所以子類別不能有相同的 static 方法,也不能覆寫 static 方法。

final

類別也可以加上 final,但加上 final,就代表是在繼承體系的最後一個,不會再有子類別。

抽象(Polymorphism)

當我們 new 子類物件,初始化機制會先建立父類物件,再讓子類物件繼承,但並非所有父類別都需要變成實體物件被使用,這時我們可以利用抽象這項特性,達到
只使用父類別概念,實作子類別物件。

抽象類別是用來被子類建立範本,也因為只是作為概念被繼承、實作用途,無法被實例化。

語法

abstract 是抽象類別的修飾子,可以修飾屬性、方法。

abstract class 類別名稱 {
    // code ...
}
public class Main
{
    public static void main(String[] args) {

        Animal animal = new Animal(); // 回報以下錯誤
    }
}

// 抽象類別只是一個範本、概念,無法被實例化
Main.java:18: error: Animal is abstract; cannot be instantiated

權限修飾子

如果限定 private 私有權限,別人就不能繼承,作為被繼承的定位,不就失去它存在的意義了嗎?
所以 abstract 修飾的類別本體,僅能以 public 權限定義,沒寫修飾子的時候也預設以 public 定義。

// 分別列出 private, protected, default 修飾子定義情形

// Main.java:8: error: modifier private not allowed here
private abstract class Animal {
   // code ...
}

Main.java:8: error: modifier protected not allowed here
protected abstract class Animal {
   // code ...
}

Main.java:8: error: modifier default not allowed here
default abstract class Animal {
   // code ...
}

然而抽象類別的成員屬性、方法可以自己定義,成員的部分可以使用 public 外的權限修飾子,也能撰寫商業邏輯供子類繼承。

abstract class Animal {
    int id;
    private String name;
    protected String species;

    public setSpecies(String species) {
        this.species = species;
    }
}

抽象方法

只有抽象類別可定義抽象方法,定義方式一樣是方法前面加上 abstract,且定義當下方法不能被實作(加上 {} 寫商業邏輯),只能靠子類 @Override 方法覆寫實作,權限修飾子一樣只能使用 public。

abstract class Animal {
    int id;
    private String name;
    protected String species;

    public setSpecies(String species) {
        this.species = species;
    }

    // 抽象方法
    public abstract void printInfo();
    abstract int getId();
}

無法作為靜態成員

前面說到抽象類別不會被實例化,也因此一旦被加上 abstract,就不會載入記憶體,自然不能預設被載入,不能使用 static 修飾子。

Main.java:8: error: modifier static not allowed here
static abstract class Animal {
   // code ...
}

#java







Related Posts

JS30 Day 6 筆記

JS30 Day 6 筆記

Python 程式設計函式的模組的引入和使用入門教學

Python 程式設計函式的模組的引入和使用入門教學

Google Chrome開發者工具教學 - Elements

Google Chrome開發者工具教學 - Elements


Comments