自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

繼承關(guān)系的類初始化和實(shí)例化的順序

開發(fā) 后端
一切都是java編譯器搞得鬼. JVM只是負(fù)責(zé)解析字節(jié)碼.字節(jié)碼雖然不是最原始的原子匯編碼,但字節(jié)碼已經(jīng)可以完全解釋JVM的指令執(zhí)行過程了。

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

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

  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只是負(fù)責(zé)解析字節(jié)碼.字節(jié)碼雖然不是最原始的原子匯編碼,但字節(jié)碼已經(jīng)可以完全解釋JVM的指令執(zhí)行過程了.一般來說,字節(jié)碼和java源碼相差比較大,javac會(huì)做前期優(yōu)化,修改增加刪除源碼產(chǎn)生jvm解釋器可以理解的字節(jié)碼. java語法帶來的安全,易用,易讀等功能讓我們忽略了字節(jié)碼會(huì)和java源碼有出路.

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

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

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

  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;  

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

  1. static {};  
  2.   Code:  
  3.    Stack=2, Locals=0, Args_size=0 
  4. 棧深為2,本地變量0個(gè),參數(shù)傳遞0個(gè).  
  5.    0:   ldc #11//String NB  
  6. 將常量池中#11放到棧頂.#11="NB".  
  7.    2:   putstatic   #13//Field b:Ljava/lang/String;  
  8. 將棧頂?shù)闹?nbsp;"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. 調(diào)用虛函數(shù)PrintStream.println("static B")  
  19.    19:  return 
  20. 退出函數(shù),銷毀函數(shù)棧幀. 

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

當(dāng)我們進(jìn)行類的實(shí)例化的時(shí)候,會(huì)調(diào)用類的構(gòu)造函數(shù).我們看看類B的構(gòu)造函數(shù)做了什么.

  1. public B();  
  2.   Code:  
  3.    Stack=2, Locals=1, Args_size=1 
  4. 棧深為2,本地變量1個(gè)(其實(shí)就是this),參數(shù)為1個(gè)(就是this).  
  5.    0:   aload_0  
  6. 將***個(gè)參數(shù)壓棧.也就是this壓棧.  
  7.    1:   invokespecial   #32//Method A."<init>":()V  
  8. this上調(diào)用父類的構(gòu)造函數(shù).在B的構(gòu)造函數(shù)中并沒有聲明super(),但是java編譯器會(huì)自動(dòng)生成此字節(jié)碼來調(diào)用父類的無參構(gòu)造函數(shù).如果在B類中聲明了super(int),編譯器會(huì)使用對(duì)應(yīng)的A類構(gòu)造函數(shù)來代替.JVM只是執(zhí)行字節(jié)碼而已,它并不對(duì)super進(jìn)行約束,約束它們的是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出棧,調(diào)用this.j,壓棧this.j.  
  27.    23:  invokevirtual   #23//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  28. 調(diào)用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. 調(diào)用PrintStream.println("Construct B")  
  35.    34:  return 

從上面的字節(jié)碼可以看出,java編譯器在編譯產(chǎn)生字節(jié)碼的時(shí)候,將父類的構(gòu)造函數(shù),域的初始化,代碼塊的執(zhí)行和B的真正的構(gòu)造函數(shù)按照順序組合在了一起,形成了新的構(gòu)造函數(shù). 一個(gè)類的編譯后的構(gòu)造函數(shù)字節(jié)碼一定會(huì)遵循這樣的順序包含以下內(nèi)容:

父類的構(gòu)造函數(shù)->

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

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

當(dāng)前類的構(gòu)造函數(shù).

到這里,應(yīng)該徹底明白繼承類的初始化和實(shí)例化順序了.

 

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

責(zé)任編輯:張偉 來源: oschina
相關(guān)推薦

2012-02-28 10:04:09

Java

2010-07-28 10:22:33

FlexApplica

2012-05-23 12:46:53

JavaJava類

2009-07-03 16:21:33

Java的聲明和初始化Java

2022-01-04 19:33:03

Java構(gòu)造器調(diào)用

2011-07-22 17:46:43

java

2011-03-23 15:02:55

ListenerFilterServlet

2009-08-28 11:09:35

C#數(shù)組初始化

2024-03-08 08:26:25

類的加載Class文件Java

2020-11-02 07:02:10

加載鏈接初始化

2024-03-12 07:44:53

JVM雙親委托機(jī)制類加載器

2012-04-09 13:43:12

Java

2011-06-17 15:29:44

C#對(duì)象初始化器集合初始化器

2023-11-12 23:08:17

C++初始化

2011-03-16 10:52:20

2009-06-10 16:17:00

Netbeans JT初始化

2012-03-13 13:38:42

Java

2021-07-07 05:00:17

初始化源碼

2009-08-26 18:28:44

C#數(shù)組

2011-06-09 14:13:06

C++JAVA缺省初始化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)