參考:
多態的底層實現
Java 的方法調用方式
Java 的方法調用有兩類,動態方法調用與靜態方法調用。靜態方法調用是指對於類的靜態方法的調用方式,是靜態綁定的;而動態方法調用需要有方法調用所作用的對象,是動態綁定的。類調用 (invokestatic) 是在編譯時刻就已經確定好具體調用方法的情況,而實例調用 (invokevirtual) 則是在調用的時候才確定具體的調用方法,這就是動態綁定,也是多態要解決的核心問題。
JVM 的方法調用指令有四個,分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個是靜態綁定,后兩個是動態綁定的。本文也可以說是對於 JVM 后兩種調用實現的考察。
實現原理
類調用方法使用的是invokesvirtual 指令。
父類和子類相同的方法的符號引用在各自方法表中偏移量是一樣的,比如父類的toString()方法的符號引用在父類的方法表中的偏移量是10,那子類的toString()方法的符號引號在子類方法表中的偏移量也是10,那么調用方法時,JVM根據方法簽名在字符串常量池中確定符號引用,然后先找對象的符號引用類型(也就是父類類型)方法表中該符號引用的偏移量,然后執行對象的實際類型的(也就是子類類型)方法表中相同偏移量的方法。
類調用方法使用的是invokeinterface指令。
但是接口和類的是實現有點不一樣,因為類是單繼承,所以可以讓子類和父類保持相同的方法的符號引用在各自方法表中的偏移量一樣,但是因為接口是多實現,可以同時實現多個接口,所以不能保證相同的方法的符號引用在各自方法表中的偏移量一樣,所以在查找到接口的符號引用的偏移量之后,需要根據根據得到的符號引用去子類的方法表中查詢,找到等價的符號引用,然后執行子類的方法。因為多一個查找的步驟,所以接口的多態調用一般比類的多態調用慢一些。