首先,來看一個簡單的JAVA類,Base。
1 public class Base { 2 String str = "Base string"; 3 protected void show( ){ 4 System.out.println(str); 5 init(); 6 } 7 protected void init(){ 8 System.out.println("Base init"); 9 } 10 }
然后,從Base類中派生一個子類Sub。並且在Sub類中的測試方法mytest中調用show方法,該方法是從父類Base中繼承來的,其中,show方法里面訪問了名為"str"的實例字段。問題是,現在Base類和Sub類中,都定義了各自的“str”實例字段,按照“如果子類定義了與父類中同名的字段,那么子類的字段將隱藏父類的字段”,請注意這類的“隱藏”二字而不是“覆蓋”或“重寫”之類的。那么show方法中“System.out.println(str)”打印的到底是Sub中的str還是Base中的str呢?
1 public class Sub extends Base { 2 String str = "Sub string"; 3 4 @Test 5 public void mytest(){ 6 Sub sub = new Sub(); 7 sub.show(); 8 } 9 }
行mytest測試方法,結果如下:
Base string
Base init
可見,show方法中訪問的是Base中的str字段。這是為什么呢?也許你會覺得好笑,但是如果結合JVM底層機制,這個還是有些玄機的。為了解釋這個現象,我畫了了一個sub對象的內部布局草圖。
如下圖所示,僅考慮str實例字段時,sub對象中同時存在着兩個名字相同但是來源不同的str字段。因為“System.out.println(str)”相當於“System.out.println(this.str)”,那么“this.str”到底代表哪一個呢?因為兩個str都是this對象的成員。
通過查看Class反匯編文件可以發現,其實在class文件中,show方法中保存的是str的符號引用,該引用只可能是其自身或是其父類(接口)中字段的引用,因此不可能是對子類的字段引用(因為父類根本就不知道子類的存在)。而在類加載或程序運行的某個時刻的解析階段,JVM會將該符號引用解析為直接引用,解析的順序為,如果在該字段所屬的類(從CONSTANT_Fieldref_info中可以得到)中找到名字和類型相同的字段,就是用該字段的直接引用。如果沒有找到,就會繼續查找其父類或接口。在本例中,在Base類中就可以找到簡單名和類型都相符合的str字段,因此解析訪問的就是來自Base的str的直接引用(其實就是str實例字段在對象映像中的偏移量)。
show方法中還調用了init方法,由於該方法沒有被子類重寫,因此此處不會有任何異議。但是如果子類重寫了init方法,情況又會不同。代碼如下:
1 public class Sub extends Base { 2 String st = "Sub string"; 3 4 @Override 5 protected void init(){ 6 System.out.println("Sub init"); 7 } 8 @Test 9 public void mytest(){ 10 Sub sub = new Sub(); 11 sub.show(); 12 } 13 }
再次執行mytest測試方法,可以看見show方法中調用的是Sub類的init方法。
Base string
Sub init
那么方法調用和字段方法有什么不同呢?通過查看Base類的class反匯編文件,可以看見,調用init方法的指令為invokevirtual #14 ,該指令的參數就是init方法的符號引用(Base.virtual_init:()V),在解析階段,該符號引用也會像實例屬性解析那樣,被替換成方法的直接引用---方法表的偏移量(方法表中存儲着實際的方法地址)。但是不同的是,invokvirtual在執行時,會使用該偏移量在方法接收者實際類型的方法表中取得該方法的實際執行地址。
如下圖所示,假設Base中除了init還有其他的虛方法,假設init虛方法在虛方法表中的偏移量為1(這個偏移量就是init方法的直接引用),如果子類沒有重寫init方法,那么子類Sub的虛方法表中偏移量為1的地方保存的也是與父類Base相同的方法地址。因此,在執行init方法調用時,還是調用的父類的init方法。
但是,當子類Sub重寫了init方法時,也相應的修改了Sub的虛方法表中偏移量為1處的方法地址,使得該地址指向了子類Sub自己的init方法,因此,指向init方法調用,實際執行的是Sub自己實現的init方法,而不是父類Base的方法。
以下為C++編寫的相同的測試例子。首先,如果不講init函數聲明為virtual,那么無論子類Sub是否自己定義了init版本,父類Base中調用的init永遠都是父類中的init(靜態綁定)。
1 class Base { 2 public: 3 char * str = "Base string"; 4 void init() { 5 cout << "Base init" << endl; 6 } 7 void show() { 8 cout << str << endl; 9 init(); 10 } 11 }; 12 class Sub :public Base { 13 public: 14 char * str = "Sub string"; 15 void init() { 16 cout << "Sub init" << endl; 17 } 18 }; 19 int main() 20 { 21 Sub s; 22 s.show(); 23 return 0; 24 }
Base string Base init
當為init函數添加virtual聲明,使其成為一個虛函數時,此時Base類會產生和維護一個虛函數表。同理,派生的子類Sub也會有一個虛函數表,對虛函數的調用都是動態綁定的,與JAVA原理類似,都是使用虛函數在虛函數表中的索引偏移量來取得函數的實際地址。
1 class Base { 2 public: 3 char * str = "Base string"; 4 virtual void init() { 5 cout << "Base init" << endl; 6 } 7 void show() { 8 cout << str << endl; 9 init(); 10 } 11 };
Base string Sub init
下面提供一個比較全面的例子,測試了實例字段、靜態字段、虛方法、靜態方法的調用機制。並分別提供了Base和Sub的反匯編代碼。
1 /** 2 * Created by chen on 2016/3/21. 3 */ 4 public class Base { 5 String str = "Base string"; 6 int base_int = 4; 7 static String static_string = "static base string"; 8 public Base(){ 9 System.out.println("Base.this" + this); 10 } 11 12 protected void virtual_show( ) { 13 System.out.println(str);//Base string,省略了this 14 System.out.println(static_string);//static base string 15 virtual_init();//虛函數調用,會使用實際類型的虛方法表來調用,運行時綁定。 16 // 因為子類已經重寫了這個方法,因此在方法表中調用該方法時實際是子類覆蓋的方法 17 static_init();//靜態方法調用,靜態綁定,不會產生多態 18 static_init2();//靜態綁定,不會產生多態 19 } 20 /*這是一個虛方法,會被子類重寫覆蓋*/ 21 protected void virtual_init(){ 22 System.out.println("Base virtual init"); 23 } 24 /*這是一個靜態方法,會被子類隱藏*/ 25 protected static void static_init(){System.out.println("Base static init");} 26 /*這是一個靜態方法,不會被子類隱藏*/ 27 protected static void static_init2(){System.out.println("Base static init2");} 28 }
1 import org.junit.Test; 2 3 /** 4 * Created by chen on 2016/3/21. 5 */ 6 public class Sub extends Base { 7 String str = "Sub string";//隱藏父類實例字段 8 static String static_string = "static Sub string";//隱藏父類靜態字段 9 10 /*打印this,以父類打印的this作對比*/ 11 public Sub(){ 12 System.out.println("Sub.this" + this); 13 } 14 15 /*測試父類方法中調用被覆蓋的init方法,只有虛方法才會被重寫,所謂的重寫本質上是對虛方法表 16 * 中存儲的原方法地址的重寫、覆蓋,從而可以實現多態*/ 17 @Override 18 protected void virtual_init() { 19 System.out.println("Sub init"); 20 } 21 /*@Override 靜態方法的覆蓋不是重寫,只能說子類有一個同名的方法隱藏了父類的同名方法 22 * 該隱藏現象不會產生多態效果(當然,因為靜態對的根本就不會出現在虛方法表中)*/ 23 protected static void static_init(){ 24 System.out.println("Sub static init"); 25 } 26 27 public void show_field(){ 28 System.out.println(str);//Sub string 29 System.out.println(super.str);//Base string,使用super限定訪問父類中被隱藏了字段 30 System.out.println(static_string);//static Sub string 31 System.out.println(Base.static_string);//static base string 32 } 33 34 public void virtual_test(){ 35 /*訪問父類的字段*/ 36 System.out.println(base_int); 37 } 38 39 @Test 40 public void mytest(){ 41 Sub sub = new Sub(); 42 sub.virtual_show(); 43 sub.show_field(); 44 sub.static_init();//調用被 45 sub.static_init2(); 46 47 System.out.println("---------------------------------------"); 48 Base base = new Sub(); 49 base.virtual_show(); 50 } 51 }
Classfile /C:/Users/chen/Desktop/Base.class Last modified 2016-3-31; size 1272 bytes MD5 checksum 2320eb084b466a042491065dc8d9aaeb Compiled from "Base.java" public class Base minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #22.#42 // java/lang/Object."<init>":()V #2 = String #43 // Base string #3 = Fieldref #21.#44 // Base.str:Ljava/lang/String; #4 = Fieldref #21.#45 // Base.base_int:I #5 = Fieldref #46.#47 // java/lang/System.out:Ljava/io/PrintStream; #6 = Class #48 // java/lang/StringBuilder #7 = Methodref #6.#42 // java/lang/StringBuilder."<init>":()V #8 = String #49 // Base.this #9 = Methodref #6.#50 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #10 = Methodref #6.#51 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #11 = Methodref #6.#52 // java/lang/StringBuilder.toString:()Ljava/lang/String; #12 = Methodref #53.#54 // java/io/PrintStream.println:(Ljava/lang/String;)V #13 = Fieldref #21.#55 // Base.static_string:Ljava/lang/String; #14 = Methodref #21.#56 // Base.virtual_init:()V #15 = Methodref #21.#57 // Base.static_init:()V #16 = Methodref #21.#58 // Base.static_init2:()V #17 = String #59 // Base virtual init #18 = String #60 // Base static init #19 = String #61 // Base static init2 #20 = String #62 // static base string #21 = Class #63 // Base #22 = Class #64 // java/lang/Object #23 = Utf8 str #24 = Utf8 Ljava/lang/String; #25 = Utf8 base_int #26 = Utf8 I #27 = Utf8 static_string #28 = Utf8 <init> #29 = Utf8 ()V #30 = Utf8 Code #31 = Utf8 LineNumberTable #32 = Utf8 LocalVariableTable #33 = Utf8 this #34 = Utf8 LBase; #35 = Utf8 virtual_show #36 = Utf8 virtual_init #37 = Utf8 static_init #38 = Utf8 static_init2 #39 = Utf8 <clinit> #40 = Utf8 SourceFile #41 = Utf8 Base.java #42 = NameAndType #28:#29 // "<init>":()V #43 = Utf8 Base string #44 = NameAndType #23:#24 // str:Ljava/lang/String; #45 = NameAndType #25:#26 // base_int:I #46 = Class #65 // java/lang/System #47 = NameAndType #66:#67 // out:Ljava/io/PrintStream; #48 = Utf8 java/lang/StringBuilder #49 = Utf8 Base.this #50 = NameAndType #68:#69 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #51 = NameAndType #68:#70 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #52 = NameAndType #71:#72 // toString:()Ljava/lang/String; #53 = Class #73 // java/io/PrintStream #54 = NameAndType #74:#75 // println:(Ljava/lang/String;)V #55 = NameAndType #27:#24 // static_string:Ljava/lang/String; #56 = NameAndType #36:#29 // virtual_init:()V #57 = NameAndType #37:#29 // static_init:()V #58 = NameAndType #38:#29 // static_init2:()V #59 = Utf8 Base virtual init #60 = Utf8 Base static init #61 = Utf8 Base static init2 #62 = Utf8 static base string #63 = Utf8 Base #64 = Utf8 java/lang/Object #65 = Utf8 java/lang/System #66 = Utf8 out #67 = Utf8 Ljava/io/PrintStream; #68 = Utf8 append #69 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #70 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder; #71 = Utf8 toString #72 = Utf8 ()Ljava/lang/String; #73 = Utf8 java/io/PrintStream #74 = Utf8 println #75 = Utf8 (Ljava/lang/String;)V { java.lang.String str; descriptor: Ljava/lang/String; flags: int base_int; descriptor: I flags: static java.lang.String static_string; descriptor: Ljava/lang/String; flags: ACC_STATIC public Base(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: ldc #2 // String Base string 7: putfield #3 // Field str:Ljava/lang/String; 10: aload_0 11: iconst_4 12: putfield #4 // Field base_int:I 15: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 18: new #6 // class java/lang/StringBuilder 21: dup 22: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 25: ldc #8 // String Base.this 27: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: aload_0 31: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 34: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 37: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: return LineNumberTable: line 8: 0 line 5: 4 line 6: 10 line 9: 15 line 10: 40 LocalVariableTable: Start Length Slot Name Signature 0 41 0 this LBase; protected void virtual_show(); descriptor: ()V flags: ACC_PROTECTED Code: stack=2, locals=1, args_size=1 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #3 // Field str:Ljava/lang/String; 7: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 13: getstatic #13 // Field static_string:Ljava/lang/String; 16: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 19: aload_0 20: invokevirtual #14 // Method virtual_init:()V 23: invokestatic #15 // Method static_init:()V 26: invokestatic #16 // Method static_init2:()V 29: return LineNumberTable: line 13: 0 line 14: 10 line 15: 19 line 17: 23 line 18: 26 line 19: 29 LocalVariableTable: Start Length Slot Name Signature 0 30 0 this LBase; protected void virtual_init(); descriptor: ()V flags: ACC_PROTECTED Code: stack=2, locals=1, args_size=1 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #17 // String Base virtual init 5: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 22: 0 line 23: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this LBase; protected static void static_init(); descriptor: ()V flags: ACC_PROTECTED, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #18 // String Base static init 5: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 25: 0 protected static void static_init2(); descriptor: ()V flags: ACC_PROTECTED, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #19 // String Base static init2 5: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 27: 0 static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #20 // String static base string 2: putstatic #13 // Field static_string:Ljava/lang/String; 5: return LineNumberTable: line 7: 0 } SourceFile: "Base.java"
Classfile /C:/Users/chen/Desktop/Sub.class Last modified 2016-3-31; size 1602 bytes MD5 checksum 8b13720ed4fb15f410919e59f20b1978 Compiled from "Sub.java" public class Sub extends Base minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #28.#52 // Base."<init>":()V #2 = String #53 // Sub string #3 = Fieldref #19.#54 // Sub.str:Ljava/lang/String; #4 = Fieldref #55.#56 // java/lang/System.out:Ljava/io/PrintStream; #5 = Class #57 // java/lang/StringBuilder #6 = Methodref #5.#52 // java/lang/StringBuilder."<init>":()V #7 = String #58 // Sub.this #8 = Methodref #5.#59 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #9 = Methodref #5.#60 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #10 = Methodref #5.#61 // java/lang/StringBuilder.toString:()Ljava/lang/String; #11 = Methodref #62.#63 // java/io/PrintStream.println:(Ljava/lang/String;)V #12 = String #64 // Sub init #13 = String #65 // Sub static init #14 = Fieldref #28.#54 // Base.str:Ljava/lang/String; #15 = Fieldref #19.#66 // Sub.static_string:Ljava/lang/String; #16 = Fieldref #28.#66 // Base.static_string:Ljava/lang/String; #17 = Fieldref #19.#67 // Sub.base_int:I #18 = Methodref #62.#68 // java/io/PrintStream.println:(I)V #19 = Class #69 // Sub #20 = Methodref #19.#52 // Sub."<init>":()V #21 = Methodref #19.#70 // Sub.virtual_show:()V #22 = Methodref #19.#71 // Sub.show_field:()V #23 = Methodref #19.#72 // Sub.static_init:()V #24 = Methodref #19.#73 // Sub.static_init2:()V #25 = String #74 // --------------------------------------- #26 = Methodref #28.#70 // Base.virtual_show:()V #27 = String #75 // static Sub string #28 = Class #76 // Base #29 = Utf8 str #30 = Utf8 Ljava/lang/String; #31 = Utf8 static_string #32 = Utf8 <init> #33 = Utf8 ()V #34 = Utf8 Code #35 = Utf8 LineNumberTable #36 = Utf8 LocalVariableTable #37 = Utf8 this #38 = Utf8 LSub; #39 = Utf8 virtual_init #40 = Utf8 static_init #41 = Utf8 show_field #42 = Utf8 virtual_test #43 = Utf8 mytest #44 = Utf8 sub #45 = Utf8 base #46 = Utf8 LBase; #47 = Utf8 RuntimeVisibleAnnotations #48 = Utf8 Lorg/junit/Test; #49 = Utf8 <clinit> #50 = Utf8 SourceFile #51 = Utf8 Sub.java #52 = NameAndType #32:#33 // "<init>":()V #53 = Utf8 Sub string #54 = NameAndType #29:#30 // str:Ljava/lang/String; #55 = Class #77 // java/lang/System #56 = NameAndType #78:#79 // out:Ljava/io/PrintStream; #57 = Utf8 java/lang/StringBuilder #58 = Utf8 Sub.this #59 = NameAndType #80:#81 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #60 = NameAndType #80:#82 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #61 = NameAndType #83:#84 // toString:()Ljava/lang/String; #62 = Class #85 // java/io/PrintStream #63 = NameAndType #86:#87 // println:(Ljava/lang/String;)V #64 = Utf8 Sub init #65 = Utf8 Sub static init #66 = NameAndType #31:#30 // static_string:Ljava/lang/String; #67 = NameAndType #88:#89 // base_int:I #68 = NameAndType #86:#90 // println:(I)V #69 = Utf8 Sub #70 = NameAndType #91:#33 // virtual_show:()V #71 = NameAndType #41:#33 // show_field:()V #72 = NameAndType #40:#33 // static_init:()V #73 = NameAndType #92:#33 // static_init2:()V #74 = Utf8 --------------------------------------- #75 = Utf8 static Sub string #76 = Utf8 Base #77 = Utf8 java/lang/System #78 = Utf8 out #79 = Utf8 Ljava/io/PrintStream; #80 = Utf8 append #81 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #82 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder; #83 = Utf8 toString #84 = Utf8 ()Ljava/lang/String; #85 = Utf8 java/io/PrintStream #86 = Utf8 println #87 = Utf8 (Ljava/lang/String;)V #88 = Utf8 base_int #89 = Utf8 I #90 = Utf8 (I)V #91 = Utf8 virtual_show #92 = Utf8 static_init2 { java.lang.String str; descriptor: Ljava/lang/String; flags: static java.lang.String static_string; descriptor: Ljava/lang/String; flags: ACC_STATIC public Sub(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method Base."<init>":()V 4: aload_0 5: ldc #2 // String Sub string 7: putfield #3 // Field str:Ljava/lang/String; 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 13: new #5 // class java/lang/StringBuilder 16: dup 17: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 20: ldc #7 // String Sub.this 22: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_0 26: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 29: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 35: return LineNumberTable: line 11: 0 line 7: 4 line 12: 10 line 13: 35 LocalVariableTable: Start Length Slot Name Signature 0 36 0 this LSub; protected void virtual_init(); descriptor: ()V flags: ACC_PROTECTED Code: stack=2, locals=1, args_size=1 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #12 // String Sub init 5: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 19: 0 line 20: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this LSub; protected static void static_init(); descriptor: ()V flags: ACC_PROTECTED, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #13 // String Sub static init 5: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 24: 0 line 25: 8 public void show_field(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #3 // Field str:Ljava/lang/String; 7: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_0 14: getfield #14 // Field Base.str:Ljava/lang/String; 17: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 20: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 23: getstatic #15 // Field static_string:Ljava/lang/String; 26: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 29: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 32: getstatic #16 // Field Base.static_string:Ljava/lang/String; 35: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 38: return LineNumberTable: line 28: 0 line 29: 10 line 30: 20 line 31: 29 line 32: 38 LocalVariableTable: Start Length Slot Name Signature 0 39 0 this LSub; public void virtual_test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #17 // Field base_int:I 7: invokevirtual #18 // Method java/io/PrintStream.println:(I)V 10: return LineNumberTable: line 36: 0 line 37: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this LSub; public void mytest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: new #19 // class Sub 3: dup 4: invokespecial #20 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #21 // Method virtual_show:()V 12: aload_1 13: invokevirtual #22 // Method show_field:()V 16: aload_1 17: pop 18: invokestatic #23 // Method static_init:()V 21: aload_1 22: pop 23: invokestatic #24 // Method static_init2:()V 26: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 29: ldc #25 // String --------------------------------------- 31: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 34: new #19 // class Sub 37: dup 38: invokespecial #20 // Method "<init>":()V 41: astore_2 42: aload_2 43: invokevirtual #26 // Method Base.virtual_show:()V 46: return LineNumberTable: line 41: 0 line 42: 8 line 43: 12 line 44: 16 line 45: 21 line 48: 26 line 49: 34 line 50: 42 line 51: 46 LocalVariableTable: Start Length Slot Name Signature 0 47 0 this LSub; 8 39 1 sub LSub; 42 5 2 base LBase; RuntimeVisibleAnnotations: 0: #48() static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #27 // String static Sub string 2: putstatic #15 // Field static_string:Ljava/lang/String; 5: return LineNumberTable: line 8: 0 } SourceFile: "Sub.java"