文章目錄
- 概念:
-
- JAVA動态綁定的内部實作機制
- 總結:
-
- java程式設計思想——Java中子類是否可以繼承父類的static變量和方法而呈現多态特性
- 參考資料:
概念:
方法可以在沿着繼承鍊的多個類中實作,子類可以重寫父類的方法。JVM決定運作時調用哪個方法。這就是動态綁定。
1、将一個方法調用同一個方法主體關聯起來被稱作綁定。
2、若程式在執行前進行綁定,由編譯器和連結程式實作,叫做前期綁定。C語言中隻有一種方法調用,就是前期綁定。
3、在運作時根據對象的類型進行綁定,叫做後期綁定,也叫動态綁定或運作時綁定,反之叫做前期綁定,也叫靜态綁定。
4、Java中除了static方法和final方法(private方法被自動認為是final方法)之外,其他所有的方法都是後期綁定。
這句話很重要,在我們使用多态時,即将子類對象賦給父類引用時,通過父類引用調用某一方法。這個時候就會出現“方法調用綁定”問題 (這是面向對象程式設計的最重要的妙訣) 。如果此方法不是static或者final(private)的,那麼綁定是後期綁定,即在編譯時(在編譯期,隻是確定調用方法的存在,并對調用參數和傳回值執行類型檢查),方法調用和方法體沒有關聯起來,隻要到了運作時,根據引用所指向的對象類型,将方法調用和方法體關聯起來。在這個過程中有許多值得注意的地方。
- 父類中有一個方法f(),如果子類中Override(覆寫)了父類的f()方法,那麼在運作期方法調用關聯到的方法體是子類中的f()
- 如果子類沒有Override父類的f(),那麼在運作期方法調用關聯到的方法體是子類從父類中繼承過來的f()。
- 大家有沒有想過一種情況:假如父類中沒有f()方法,子類中有,在多态中,方法調用f()時,會出現什麼情況呢?其實大家可以做一下實驗,結果就是會編譯出錯!其實很好了解,因為在編譯期,編譯器隻知道,父類引用調用了f()方法,如果父類中沒有f()方法,當然會編譯出錯呀!
一般情況說完了,說一下特殊的情況,關于final的,首先看一下下面的例子:
package com.sailang.polymorphism;
public class PrivateOverride
{
private void f()
{
System.out.println("private f()");
}
public static void main(String[] args)
{
PrivateOverride po = new Derived();
po.f();
}
}
class Derived extends PrivateOverride
{
public void f()
{
System.out.println("public f()");
}
}
大家覺得結果會是什麼呢?
結果是private f()。如果各位看懂了我上面的講解,應該就不難了解了吧!PrivateOverride中的f()方法時private的,即是final的,po.f()是前期綁定,在編譯期方法調用就直接綁定到了PrivateOverride的f()方法體上了。進一步分析,PrivateOverride的f()方法時final的,從繼承角度考慮,子類是不會繼承f()方法的。Derived中的f()方法是一個全新的方法。
如果方法是static的,看下面的例子:
package com.sailang.polymorphism;
public class StaticPolymorphism
{
public static void main(String[] args)
{
StaticSuper sup = new StaticSub();
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
class StaticSuper
{
public static String staticGet()
{
return "Base staticGet()";
}
public String dynamicGet()
{
return "Base dynamicGet()";
}
}
class StaticSub extends StaticSuper
{
public static String staticGet()
{
return "Derived staticGet()";
}
public String dynamicGet()
{
return "Derived dynamicGet()";
}
}
輸出:
Base staticGet()
Derived dynamicGet()
結果很容易知道!
不過我想問一下大家,子類中的dynamicGet()是Override了父類的dynamicGet()方法,對吧!那麼子類中的staticGet()方法覆寫了父類的staticGet()方法了嗎?大家可以做一下實驗。我實驗的結果是,static方法既不能被覆寫也不能被繼承!不知道這個結論對不對!
5、後期綁定是java中實作的多态的基礎。注意多态隻是對方法調用起作用的,對域(成員變量)是不起作用的,即對域的調用是編譯器解析的。(這方面的知識,我就不寫了,大家可以參考《Java程式設計思想》中文第四版的P156-157)
JAVA動态綁定的内部實作機制
JAVA虛拟機調用一個類方法時,它會基于對象引用的類型(通常在編譯時可知)來選擇所調用的方法。相反,當虛拟機調用一個執行個體方法時,它會基于對象實際的類型(隻能在運作時得知)來選擇所調用的方法,這就是動态綁定,是多态的一種。動态綁定為解決實際的業務問題提供了很大的靈活性,是一種非常優美的機制。
1 、JAVA對象模型
JAVA虛拟機規範并沒有規定JAVA對象在堆裡是如何表示的。對象的内部表示也影響着整個堆以及垃圾收集器的設計,它由虛拟機的實作者決定。
JAVA對象中包含的基本資料由它所屬的類及其所有超類聲明的執行個體變量組成。隻要有一個對象引用,虛拟機就必須能夠快速地定位對象執行個體的資料。另外,它也必須能通過該對象引用通路相應的類資料(存儲于方法區的類型資訊),是以在對象中通常會有一個指向方法區的指針。當程式在運作時需要轉換某個對象引用為另外一種類型時,虛拟機必須要檢查這種轉換是否被允許,被轉換的對象是否的确是被引用的對象或者它的超類型。當程式在執行instanceof操作時,虛拟機也進行了同樣的檢查。是以虛拟機都需要檢視被引用的對象的類資料。
不管虛拟機的實作使用什麼樣的對象表示法,很可能每個對象都有一個方法表因為方法表加快了調用執行個體方法時的效率。但是JAVA虛拟機規範并未要求必須使用方法表,是以并不是所有實作中都會使用它。
下面是一種JAVA對象的記憶體表示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TP35UMJpnT5tGRPpHOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxIjMxAjM1YTMyAzNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
方法資料存放在類的方法區中,包含一個方法的具體實作的位元組碼二進制。方法指針直接指向這個方法在記憶體中的起始位置,通過方法指針就可以找到這個方法。
2 動态綁定内部機制
方法表是一個指向方法區中的方法指針的數組。方法表中不包含static、private等靜态綁定的方法,僅僅包含那些需要動态綁定的執行個體方法。
在方法表中,來自超類的方法出現在來自子類的方法之前,并且排列方法指針的順序和方法在class檔案中出現的順序相同,這種排列順序的例外情況是,被子類的方法覆寫的方法出現在超類中該方法第一次出現的地方。
總結:
1、動态綁定展現了Java的繼承與多态
2、在運作時根據對象的類型進行綁定,叫做後期綁定,也叫動态綁定或運作時綁定,反之叫做前期綁定,也叫靜态綁定。
3、Java中除了static方法和final方法(private方法被自動認為是final方法)之外,其他所有的方法都是後期綁定。
4、static方法既不能被覆寫也不能被繼承
java程式設計思想——Java中子類是否可以繼承父類的static變量和方法而呈現多态特性
參考資料:
方法調用綁定–前期綁定和後期綁定