《Java 核心技术卷1 第10版》学习笔记------ -理解方法调用【重载解析、静态绑定、动态绑定】
弄清楚如何在對象上應用方法調用非常重要。下面假設要調用 x.f(args,) 隱式參數 x 聲明為類 C 的一個對象。下面是調用過程的詳細描述:
1 ) 編譯器査看對象的聲明類型和方法名。假設調用 x.f(param,) 且隱式參數 x 聲明為 C類的對象。需要注意的是: 有可能存在多個名字為 f, 但參數類型不一樣的方法。例如,可能存在方法 f(im) 和方法 String。) 編譯器將會一一列舉所有 C 類中名為 f 的方法和其超類中訪問屬性為 public 且名為 f 的方法(超類的私有方法不可訪問)。
至此, 編譯器已獲得所有可能被調用的候選方法。
2 ) 接下來,編譯器將査看調用方法時提供的參數類型。如果在所有名為 f 的方法中存在一個與提供的參數類型完全匹配, 就選擇這個方法。這個過程被稱為重載解析( overloadingresolution)。 例如,對于調用 x.f(“ Hello” )來說, 編譯器將會挑選 f(String,) 而不是 f(int。)由于允許類型轉換( int 可以轉換成 double, Manager 可以轉換成 Employee, 等等,) 所以這個過程可能很復雜。 如果編譯器沒有找到與參數類型匹配的方法, 或者發現經過類型轉換后有多個方法與之匹配, 就會報告一個錯誤。
至此, 編譯器已獲得需要調用的方法名字和參數類型。
注釋: 前面曾經說過,方法的名字和參數列表稱為方法的簽名。例如, f(int) 和 f(String)是兩個具有相同名字, 不同簽名的方法。如果在子類中定義了一個與超類簽名相同的方法, 那么子類中的這個方法就覆蓋了超類中的這個相同簽名的方法。
不過, 返回類型不是簽名的一部分, 因此, 在覆蓋方法時, 一定要保證返回類型的兼容性。 允許子類將覆蓋方法的返回類型定義為原返回類型的子類型。 例如, 假設Employee 類有
public Employee getBuddyO { . . . }
經理不會想找這種地位低下的員工。 為了反映這一點, 在后面的子類 Manager 中,可以按照如下所示的方式覆蓋這個方法
public Manager getBuddyO { . . . } // OK to change return type
我們說, 這兩個 getBuddy 方法具有可協變的返回類型。
3 ) 如果是 private 方法、 static 方法、 final 方法(有關 final 修飾符的含義將在下一節講述)或者構造器, 那么編譯器將可以準確地知道應該調用哪個方法, 我們將這種調用方式稱為靜態綁定(static binding )。 與此對應的是,調用的方法依賴于隱式參數的實際類型, 并且在運行時實現動態綁定。在我們列舉的示例中, 編譯器采用動態綁定的方式生成一條調用 f(String) 的指令。
4 ) 當程序運行,并且采用動態綁定調用方法時, 虛擬機一定調用與 x 所引用對象的實際類型最合適的那個類的方法。假設 x 的實際類型是 D,它是 C 類的子類。如果 D 類定義了方法 f(String,) 就直接調用它;否則, 將在 D 類的超類中尋找 f(String,) 以此類推。每次調用方法都要進行搜索,時間開銷相當大。因此, 虛擬機預先為每個類創建了一個方法表(method table), 其中列出了所有方法的簽名和實際調用的方法。這樣一來,在真正調用方法的時候, 虛擬機僅查找這個表就行了。在前面的例子中, 虛擬機搜索 D 類的方法表, 以便尋找與調用 f(Sting) 相K配的方法。這個方法既有可能是 D.f(String), 也有可能是X.f(String), 這里的 X 是 D 的超類。這里需要提醒一點, 如果調用 super.f(param), 編譯器將對隱式參數超類的方法表進行搜索。
現在, 查看一下程序清單 5-1 中調用 e.getSalary() 的詳細過程。e 聲明為 Employee 類型。Employee 類只有一個名叫 getSalary 的方法, 這個方法沒有參數。 因此, 在這里不必擔心重載解析的問題。
由于 getSalary 不是 private 方法、 static 方法或 final 方法,所以將采用動態綁定。虛擬機為 Employee 和 Manager 兩個類生成方法表。在 Employee 的方法表中, 列出了這個類定義的所有方法:
? ? ? ? ? ?Employee:
? ? ? ? ? ?getNameO?> Employee.getNameO
? ? ? ? ? ?getSalaryO -> Employee.getSalaryO
? ? ? ? ? ?getHireDayO -> Employee.getHireDayO
? ? ? ? ? ?raiseSalary(double) -> Employee. raiseSal ary(doubl e)
實際上, 上面列出的方法并不完整, 稍后會看到 Employee 類有一個超類 Object,Employee 類從這個超類中還繼承了許多方法,在此,我們略去了 Object 方法。
Manager?方法表稍微有些不同。其中有三個方法是繼承而來的,一個方法是重新定義的,還有一個方法是新增加的。
? ? ? ? ? ?Manager:
? ? ? ? ? ?getNameO -> Employee.getNameO
? ? ? ? ? ?getSalaryO -> Manager.getSalary0
? ? ? ? ? ?getHireDayO -> Employee.getHireDayO
? ? ? ? ? ?raiseSalary(double) -> Employee.raiseSal ary(double)
? ? ? ? ? ?setBonus(double) -> Manager.setBonus(double)
在運行時, 調用 e.getSalaryO 的解析過程為:
1 ) 首先, 虛擬機提取 e 的實際類型的方法表。既可能是 Employee、 Manager 的方法表,也可能是 Employee 類的其他子類的方法表。
2 ) 接下來, 虛擬機搜索定義 getSalary 簽名的類。此時, 虛擬機已經知道應該調用哪個方法。
3 ) 最后,虛擬機調用方法。動態綁定有一個非常重要的特性: 無需對現存的代碼進行修改,就可以對程序進行擴展。假設增加一個新類 Executive, 并且變量 e 有可能引用這個類的對象, 我們不需要對包含調用e.getSalary() 的代碼進行重新編譯。 如果 e 恰好引用一個 Executive 類的對象,就會自動地調用 Executive.getSalaryO 方法。
警告: 在覆蓋一個方法的時候,子類方法不能低于超類方法的可見性。特別是, 如果超類方法是 public, 子類方法一定聲明為 public。經常會發生這類錯誤:在聲明子類方法的時候, 遺漏了 public 修飾符。此時,編譯器將會把它解釋為試圖提供更嚴格的訪問權限
總結
以上是生活随笔為你收集整理的《Java 核心技术卷1 第10版》学习笔记------ -理解方法调用【重载解析、静态绑定、动态绑定】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Java 核心技术卷1 第10版》学习
- 下一篇: Java Enum 使用