標籤:rtu 可變 end 特性 多態 ati 找不到 邏輯 ...
今天被實習生問了這麼個問題:
在java中,static成員函數是否可以被重寫呢?
結論是,你可以在子類中重寫一個static函數,但是這個函數並不能像正常的非static函數那樣運行。
也就是說,雖然你可以定義一個重寫函數,但是該函數沒有多態特性。讓我們測試一下:
1 class testClass1{ 2 static void SMethod(){ 3 System.out.println("static in testClass1"); 4 } 5 } 6 class testClass2 extends testClass1{ 7 static void SMethod(){ 8 System.out.println("static in testClass2"); 9 }10 }11 public class MainClass{12 public static void main(String... args){13 testClass1 tc1=new testClass2();14 testClass2 tc2 =new testClass2();15 tc1.SMethod(); //輸出結果為 static in testClass116 tc2.SMethod(); //輸出結果為 static in testClass217 }18 }
從結果中可以看到,當我們用父類的執行個體引用(實際上該執行個體是一個子類)調用static函數時,調用的是父類的static函數。
原因在於方法被載入的順序。
當一個方法被調用時,JVM首先檢查其是不是類方法。如果是,則直接從調用該方法引用變數所屬類中找到該方法並執行,而不再確定它是否被重寫(覆蓋)。如果不是,才會去進行其它操作(例如動態方法查詢)
可能有的人一拍大腿,這不就是java的靜態/動態指派麼!
有點像,但還真不是,靜態指派與動態指派是用來確定重載和重寫邏輯的。在重載過程中,編譯器根據方法參數的靜態類型(比如tc1的靜態類型是class1,tc2的是class2,但本文這裡不是重載!)來確定使用方法的版本,這叫做靜態指派。動態指派是用於方法重寫的,比如我調用一個類A的方法f,如果該類有子類a,那麼我以a來調用f的時候,調用的實際是a.f而非A.f。
看起來還真的像動態指派是不是?但是結果不符合啊!
這裡的原因在於,動態指派時,我們實際是在討論Java的invokevirtual指令的行為:這個指令首先會去尋找調用者的運行時類型,然後在其方法表裡面尋找匹配的方法,如果找不到,再從其父類裡找。這個過程就是Java中方法重寫的本質,也就是動態指派。
而static方法是通過invokestatic指令來調用的。由於static方法是一種編譯期可知,運行期不可變的方法,所以儘管子類和父類都有同樣的方法名,而事實上它們是不同的方法,也是完全可以區分的方法。在調用static方法時,編譯器就會直接在類載入時把其符號引用解析為直接引用,不存在說子類找不到方法之後再去父類找這種行為,所以也叫解析調用。
這就是上面的例子中看起來像是重寫的方法卻沒有產生重寫的效果的原因。
全文完。
從Java繼承類的重名static函數淺談解析調用與指派