1. 程式人生 > >Java--繼承與多型

Java--繼承與多型

何謂繼承

繼承的基本概念就不在贅述。

多型與is-a

  • 在Java中,子類只能繼承一個父類,子類與父類之間會有is-a的關係,我們稱之為“是一種”的關係。我們要理解多型,必須知道我們操作的物件是“哪一種”東西。我們可以將自己當做編譯程式,檢查語法的邏輯是否正確,方法是從=號右邊往左讀,右邊是不是左邊的一種呢(右邊的類是不是左邊的一種子類呢)。
  • 有效的理解多型有助於我們寫出來的東西更加的有彈性,更好的維護。
  • 我們來看一個例子:現在讓你設計一個方法,要求顯示一些遊戲角色中的血量。
    有人會用過載方法進行設計:
public static void
showBlood(SwordsMan swordsMan) { System.out.printf("%s 血量 %d %n", swordsMan.getName(), swordsMan.getBlood()); } public static void showBlood(Magician magician) { System.out.printf("%s 血量 %d %n", magician.getName(), magician.getBlood()); }
  • 這的確是一個可行的方法,但是如果當我們要顯示100個甚至更多的角色血量時,難道要重載出100個方法?這顯然是不可能的,我們這時候就可以用下面的方法。
  • 我們知道上面的兩個角色都是繼承於一種父類,我們暫且將父類命名為Role,我們可以用以下的方法設計並呼叫:
static void showBlood(Role role)
{
    System.out.printf("%s 血量 %d %n", role.getName(), role.getBlood());
}

showBlood(swordsMan);
showBlood(magician);
  • 可以像上面這樣設計的,是因為SwordsMan是一種Role,magician是一種Role。
  • 這就是多型,可以使用單一介面操作多種型別的物件

重新定義行為

  • 我們有這個需求是當我們需要操作介面相同,而操作內容不同時,可以將這個方法提升至父類。在父類中只是定義了這個方法,但是具體的操作內容是由子類來執行的。在繼承父類後,定義與父類中相同的方法部署,但執行的內容不同,這稱為重新定義。
  • 標註:在子類中的某個方法前標註 @Override ,表示要求編譯程式檢查,該方法是不是真正的重新定義了父類中的某個方法,如果不是的話就會引發錯誤。

抽象方法,抽象類

在上面提到過重新定義行為,當我們在一個類中沒有對這個類中的方法寫具體操作的程式碼的時候,這個方法只是被定義了,我們稱它為抽象方法,定義這個方法的時候必須加上abstract名稱,表示這個方法是不完整的,此時這個類的宣告也必須加上abstract,表明這個類也是不完整的,這個類就不能用來生成例項,子類如果繼承了抽象類,對抽象方法有兩種處理方法,一種就是繼續定義它為抽象方法,另一種就是操作這個抽象方法。

繼承語法細節

protected成員

protected與static和public一樣都是對許可權的一種限制,被宣告為protected的成員,在相同的包中可以直接存取,在不同的包中,只有繼承了父類之後才能進行存取。

重新定義的細節

如果我們想在子類中使用父類中的方法或建構函式,我們可以使用super關鍵字,當我們使用super呼叫父類中的方法時,這個方法不能被宣告為private。
重新定義方法時,我們對於父類中的方法許可權只能擴大,不能縮小
在JDK5之後,如果子類中重新定義的方法的返回型別是父類方法返回型別的子類,是可以通過編譯的。

再看建構函式

建構函式可以過載,父類可以過載多個建構函式,如果子類建構函式中沒有指定使用父類中的哪個建構函式,就會預設使用父類中無引數的建構函式。
如果自行定義了建構函式,就不會加入任何建構函式,來看個例子:

class Some
{
    Some(int i)
    {
        out.println("....");
    }
}

class Other extend Some
{
    Other()
    {
        out.println(".....");
    }
}

這是會報錯的,因為我們在Some類中已經定義了Some(int i)這個方法,所以不會預設給Some類中加入無引數的方法,當我們編譯Other()方法時,程式預設呼叫父類中的無引數方法,但是父類中沒有無引數的方法,所以就會報錯。

再看final方法

如果class之前由final方法,表示這個類是最後一個了,不會有子類,也就是不能被繼承。
定義方法的時候,也可以限定該方法是final,表明子類中不可以重新定義該方法。

Java.lang.Object

在Java中,如果我們沒有指定一個類繼承哪個類的時候,預設是繼承自Object的,因此Object是最上層的父類,即所有的物件都“是一種”Object。
Array類:可以不限長度的收集物件。來看一個例子:

package com.paranoid;

import java.util.Arrays;

public class ArrayList
{
    private Object[] list;       //使用Object陣列收集
    private int next;            //下一個可儲存物件的索引

    public ArrayList(int capacity)          //指定初始容量
    {
        list = new Object(capacity);
    }

    public ArrayList()                      //初始容量預設為16
    {
        this(16);
    }

    public void add(Object o)               //收集物件方法
    {
        if(next == list.length)
        {
            list = Arrays.copyOf(list, list.length*2);
        }
        list[next++] = o;
    } 

    public Object get(int index)             //依索引取得收集的物件
    {
        return list[index];
    }
    public int size()                        //已收集物件的個數
    {
        return next;
    }
}

關於equals()和“==”

對於這兩個的不同,現在只是淺要的說一下,==比較的是兩個變數所參考的是不是同一個物件,而equals()比較的是兩個物件的實質相等性。
對於instanceof它判斷的是物件是否是由某個類建立的,左運算元是物件,右運算元是類。左邊是否是由右邊建立的。

關於垃圾收集

  • 建立物件的時候會佔據記憶體,如果程式流程中已經無法再使用某個物件,該物件只是對耗記憶體的垃圾。對於不再有用的物件,JVM中有垃圾收集機制。
  • 無法通過變數參考的物件就是垃圾。