Encapsulation,Message Passing以及Inheritance是構成Object-Oriented的三大要素,如果某程式語言只具備前面兩項特性,一般成為Object-Based。所謂Inheritance(繼承),是指Sub Class(子類別)繼承Super Class(父類別)後,就會自動取得父類別特性。如果子類別繼承了一個以上的父類別,則稱為Multiple Inheritance(多重繼承)。Java為了避開多重繼承的複雜性, class只允許單一繼承。

Java使用關鍵字extends來表達繼承觀念:

public class Animal {
    public String moveMethod() {
        return "Unspecified";
public class Bird extends Animal {
    public String moveMethod() {
        return "Fly";
public class Dog extends Animal {
    public String moveMethod() {
        return "run";
public class Fish extends Animal {
    public String moveMethod() {
        return "swim";

若class宣告時沒有指定extends,則Java會自動extends java.lang.Object。

public class A {

和下面的寫法相同

public class A extends java.lang.Object {

UpCasting(向上轉型)和DownCasting(向下轉型)

所謂casting是指型態轉換, UpCasting是將子類別型態的reference轉型為父類別型態, DownCasting則是將父類別型態的reference轉型成子類別型態。由於子類別可以視為和父類別相容,如Fish, Dog, Bird都是一種Animal, 因此UpCasting一定沒有問題:

Animal a;
Bird b;
a = b; // upcasting, Bird is a kind of Animal

父類別的reference可以指到子類別的Object,這種觀念稱為Polymorphism(多型)

但在downcasting的情況下, 父類別的reference和子類別並不相容, 如Animal不見得是一個Bird, 因此必須使用(SubClass)的casting語法來做強迫轉換。

Animal a = new Bird(); // upcasting
Bird b;
b = (Bird)a; // downcasting, compile correct
if (a instanceof Bird) { // true

downcasting除了必須由設計者下達外, JVM在runtime也會檢查實際的物件能否和reference的型態相容

Animal a = new Dog(); // upcasting
Bird b;
b = (Bird) a; // downcasting, compile correct, but runtime error

比較完整的範例如下

public class InheritanceExample {
    public static void main(String[] argv) {
        Animal a1, a2, a3, a4;
        Bird b;
        Dog d;
        Fish f;
        a2 = a1 = new Animal();
        b = new Bird();
        d = new Dog();
        f = new Fish();
        System.out.println(a1.moveMethod());
        System.out.println(b.moveMethod());
        System.out.println(d.moveMethod());
        System.out.println(f.moveMethod());
        a1 = b; // Correct, we call this upcasting
        b = a1; // Compile Error, type not compatible
        b = (Bird)a1; // downcasting, Compile Correct
        a2 = b; // Correct,we call this upcasting
        d = a2; // Compile Error, type not compatible
        d = (Dog)a2; // Compile Correct, but runtime error

Override(覆寫)

子類別重新定義它所能看到的父類別中的method(如public, protected, 如果子類別和父類別在同一個package裡, 則沒有修飾字的method也可以), 稱為override。

public class Animal {
    public String moveMethod() {
        return "Unspecified";
public class Bird extends Animal {
    // override Animal's moveMethod
    public String moveMethod() {
        return "Fly";

要特別強調的是

  • 如果子類別看不到父類別的方法(如父類別的private方法,或子父類別不在同一個package而子類別定義了父類別內的package method),則就算定義了同樣的method,也不是override
  • 重複定義static method也不算override
  • 子類別不可縮小父類別方法的存取範圍
  • public class C2 {
        public void a() {}
    public class C1 extends C2 {
        protected void a() { // Compile Error,不得縮小存取範圍
    

    Virtual Function(虛擬函數)

    訊息傳遞的章節裡,我們有提到過Object接收到訊息後,是在Runtime才決定實際所要呼叫的Method。由於父類別的reference可以指到子類別物件(Polymorphism),而子類別和父類別可能都定義了相同的Method(Override),當使用父類別reference傳遞訊息給子類別物件時,應該要呼叫父類別的方法還是子類別的方法? 如果

  • 呼叫子類別的方法,則稱為Virtual Function
  • 呼叫父類別的方法,則稱為Non-Virtual Function
  • 有些程式語言,如C++,以上兩種機制都提供,可由設計者自行決定。但是Java語言為了遵循物件導向的精神,並避免設計者因語言設計複雜而犯錯,因此只提供了Virtual Function。

    public class InheritanceExample {
        public static void main(String[] argv) {
            Animal a1;
            a1 = new Animal();
            System.out.println(a1.moveMethod()); // print out "Unspecified"
            a1 = new Bird(); // polymorphism
            System.out.println(a1.moveMethod()); // print out "Fly"
    

    請注意上一小節所提到Override的注意事項

    class Animal {
        public static String moveMethod() {
            return "Unspecified";
        public static void main(String[] argv) {
            Animal a1;
            a1 = new Bird();
            System.out.println(a1.moveMethod()); // print out "Unspecified"
    class Bird extends Animal {
        // we can't override static method
        public static String moveMethod() {
            return "Fly";
    

    上面的moveMethod()由於宣告為static,因此是依照reference的type來決定執行的method。

    class Animal {
        private String moveMethod() {
            return "Unspecified";
        public static void main(String[] argv) {
            Animal a1;
            a1 = new Bird();
            System.out.println(a1.moveMethod()); // print out "Unspecified"
    class Bird extends Animal {
        // this is not override because Bird can't see Animal's moveMethod
        public String moveMethod() {
            return "Fly";
    

    由於上面Animal內的moveMethod宣告為private,因此執行時印出"Unspecified"。

    採用Virtual Function的優點

  • Runtime自動尋找最特定的方法(儘量用子類別的方法),可用父類別reference呼叫到子類別的方法,因此增加新的子類別時,不需要修改程式
  • 執行起來比較慢
  • 本章觀念整理範例

    public class Shape2D { // define super class
        public double area() { // all Shape2D have their own area
            return 0;
    public class Rectangle extends Shape2D {
        private double length, width;
        public Rectangle(double l, double w) { // define constructor
            length = l;
            width = w;
        public double area() { // Override
            return length * width;
    public class Circle extends Shape2D {
        private double radius;
        public Circle(double r) {
            radius = r;
        public double area() { // Override
            return 3.141592654 * radius * radius;
    public class Parallelogram extends Shape2D {
        private double top, bottom, height;
        public Parallelogram(double t, double b, double h) {
            top = t;
            bottom = b;
            height = h;
        public double area() { // Override
            return (top + bottom) * height / 2.0;
    publicclass Main {
        public static double sum(Shape2D[] shapes) {
            double total = 0;
            for (int i = 0; i < shapes.length; i++) {
                total += shapes[i].area(); // use Virtual Function to calculate area of Shape2D
                                           // Without Virtual Function, value of Shape2D.area() will be 0
            return total;
        public static void main(String[] argv) {
            Shape2D[] data; // array of reference to Shape2D
            data = new Shape2D[5]; // create array object
            data[0] = new Rectangle(2.4, 3.8); // Polymorphism
            data[1] = new Circle(3.9);
            data[2] = new Parallelogram(3.5, 6.7, 10.2);
            data[3] = new Rectangle(5.3, 7.2);
            data[4] = new Circle(4.6);
            System.out.println("Sum of all Shape2D is "+sum(data));
    

    如果程式語言不支援virtual function的話, 則上面的範例就得寫成下面的形式才行

    public class Main { // example for non-virtual function implementation
        public double sum(Shape2D[] shapes) {
            double total = 0;
            for (int i = 0;  i < shapes.length; i++) {
                if (shapes[i] instanceof Rectangle) {
                    total += ((Rectangle)shapes[i]).area();
                } else if (shapes[i] instanceof Circle) {
                    total += ((Circle)shapes[i]).area();
                } else if (shapes[i] instanceof Parallelogram) {
                    total += ((Parallelogram)shapes[i]).area();
                } // modify source code here for new sub classes
            return total;
        public static void main(String[] argv) {
            Shape2D[] data; // array of reference to Shape2D
            data = new Shape2D[5]; // create array object
            data[0] = new Rectangle(2.4, 3.8); // Polymorphism
            data[1] = new Circle(3.9);
            data[2] = new Parallelogram(3.5, 6.7, 10.2);
            data[3] = new Rectangle(5.3, 7.2);
            data[4] = new Circle(4.6);
            System.out.println("Sum of all Shape2D is "+sum(data));
    

    final修飾字

    final除可用來修飾變數外,也可放在class和object method前面:

    public final class FinalClass {
        public final void finalMethod() {
    

    放在class前面表示class不可被繼承, 放在object method表示不可被Override。

    繼承關係下的Constructor執行順序

  • 先將所有變數設為內定值。對數值型態來說,其值為0; 對reference來說,其值為null; 對boolean來說,其值為false。
  • 呼叫父類別的constructor。如果子類別Constructor裡沒有指定父類別的Constructor, 則使用父類別沒有參數的Constructor。
  • 執行變數宣告的初始化動作。
  • 執行自己的constructor。
  • 如果要指定父類別其他的constructor,則必須在子類別的constructor的第一行使用關鍵字super來處理。

    class Animal {
        int aMask = 0x00FF;
        public Animal() {
        public Animal(int mask) {
            aMask = mask;
    public class Bird extends Animal {
        int bMask = 0xFF00;
        int fullMask;
        public Bird() {
            // Compiler add super() here
            fullMask = bMask | aMask;
        public Bird(int mask) {
            /* 若有super,則必須放在第一行,連變數宣告也不能擺在super前面 */
            super(mask);
            fullMask = bMask | aMask;
        public static void main(String[] argv) {
            Bird b = new Bird();
            System.out.println(b.fullMask);
            b = new Bird(0x0011);
            System.out.println(b.fullMask);
    

    當執行new Bird()時,此物件內各個變數的變化如下

    步驟aMaskbMaskfullMask default000 call Bird()000 call Animal()000 Animal initialize0x00FF00 execute Animal()0x00FF00 Bird initialize0x00FF0xFF000 execute Bird()0x00FF0xFF000xFFFF

    當執行new Bird(0x0011)時,此物件內各個變數的變化如下

    步驟aMaskbMaskfullMask default000 call Bird(0x0011)000 call Animal(0x0011)000 Animal initialize0x00FF00