成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

繼承關系的類初始化和實例化的順序

開發 后端
一切都是java編譯器搞得鬼. JVM只是負責解析字節碼.字節碼雖然不是最原始的原子匯編碼,但字節碼已經可以完全解釋JVM的指令執行過程了。

就像之前的一個評論.我們學習的是思路. 很多人都知道繼承關系的類的初始化和實例化的順序,但如果忘記了怎么辦? 如何找到自己的答案? 又如果遇到的問題是關于泛型的擦除問題,又該如何去分析?

思路,重點是思路.泛型擦除先不談.看繼承. 首先給出一個例子,看看它的輸出是什么.

  1. public class A {  
  2.     private static String a = "NA";  
  3.     private String i="NA";  
  4.     {  
  5.         i = "A";  
  6.         System.out.println(i);  
  7.     }  
  8.       
  9.     static {  
  10.         a = "Static A";  
  11.         System.out.println(a);  
  12.     }  
  13.       
  14.     public A() {  
  15.         System.out.println("Construct A");  
  16.     }  
  1. public class B extends A {  
  2.     private static String b = "NB";  
  3.     private String j="NB";  
  4.     {  
  5.         j = "B";  
  6.         System.out.println(j);  
  7.     }  
  8.       
  9.     static {  
  10.         b = "Static B";  
  11.         System.out.println(b);  
  12.     }  
  13.       
  14.     public B() {  
  15.         System.out.println("Construct B");  
  16.     }  
  1. public class C {  
  2.     public static void main(String[] args) {  
  3.         new B();  
  4.     }  
  5.  

以上輸出是:

Static A
Static B
A
Construct A
B
Construct B

一切都是java編譯器搞得鬼. JVM只是負責解析字節碼.字節碼雖然不是最原始的原子匯編碼,但字節碼已經可以完全解釋JVM的指令執行過程了.一般來說,字節碼和java源碼相差比較大,javac會做前期優化,修改增加刪除源碼產生jvm解釋器可以理解的字節碼. java語法帶來的安全,易用,易讀等功能讓我們忽略了字節碼會和java源碼有出路.

當遇到new的時候,比如new B(),將會嘗試去初始化B類.如果B已經初始化,則開始實例化B類.如果B類沒有初始化,則初始化B類,但B類繼承A,所以在初始化B類之前需要先初始化A類.所以類的初始化過程是:A->B. 類在初始化的時候會執行static域和塊. 類的實例化在類初始化之后,實例化的時候必須先實例化父類.實例化會先執行域和塊,然后再執行構造函數.

上面的理論如果靠這種死記硬背,總會忘記.哦,還有父類的構造函數必須放在子類構造函數的***行.為什么?

遇到這種語法問題的時候,看教科書不如自己找出答案.工具就在JDK中,一個名叫javap的命令. javap會打出一個class的字節碼偽碼. 我們只需要分析B的字節碼,就可以找到答案.

  1. joeytekiMacBook-Air:bin joey$ javap -verbose B  
  2. Compiled from "B.java" 
  3. public class B extends A  
  4.   SourceFile: "B.java" 
  5.   minor version: 0 
  6.   major version: 50 
  7.   Constant pool:  
  8. const #1 = class    #2//  B  
  9. const #2 = Asciz    B;  
  10. const #3 = class    #4//  A  
  11. const #4 = Asciz    A;  
  12. const #5 = Asciz    b;  
  13. const #6 = Asciz    Ljava/lang/String;;  
  14. const #7 = Asciz    j;  
  15. const #8 = Asciz    <clinit>;  
  16. const #9 = Asciz    ()V;  
  17. const #10 = Asciz   Code;  
  18. const #11 = String  #12;    //  NB  
  19. const #12 = Asciz   NB;  
  20. const #13 = Field   #1.#14//  B.b:Ljava/lang/String;  
  21. const #14 = NameAndType #5:#6;//  b:Ljava/lang/String;  
  22. const #15 = String  #16;    //  Static B  
  23. const #16 = Asciz   Static B;  
  24. const #17 = Field   #18.#20;    //  java/lang/System.out:Ljava/io/PrintStream;  
  25. const #18 = class   #19;    //  java/lang/System  
  26. const #19 = Asciz   java/lang/System;  
  27. const #20 = NameAndType #21:#22;//  out:Ljava/io/PrintStream;  
  28. const #21 = Asciz   out;  
  29. const #22 = Asciz   Ljava/io/PrintStream;;  
  30. const #23 = Method  #24.#26;    //  java/io/PrintStream.println:(Ljava/lang/String;)V  
  31. const #24 = class   #25;    //  java/io/PrintStream  
  32. const #25 = Asciz   java/io/PrintStream;  
  33. const #26 = NameAndType #27:#28;//  println:(Ljava/lang/String;)V  
  34. const #27 = Asciz   println;  
  35. const #28 = Asciz   (Ljava/lang/String;)V;  
  36. const #29 = Asciz   LineNumberTable;  
  37. const #30 = Asciz   LocalVariableTable;  
  38. const #31 = Asciz   <init>;  
  39. const #32 = Method  #3.#33//  A."<init>":()V  
  40. const #33 = NameAndType #31:#9;//  "<init>":()V  
  41. const #34 = Field   #1.#35//  B.j:Ljava/lang/String;  
  42. const #35 = NameAndType #7:#6;//  j:Ljava/lang/String;  
  43. const #36 = String  #2//  B  
  44. const #37 = String  #38;    //  Construct B  
  45. const #38 = Asciz   Construct B;  
  46. const #39 = Asciz   this;  
  47. const #40 = Asciz   LB;;  
  48. const #41 = Asciz   SourceFile;  
  49. const #42 = Asciz   B.java;  
  50.  
  51. {  
  52. static {};  
  53.   Code:  
  54.    Stack=2, Locals=0, Args_size=0 
  55.    0:   ldc #11//String NB  
  56.    2:   putstatic   #13//Field b:Ljava/lang/String;  
  57.    5:   ldc #15//String Static B  
  58.    7:   putstatic   #13//Field b:Ljava/lang/String;  
  59.    10:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  60.    13:  getstatic   #13//Field b:Ljava/lang/String;  
  61.    16:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  62.    19:  return 
  63.   LineNumberTable:   
  64.    line 30 
  65.    line 115 
  66.    line 1210 
  67.    line 1319 
  68.  
  69.  
  70.  
  71. public B();  
  72.   Code:  
  73.    Stack=2, Locals=1, Args_size=1 
  74.    0:   aload_0  
  75.    1:   invokespecial   #32//Method A."<init>":()V  
  76.    4:   aload_0  
  77.    5:   ldc #11//String NB  
  78.    7:   putfield    #34//Field j:Ljava/lang/String;  
  79.    10:  aload_0  
  80.    11:  ldc #36//String B  
  81.    13:  putfield    #34//Field j:Ljava/lang/String;  
  82.    16:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  83.    19:  aload_0  
  84.    20:  getfield    #34//Field j:Ljava/lang/String;  
  85.    23:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  86.    26:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  87.    29:  ldc #37//String Construct B  
  88.    31:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  89.    34:  return 
  90.   LineNumberTable:   
  91.    line 150 
  92.    line 44 
  93.    line 610 
  94.    line 716 
  95.    line 1626 
  96.    line 1734 
  97.  
  98.   LocalVariableTable:   
  99.    Start  Length  Slot  Name   Signature  
  100.    0      35      0    this       LB;  

類的生命周期,將經歷類的裝載,鏈接,初始化,使用,卸載. 裝載是將字節碼讀入到內存的方法區中, 而類的初始化則會在線程棧中執行static{}塊的code. 在之前,這個塊有另一個名字<cinit>即類初始化方法.現在改名為static{}了. 類的初始化只進行一次. 但是,每當一個類在裝載和鏈接完畢以后,通過字節碼的分析,JVM解析器已經知道B是繼承A的,于是在初始化B類前,A類會先初始化.這是一個遞歸過程. 所以,B類的初始化會導致A類static{}執行,然后是B的static{}執行.讓我們看看B的static{}塊中執行了什么.

  1. static {};  
  2.   Code:  
  3.    Stack=2, Locals=0, Args_size=0 
  4. 棧深為2,本地變量0個,參數傳遞0個.  
  5.    0:   ldc #11//String NB  
  6. 將常量池中#11放到棧頂.#11="NB".  
  7.    2:   putstatic   #13//Field b:Ljava/lang/String;  
  8. 將棧頂的值 "NB" 賦予常量池中的#13,也就是 static b="NB".  
  9.    5:   ldc #15//String Static B  
  10. 將#15放入棧頂. #15="static B".  
  11.    7:   putstatic   #13//Field b:Ljava/lang/String;  
  12. 賦值static b = "static B".  
  13.    10:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  14. 將PrintStream引用壓棧.  
  15.    13:  getstatic   #13//Field b:Ljava/lang/String;  
  16. static b的值壓棧.  
  17.    16:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  18. 調用虛函數PrintStream.println("static B")  
  19.    19:  return 
  20. 退出函數,銷毀函數棧幀. 

通過注釋,我們看到類B中的static域賦值和static塊均被放到了類的初始化函數中.

當我們進行類的實例化的時候,會調用類的構造函數.我們看看類B的構造函數做了什么.

  1. public B();  
  2.   Code:  
  3.    Stack=2, Locals=1, Args_size=1 
  4. 棧深為2,本地變量1個(其實就是this),參數為1個(就是this).  
  5.    0:   aload_0  
  6. 將***個參數壓棧.也就是this壓棧.  
  7.    1:   invokespecial   #32//Method A."<init>":()V  
  8. this上調用父類的構造函數.在B的構造函數中并沒有聲明super(),但是java編譯器會自動生成此字節碼來調用父類的無參構造函數.如果在B類中聲明了super(int),編譯器會使用對應的A類構造函數來代替.JVM只是執行字節碼而已,它并不對super進行約束,約束它們的是java的編譯器.this出棧.  
  9.    4:   aload_0  
  10. this壓棧.  
  11.    5:   ldc #11//String NB  
  12. "NB"壓棧.  
  13.    7:   putfield    #34//Field j:Ljava/lang/String;  
  14. 給j賦值this.j="NB"this"NB"出棧.  
  15.    10:  aload_0  
  16. this壓棧.  
  17.    11:  ldc #36//String B  
  18. "B"壓棧  
  19.    13:  putfield    #34//Field j:Ljava/lang/String;  
  20. 給j賦值this.j="B"this"B"出棧.棧空  
  21.    16:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  22. 壓棧PrintStream  
  23.    19:  aload_0  
  24. 壓棧this 
  25.    20:  getfield    #34//Field j:Ljava/lang/String;  
  26. this出棧,調用this.j,壓棧this.j.  
  27.    23:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  28. 調用PrintStream.println(this.j).棧空.  
  29.    26:  getstatic   #17//Field java/lang/System.out:Ljava/io/PrintStream;  
  30. 壓棧PrintStream  
  31.    29:  ldc #37//String Construct B  
  32. 壓棧"Construct B" 
  33.    31:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  34. 調用PrintStream.println("Construct B")  
  35.    34:  return 

從上面的字節碼可以看出,java編譯器在編譯產生字節碼的時候,將父類的構造函數,域的初始化,代碼塊的執行和B的真正的構造函數按照順序組合在了一起,形成了新的構造函數. 一個類的編譯后的構造函數字節碼一定會遵循這樣的順序包含以下內容:

父類的構造函數->

當前類的域初始化->(按照書寫順序)

代碼塊->(按照書寫順序)

當前類的構造函數.

到這里,應該徹底明白繼承類的初始化和實例化順序了.

 

原文鏈接:http://my.oschina.net/xpbug/blog/111371

責任編輯:張偉 來源: oschina
相關推薦

2012-02-28 10:04:09

Java

2010-07-28 10:22:33

FlexApplica

2012-05-23 12:46:53

JavaJava類

2022-01-04 19:33:03

Java構造器調用

2009-07-03 16:21:33

Java的聲明和初始化Java

2011-07-22 17:46:43

java

2011-03-23 15:02:55

ListenerFilterServlet

2009-08-28 11:09:35

C#數組初始化

2020-11-02 07:02:10

加載鏈接初始化

2024-03-08 08:26:25

類的加載Class文件Java

2024-03-12 07:44:53

JVM雙親委托機制類加載器

2012-04-09 13:43:12

Java

2011-06-17 15:29:44

C#對象初始化器集合初始化器

2011-03-16 10:52:20

2012-03-13 13:38:42

Java

2009-06-10 16:17:00

Netbeans JT初始化

2021-07-07 05:00:17

初始化源碼

2023-11-12 23:08:17

C++初始化

2009-08-26 18:28:44

C#數組

2011-06-09 14:13:06

C++JAVA缺省初始化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产在线精品一区 | 嫩草伊人| 国产三级精品三级在线观看四季网 | 免费国产成人av | 久久小视频 | 日韩小视频 | av片在线观看网站 | 四季久久免费一区二区三区四区 | 毛色毛片免费看 | 成人av免费 | 成人精品国产 | 久久久久久久av | 九九热精品在线 | 久热免费在线 | 喷水毛片| 一道本视频 | 亚洲人成在线播放 | 欧州一区二区三区 | 精品国产成人 | 久久成人亚洲 | 九九热在线免费观看 | 国产精品高潮呻吟久久 | 69av片| a级片在线 | 一区二区三区亚洲视频 | 中文一区 | 精品国产一区二区三区久久 | 亚洲一区二区精品视频 | 九色av| 羞羞视频免费观 | 久久久久久亚洲国产精品 | 国产精品久久久久久妇女6080 | 欧美激情在线观看一区二区三区 | 日韩国产精品一区二区三区 | 精品国偷自产在线 | 欧美日韩在线精品 | 国产农村妇女毛片精品久久麻豆 | 性色av网站 | 日韩福利在线 | www.精品一区 | 午夜不卡福利视频 |