好久以前一直有一個疑問,最近從新翻了遍JVM的書,纔算是終於有所頓悟。問題以下:java
1 package org.hanyan.test.testClass; 2 3 public class T3AboutThis { 4 public static void main(String[] args) { 5 new SubT3().func(); 6 SuperT3 s = new SuperT3(); 7 System.out.println(s instanceof SubT3);//false 8 } 9 } 10 11 class SuperT3 { 12 public String name = "Jack"; 13 14 protected void func() { 15 System.out.println(this.name);//Jack 16 System.out.println(this instanceof SuperT3);//true 17 System.out.println(this instanceof SubT3);//true 18 } 19 } 20 class SubT3 extends SuperT3 { 21 public String name = "Peter"; 22 }
一直沒搞明白,上述代碼中第17行爲何打印出來的true。jvm
javap 打印了SuperT3類編譯後的字節碼指令以下:this
第1三、23字節的指令均爲同一個引用變量,這個引用變量咱們能夠在本地變量表中看到是Lorg/hanyan/test/testClass/SuperT3,spa
這裏搞不明白爲何1四、24字節的instanceof指令會返回一樣的結果:truecode
目前懷疑是在運行中,jvm將本地變量this替換了。但這又引出一個問題,爲何this.name中的this沒有被替換?這個替換規則是怎麼樣的?blog
其實這個現象中有兩個點須要說明:get
1.關於17行爲何會打印出true?虛擬機
這個是虛擬機字節碼指令invokevirtual的動態查找過程的表現。編譯
1三、23字節的指令的參數均爲同一引用Lorg/hanyan/test/testClass/SuperT3,這裏我以前理解的不深入,其實這裏的引用是「符號引用」,指SuperT3類常量池中的某個CONTANT_Methodref_info類型的「符號引用」。但虛擬機執行引擎在運行期執行這個字節碼指令時,首先會查找這條指令的接收者(調用者)的實際類型,而後再肯定真正的「直接引用」指向哪裏。這個解析操做實現了方法的動態分派,也就是咱們常說的「方法重寫」。(具體「符號引用」解析爲「直接引用」的解釋請參考java的動態解析)。因此1三、23的字節碼指令的接收者實際是SubT3,即返回true。class
那麼就會引出第二個關注點,代碼的15行爲何是「Jack」?
2.代碼的15行爲何是「Jack」?
虛擬機字節碼指令getfeild,與invokevirtual不一樣,不存在動態查找過程。因此,getfeild字節碼指令的參數爲當前類的常量池的「符號引用」,此時的「符號引用」已在類加載過程當中的「解析」階段轉化爲「直接引用」,即表示當前類的變量name的值,因此返回「Jack」。
至此,整個解釋完畢。
總結,this關鍵字也只是在編譯好的字節碼文件中,方法的第一個參數(方法的接收者)的訪問符而已。整個問題的重點應該關注虛擬機如何執行這些字節碼指令。