为何JAVA虚函数(虚方法)会造成父类可以"访问"子类的假象?

 

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

 

posted @ 2016-03-21 10:54  陈洋Cy  阅读(7303)  评论(0编辑  收藏  举报