為何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"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM