溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

再談 Java 的繼承和超類 Object

發(fā)布時間:2020-07-10 20:57:43 來源:網絡 閱讀:139 作者:沉默王二 欄目:編程語言

再來聊聊繼承,以及超類 Object。

01、先有繼承,后有多態(tài)

利用繼承,我們可以基于已存在的類構造一個新類。繼承的好處在于,子類可以復用父類的非 private 的方法和非 private 成員變量。

is-a 是繼承的一個明顯特征,就是說子類的對象引用類型可以是一個父類。我們可以將通用的方法和成員變量放在父類中,達到代碼復用的目的;然后將特殊的方法和成員變量放在子類中,除此之外,子類還可以覆蓋父類的方法。這樣,子類也就煥發(fā)出了新的生命力。

一個對象變量可以引用多種類型的現象被稱為多態(tài)。多態(tài)發(fā)生的前提條件就是繼承。也就是說,先有繼承,后有多態(tài)。

class Wanger {

    public void write() {
        System.out.println("我為自己活著");
    }

}

class Wangxiaoer extends Wanger {
    public void write() {
        System.out.println("我也為自己活著");
    }
}

class Test {
    public static void main(String [] args) {
        Wanger wanger;
        wanger = new Wanger();
        wanger = new Wangxiaoer();

        Wangxiaoer wangxiaoer;
        //wangxiaoer = new Wanger(); // 不可以
        wangxiaoer = new Wangxiaoer(); // 只能這樣
    }
}

wanger 這個對象變量既可以引用 Wanger 對象,也可以引用 Wangxiaoer對象。但 wangxiaoer 就只能引用 Wangxiaoer 對象,不能引用 Wanger 對象。根本的原因在于 WangxiaoerWanger 的繼承者。

當使用 wanger 調用 write() 方法時,程序會在運行時自動識別其引用的對象類型,然后選擇調用哪個方法——這種現象稱為動態(tài)綁定。

動態(tài)綁定有一個非常重要的特性:無需對現有的代碼進行修改,就能對程序進行擴展。假如 Wangdaer 也繼承了 Wanger,并且 wanger 引用了Wangdaer 的對象,那么 wanger.write() 仍然可以正常運行。

當然了,有些類不愿意被繼承,也沒法被繼承。誰不愿意被繼承呢?比如武則天,親手弄死自己的親兒子。誰沒法被繼承呢,每朝每代最后的那位倒霉皇帝。

類怎么做到不被繼承呢?可以使用 final 關鍵字。final 關鍵字修飾的類不能被繼承,final 修飾的方法不能被覆蓋。

final class Wanger {

    public final void write() {
        System.out.println("你們誰都別想繼承我");
    }

}

繼承是面向對象編程當中舉足輕重的一個概念,與多態(tài)、封裝共為面向對象的三個基本特征。 繼承可以使得子類具有父類的成員變量和方法,還可以重新定義、追加成員變量和方法等。

在設計繼承的時候,可以將通用的方法和成員變量放在父類中。但不建議隨心所欲地將成員變量以 protected 的形式放在父類當中;盡管允許這樣做,并且子類可以在需要的時候直接訪問,但這樣做會破壞類的封裝性(封裝要求成員變量以 private 的形式出現,并且提供對應 getter / setter 用來訪問)。

Java 是不允許多繼承的,為什么呢?

如果有兩個類共同繼承一個有特定方法的父類,那么該方法會被兩個子類重寫。然后,如果你決定同時繼承這兩個子類,那么在你調用該重寫方法時,編譯器不能識別你要調用哪個子類的方法。

這也正是著名的菱形問題,見下圖。ClassC 同時繼承了 ClassA 和 ClassB,ClassC 的對象在調用 ClassA 和 ClassB 中重載的方法時,就不知道該調用 ClassA 的方法,還是 ClassB 的方法。

再談 Java 的繼承和超類 Object

02、超類 Object

在 Java 中,所有類都由 Object 類繼承而來。Object 這個單詞的英文意思是對象,是不是突然感覺頓悟了——萬物皆對象?沒錯,Java 的設計者真是良苦用心了?。‖F在,你一定明白了為什么 Java 是面向對象編程語言的原因。

你可能會疑惑地反問道:“我的類明明沒有繼承 Object 類啊?”如果一個類沒用顯式地繼承某一個類,那么它就會隱式地繼承 Object 類。換句話說,不管是雞生了蛋,還是蛋孵出了雞,總有一只 Object 雞或者一個 Object 蛋。

在面試的時候,你可能會被問到這么一個問題:“Object 類包含了哪些方法呢?”

1)protected Object clone() throws CloneNotSupportedException 創(chuàng)建并返回此對象的副本。

不過,《阿里巴巴 Java 開發(fā)手冊》上建議:慎用 Object 的 clone 方法來拷貝對象。因為 Object 的 clone 方法默認是淺拷貝,如果想實現深拷貝需要重寫 clone 方法實現屬性對象的拷貝。

什么是淺拷貝,什么是深拷貝呢?

淺拷貝是指在拷貝對象時,會對基本數據類型的變量重新復制一份,而對于引用類型的變量只拷貝了引用,并沒有對引用指向的對象進行拷貝。

深拷貝是指在拷貝對象時,同時對引用指向的對象進行拷貝。

淺拷貝和深拷貝的區(qū)別就在于是否拷貝了對象中的引用變量所指向的對象。

2)public boolean equals(Object obj) 判斷另一對象與此對象是否「相等」。

該方法使用的區(qū)分度最高的“==”操作符進行判斷,所以只要兩個對象不是同一個對象,那么 equals() 方法一定返回 false。

《阿里巴巴 Java 開發(fā)手冊》上強調:由于 Object 的 equals 方法容易拋出空指針異常,所以應該使用常量或者確定不為 null 的對象來調用 equals。

正例:"test".equals(object);
反例:object.equals("test");

在正式的開發(fā)項目當中,最經常使用該方法進行判斷的就是字符串。不過,建議使用org.apache.commons.lang3.StringUtils,不用擔心出現空指針異常。具體使用情況如下所示:

StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false

3)public native int hashCode() 返回此對象的哈希碼。hashCode() 是一個 native 方法,而且返回值類型是整行;實際上,該方法將對象在內存中的地址作為哈希碼返回,可以保證不同對象的返回值不同。

A native method is a Java method whose implementation is provided by non-java code.<br>
native 方法是一個 Java 調用非 Java 代碼的接口。該方法的實現由非 Java 語言實現,比如 C。這個特征并非 Java 所特有,其它的編程語言也有這個機制,比如 C++。

hashCode() 通常在哈希表中起作用,比如 HashMap。

向哈希表中添加 Object 時,首先調用 hashCode() 方法計算 Object 的哈希碼,通過哈希碼可以直接定位 Object 在哈希表中的位置。如果該位置沒有對象,可以直接將 Object 插入該位置;如果該位置有對象,則調用 equals() 方法比較這個對象與 Object 是否相等,如果相等,則不需要保存 Object;如果不相等,則將該 Object 加入到哈希表中。

4)protected void finalize() throws Throwable 當垃圾回收機制確定該對象不再被調用時,垃圾回收器會調用此方法。不過,fnalize 機制現在已經不被推薦使用,并且在 JDK 9 開始被標記為 deprecated(過時的)。

5)public final Class getClass() 返回此對象的運行時類。

當我們想知道一個類本身的一些信息(比如說類名),該怎么辦呢?這時候就需要用到 Class 類,該類包含了與類有關的信息。請看以下代碼:

Wanger wanger = new Wanger();
Class c1 = wanger.getClass();
System.out.println(c1.getName());
// 輸出 Wanger

6)public String toString() 返回此對象的字符串表示形式。

《阿里巴巴 Java 開發(fā)手冊》強制規(guī)定:POJO 類必須重寫 toString 方法;可以使用 Eclipse 直接生成,點擊 「Source」→「Generate toString」。示例如下:

class Wanger {
    private Integer age;

    @Override
    public String toString() {
        return "Wanger [age=" + age + "]";
    }

}

重寫 toString() 有什么好處呢?當方法在執(zhí)行過程中拋出異常時,可以直接調用 POJO 的 toString() 方法打印其屬性值,便于排查問題。

POJO(Plain Ordinary Java Object)指簡單的 Java 對象,也就是普通的 JavaBeans,包含一些成員變量及其 getter / setter ,沒有業(yè)務邏輯。有時叫做 VO (value - object),有時叫做 DAO (Data Transform Object)。

03、總結

本篇,我們先談了面向對象的重要特征繼承;然后談到了繼承的終極父類 Object。這些知識點都相當的重要,請務必深入理解!

上一篇:請用面向對象的思想,談一談這次面試的過程

下一篇:Java:接口和抽象類,傻傻分不清楚?

謝謝大家的閱讀,原創(chuàng)不易,喜歡就隨手點個贊

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI