2013-10-04 46 views
0

根據我對動態綁定的理解,JVM在運行時會查看對象的實際類型,並在該類中搜索實現並通過繼承級別進行工作。誤解動態綁定

例如,如果我們有:Vehicle v = new Car();

假設類Car延伸Vehicle,我們可以看到,參考變量類型是車輛和物體類型是汽車。

如果我們說:v.start()

的JVM會尋找開始實施的方法首先在汽車類,然後在Vehicle類。

這方面的一個例子是這樣的代碼:

public class scratch{ 
    public static void main(String [] args){ 
     Vehicle v = new Car(); 
     v.start(); 
    } 
} 

class Vehicle{ 
    public void start(){ 
     System.out.println("Vehicle class"); 
    } 
} 

class Car extends Vehicle{ 
    public void start(){ 
     System.out.println("Car class"); 
    } 
} 

這段代碼的輸出,符合市場預期是:「汽車類」

這是我的問題:如果我參加了啓動方法從類車輛,完全擦除它,程序將不會運行了。根據我對動態綁定的理解,JVM仍然應該查看對象的實際類型(在本例中爲Car),並仍然運行start方法的汽車實現。但是,它不這樣做。

爲什麼?

+0

因爲該方法不存在。你會得到一個[NoSuchMethodError](http://docs.oracle.com/javase/7/docs/api/java/lang/NoSuchMethodError.html) –

+1

恕我直言,如果你刪除'開始'功能,並做'車v = new Car()'這將是一個編譯器錯誤。由於編譯器檢查LHS對象以驗證調用的函數是否存在於其中。 –

+0

@VusP它取決於他是否修改.class或.java。但是,是的,編譯錯誤更可能。 –

回答

1

Vehicle中刪除start()的問題與多態性有關。在Vehicle中,如果您在此處定義start(),那麼所有Vehicle即使是子類都具有該方法。

如果從Vehicle()刪除start(),那麼就不能保證任何Vehiclestart()方法,即使我們知道這是一個Car確實有start()。如果有HorselessCarriage類延伸Vehicle但未定義start()會怎麼樣?然後,沒有start()方法。因此,如果Vehicle上沒有start()方法,則不能通過Vehicle變量調用start()

的是能夠調用start()Vehicle整點是確保任何Vehicle實現有一個start()方法調用。

UPDATE

的JVM拍攝對象的運行時類型和查找匹配方法調用的簽名的方法。如果沒有找到,它會繼承樹到超類,並在那裏尋找方法。

更多細節給出在JLS, Section 15.12.4.4

設X是所述方法 調用的目標參考的編譯時間類型。然後:

如果類S包含名爲米 具有相同描述符(相同數目的參數,同樣 參數類型和相同的返回類型)非抽象方法,通過該方法 調用所要求的聲明在編譯時確定(第15.12.3節),則:

如果調用模式是super或interface,那麼這是要調用的方法 ,並且過程終止。

如果調用模式是虛擬的,並且S中的聲明重寫 (§8.4.8.1)X.m,則在S中聲明的方法是調用的方法爲 ,過程終止。

如果調用模式是虛擬的,並且S中的聲明並不是 重寫X.m,而且X.m被聲明爲抽象,那麼將拋出一個 AbstractMethodError。否則,如果S有超類,則使用S的直接超類代替S遞歸地執行相同的查找過程 ; 要調用的方法是此查找過程的遞歸調用的結果 。

這裏,S看起來是對象的運行時類型。

+0

這是有道理的,我甚至在寫這個問題之前就懷疑它。但是我不得不問,動態綁定是如何工作的?在決定調用什麼方法時,JVM從哪裏開始? –

+0

@ user121834我已經更新了我的答案。 – rgettman

+0

@rgettman感謝您的確認。這是我認爲它的工作原理,但還沒有查找! – aglassman

0

當您在Vehicle類中有start()方法時,Car將重寫該方法。當您從Vehicle中移除start()方法時,您不再覆蓋該方法。所以調用v.start()沒有方法可以調用。這就是爲什麼你應該使用@Override,以便在代碼中清楚發生了什麼。在Vehicle中沒有start()方法的情況下,爲了在Car上調用start(),首先必須將車輛投射到Car類。

+0

我將以同樣的方式回覆我對rgettman的評論。這是有道理的,我期待這一點,但是在運行時,JVM在決定運行哪個方法實現時啓動哪個類? –

+0

在運行時,我認爲它從實際對象的類開始。所以在這種情況下,首先看Car。 – aglassman

1

在簡而言之,JVM需要一個端點開始爲start方法的參考搜索,不管對象類型有要調用一個方法,JVM需要一面鏡子,以確保您試圖調用現有的方法。

+0

我不完全明白。在這種情況下,JVM選擇的端點是什麼?另外,鏡子是什麼意思? –

+0

動態鏈接必須有一個開始點,並在[虛擬表](http://en.wikipedia.org/wiki/Virtual_method_table)中找到對該方法的適當引用,以便如果嘗試訪問方法這個變量不存在於這個變量中,只是JVM沒有通過編譯面,因爲這個變量沒有這個方法。 – xhamr

0

但是,一旦您刪除該方法,車輛不再具有任何「啓動」功能:它是Vehicle類中的未知方法,並且您通過Vehicle引用訪問它。讓Java做你想要什麼,你可以做

abstract class Vehicle 
{ 
    public abstract void start(); 
} 

您的代碼應該然後再工作,因爲車輛的所有的孩子現在都保證一個啓動方法。但是,在您的示例中,一旦啓動方法被移除,不能保證前面的某個語句未創建其他Vehicle後代,例如沒有啓動方法的Motorcycle並將其分配給v引用。

0

我認爲一個簡單的方法來看問題是介紹一種方法。下面是該方法的定義:

public void callStart(Vehicle vehicle) { 
    vehicle.start(); 
} 

這種方法可以讓你在一個具體的Car或混凝土Vehicle通過。

讓我們假裝Java讓你編譯這段代碼。如果 Java允許您在沒有start()方法的情況下爲Vehicle執行此操作,那麼您必須在運行時發現錯誤。但是Java通過讓你知道什麼時候編譯出現錯誤,爲你節省了一些時間。

這不同於一些動態語言,如Javascript。如果這是JavaScript,你可以通過一個具體的Vehicle,然後你必須在運行時發現你的錯誤。另一個區別是,在JavaScript中,你可以傳遞一個具體的Car,它會工作沒有錯誤。這被稱爲duck typing,是Java沒有的功能。