Java基礎24:對象向上轉型和向下轉型

前言:Java中的繼承機制使得一個類可以繼承另一個類,繼承的類稱爲子類,被繼承的類稱爲父類。在一個子類被創建的時候,首先會在內存中創建一個父類對象,然後在父類對象外部放上子類獨有的屬性,兩者合起來形成一個子類的對象,所以子類可以繼承父類中所有的屬性和方法,包括private修飾的屬性和方法,但是子類只是擁有父類private修飾的屬性和方法,卻不能直接使用它,也就是無法直接訪問到它(子類可以通過調用父類的public聲明的get方法來獲取父類的private屬性,但無法訪問父類的private方法)。同時子類可以對繼承的方法進行重寫(@Override),並且新建自己獨有的方法。

一、轉型的分類和動態綁定介紹

1、轉型的分類

轉型是在繼承的基礎上而言的,繼承是面嚮對象語言中,代碼複用的一種機制,通過繼承,子類可以複用父類的功能,如果父類不能滿足當前子類的需求,則子類可以重寫父類中的方法來加以擴展。

向上轉型:子類引用的對象轉換爲父類類型稱爲向上轉型。通俗地說就是用父類的引用變量去引用子類的實例對象,此處父類對象可以是接口。向上轉型屬於自動類型轉換。

向下轉型:父類引用的對象轉換爲子類類型稱爲向下轉型。向下轉型屬於強制類型轉換。

2、動態綁定介紹

程序綁定的概念:綁定指的是一個方法的調用與方法所在的類(方法主體)關聯起來。對Java來說,綁定分爲靜態綁定和動態綁定,或者叫做前期綁定和後期綁定

靜態綁定:在程序執行前方法已經被綁定,此時由編譯器或其它連接程序實現。例如:C。針對Java簡單的可以理解爲程序編譯期的綁定;這裏特別說明一點,Java當中的方法只有final,static,private和構造方法是前期綁定
動態綁定:

動態綁定:在運行時根據具體對象的類型進行綁定。若一種語言實現了後期綁定,同時必須提供一些機制,可在運行期間判斷對象的類型,並分別調用適當的方法。也就是說,編譯器此時依然不知道對象的類型,但方法調用機制能自己去調查,找到正確的方法主體。不同的語言對後期綁定的實現方法是有所區別的。但我們至少可以這樣認爲:它們都要在對象中安插某些特殊類型的信息。

動態綁定的過程:JVM虛擬機提取對象的實際類型的方法表—>JVM虛擬機搜索方法簽名—>調用方法。

二、向上轉型

1、前篇文章提到過,在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備調用該子類的方法的能力,其實這就是向上轉型。

向上轉型,就是用父類的引用變量去引用子類的實例,這是允許的。當向上轉型之後,父類引用變量可以訪問子類中屬於父類的屬性和方法,但是不能訪問子類獨有的屬性和方法,因爲向上轉型後子類對象會丟失身份。

2、舉個栗子:

假設有一個Fruit類,Fruit類中有一個show()方法,代碼如下:

class Fruit{
	public void show() {
		System.out.println("this is a fruit");
	}
}

有一個Apple類繼承自Fruit類,該類有自己的方法test(),並且重寫了父類的show()方法,代碼如下:

class Apple extends Fruit{
    @Override
    public void show() {
	System.out.println("this is a apple");
    }
    public void test() {
	System.out.println("I am a apple");
    }
}

實例化Apple類,並新建一個Fruit類的引用變量引用該實例,調用實例的show()方法:

Fruit fruit = new Apple();//自動類型轉換
fruit.show();

運行結果如下圖所示:

向上轉型需要注意的問題:

向上轉型時,父類指向子類引用對象會遺失除與父類對象共有的其他方法,也就是在轉型過程中,子類的新有的方法都會遺失掉,在編譯時,系統會提供找不到方法的錯誤。

所以,子類對象當成父類對象,只能調用父類的成員,如果子類重寫了父類的方法,就根據這個引用指向調用子類重寫的這個方法。這個調用過程就稱爲“動態綁定”。

三、向下轉型

並不是所有的對象都可以向下轉型,只有當這個對象原本就是子類對象通過向上轉型得到的時候才能夠成功轉型。

/*
*  第一段代碼
**/
Fruit fruit = new Apple();
Apple apple = (Apple) fruit;//強制類型轉換

/*
*  第二段代碼
**/
class Orange extends Fruit{
    @Override
    public void show() {
	System.out.println("this is a Orange");
    }
    public void test() {
	System.out.println("i am a Orange");
    }
}

 Fruit fruit = new Apple();
 Orange orange = (Orange) fruit;//強制類型轉換

代碼分析:

第一段代碼向下轉型成功:因爲fruit引用的對象原本就是Apple對象向上轉型得到的,在對fruit向下轉型後得到的還是Apple類的對象,能夠被Apple類的引用變量引用;

第二代碼向下轉型失敗:上述代碼雖然能夠編譯成功,但是在運行的時候會報錯,因爲fruit對象是由Apple對象向上轉型得到的,只能夠向下轉型成Apple對象,不能夠向下轉型成Orange對象。

總結:

情況一:如果父類引用的對象如果引用的是指向的子類對象,那麼在向下轉型的過程中是安全的。也就是編譯是不會出錯誤的。

情況二:如果父類引用的對象是父類本身或者原本不是子類對象,那麼在向下轉型的過程中是不安全的,編譯不會出錯,但是運行時會出現java.lang.ClassCastException錯誤。它可以使用instanceof來避免出錯此類錯誤。

四、轉型的好處

通過向上向下轉型肯定是有好處的,比如可以減少編程代碼。

假設在主類中定義了一個run()方法,該方法傳入一個Fruit參數,並調用了Fruit對象的show()方法,代碼如下:

public static void run(Fruit fruit) {
	fruit.show();
   }

在main()方法中的代碼如下:

public static void main(String[] args) {
    run(new Fruit());
    run(new Apple());
    run(new Orange());
    }

上述代碼中,調用run()方法時的參數不僅是Fruit對象,也可以是Apple對象和Orange對象,當傳入的是Apple對象和Orange對象時,就會向上轉型成Fruit對象,但是調用的show()方法還是Apple對象和Orange對象的show()方法。這樣就不需要在主類中同時重載三個run()方法,減少了代碼量
 

注意:向上轉型常用於多態中,既能減少代碼量,又能提高程序的擴展性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章