首先,来看一个简单的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"