這篇會記錄:
- 方法重載(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
裡其實背後發生兩件事:
- 加上預設建構子
- 預設建構子加上
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 ...
}