java 重载 不可_深入深刻深到不能再深的理解java中的重载和重写
面向對象的三個特性
面向對象有三個重要的特性:多態,繼承,封裝。
多態的表現
多態的在java中的應用體現在方法的重載和重寫。
重載:字面上的意義一個類里面,有同名但是參數必須不相同的方法。(這里的”不同參數“必須不是泛型,比如List 和List)
重寫:對應的是子類去重寫了父類的方法,有自己的實現,不能更改方法的參數和名稱。
但是在JVM中是怎么實現的呢?JVM對于這兩種方式是怎么區分的?
方法的重載
我們都知道,.java的源文件都會被編譯成.class的文件,class文件是一堆二進制流。
執行 方法的指令在JVM里面有四個
invokestatic 執行靜態的方法
invokespecial 執行構造方法,或者調用父類的方法(對應super關鍵字)
invokedynamic 執行動態語言的方法
invokevirtual 執行普通方法
考慮下面代碼
class A{
pulic void print(B b){
}
pulic void print(C c){
}
}
public static void main(String args[]){
A a = new A();
B b = new C();
a.print(b);
}
當我們調用 a.print(b)的時候,虛擬機會根據你傳遞參數的靜態類型去匹配方法,這是一個靜態分派的過程。
什么叫靜態類型 比如 B b = new C();C是派生B的,我們經常在java中這么寫,沒有任何問題。這里對象B的靜態類型就是B,但是它的實際類型是C。
我們調用a.print(b)的時候它匹配的是 print(B b)這個方法,所以編譯器它不會考慮這個b的實際類型(因為B的實際類型會被執行invospecial去指定)是什么,它直接在編譯成字節碼階段就把這個方法匹配了。
它的字節碼
image.png
但是還有一個點要注意,靜態類型是可以強轉的
public static void main(String args[]){
A a = new A();
B b = new C();
a.print((C)b);
}
這時候它匹配的就是print(C c)這個方法了
image.png
方法重載:只會根據參數的靜態類型去匹配。
方法的重寫
方法的重寫:表現的就是重寫父類的方法
public class B {
public void callMe(){
System.out.print("B is call");
}
}
public class C extends B {
@Override
public void callMe() {
System.out.print("C is call");
}
}
public static void main(String args[]){
B b = new B();
b.callMe();
B c = new C();
c.callMe();
}
在編譯階段它只會在靜態類型中去匹配方法,所以盡管b和c 的實際類型不同,但它們執行callMe的字節碼指令是相同的
image.png
但是虛擬機在執行這個指令的時候,它會先在操作數棧中拿到調用這個方法的對象,然后根據這個對象的實際類型去匹配方法,如果這個類型里面沒有,則它就會去父類去找一直遞歸直到找到為止。否則將會拋出MethodNotFoundException。
這樣遞歸非常麻煩,所以虛擬機會對每一個clas對象都生成一個方法表。(這個在連接階段會準備好,一般再類變量初始為零值之后)
它里面保存了這個類所有方法直接引用(也是就能再虛擬機中找到執行的代碼),保存的順序先是父類的方法,然后才是自己的方法,如果子類重寫了父類的方法,它會將這個指針指向自己的實現,否則指向父類,這樣的好處是只用查找一次,不管類型怎么切換,只要他們的方法表的順序是一致的,我就可以根據這個下標去找到對應的方法入口。
image.png
比如使用son的對象 son.hardChoice(),會再son的方法表里面找到方法入口,虛擬機會記住這個下標,當我再使用
father.hardChoice()的時候,直接切換到Father的方法表,通過記住的這個下標找到方法入口,不用去遍歷了!
同理接口也有對應的接口表,它里面的方法就是無序的了,無法通過下標去查找,每一次調用方法都只能去遍歷整個表,所以一般來說查找接口方法比查找普通方法要慢。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java 重载 不可_深入深刻深到不能再深的理解java中的重载和重写的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IC卡复位应答ATR的数据元和它们的意义
- 下一篇: sys下gpio操作