類(lèi)和對(duì)象的初始化

          類(lèi)的生命周期:分為裝載,鏈接,初始化
          如圖:


          1)裝載:查找并裝載類(lèi)型的二進(jìn)制數(shù)據(jù)
          2)連接:執(zhí)行驗(yàn)證,準(zhǔn)備,和解析(可選)
                   a) 驗(yàn)證:確保導(dǎo)入類(lèi)型正確
                   b) 準(zhǔn)備:為類(lèi)變量分配內(nèi)存,并將其初始化為默認(rèn)值
                   c) 解析:把類(lèi)型中的符號(hào)引用轉(zhuǎn)換成直接引用
          3)初始化:把類(lèi)變量初始化為默認(rèn)初值


                隨著Java虛擬機(jī)裝載了一個(gè)類(lèi),并執(zhí)行了一些它選擇進(jìn)行的驗(yàn)證之后,類(lèi)就可以進(jìn)入準(zhǔn)備階
          段了。在準(zhǔn)備階段,Java虛擬機(jī)為類(lèi)變量分配內(nèi)存,設(shè)置默認(rèn)初始值:但在到達(dá)初始化階段之前,
          類(lèi)變量都沒(méi)有被初始化為真正的初始值。(在準(zhǔn)備階段是不會(huì)執(zhí)行Java代碼的。)在準(zhǔn)備階段,虛
          擬機(jī)把給類(lèi)變量新分配的內(nèi)存根據(jù)類(lèi)型設(shè)置為默認(rèn)值。

           為了準(zhǔn)備讓一個(gè)類(lèi)或者接口被"首次主動(dòng)"使用,最后一個(gè)步驟就是初始化,也就是為類(lèi)變量      
          賦予正確的初始值。這里的”正確”初始值指的是程序員希望這個(gè)類(lèi)變量所具備的起始值。正
          確的初始值是和在準(zhǔn)備階段賦予的默認(rèn)初始值對(duì)比而言的。前面說(shuō)過(guò),根據(jù)類(lèi)型的不同,類(lèi)變
          量已經(jīng)被賦予了默認(rèn)初始值。而正確的初始值是根據(jù)程序員制定的主觀計(jì)劃面生成的。


          在Java代碼中,一個(gè)正確的初始值是通過(guò)類(lèi)變量初始化語(yǔ)句或者靜態(tài)初始化語(yǔ)句給出的。
           1)一個(gè)類(lèi)變量初始化語(yǔ)句是變量聲明后面的等號(hào)和表達(dá)式:
           2)靜態(tài)初始化語(yǔ)句是一個(gè)以static開(kāi)頭的程序塊
           example : 
              public class Example1 {
               
               // 類(lèi)變量初始化語(yǔ)句
               static int value = (int) (Math.random()*6.0);
               
               // 靜態(tài)初始化語(yǔ)句
               static{
                System.out.println("this is example");
               }
              }
          所有的類(lèi)變量初始化語(yǔ)句和類(lèi)型的靜態(tài)初始化器都被Java編譯器收集在—起,放到——個(gè)特殊
          的方法中。對(duì)于類(lèi)來(lái)說(shuō),這個(gè)方法被稱(chēng)作類(lèi)初始化方法;對(duì)于接口來(lái)說(shuō),它被稱(chēng)為接口初始化
          方法。在類(lèi)和接口的Javaclass文件中,這個(gè)方法被稱(chēng)為”<clinit>”。通常的Java程序方法是無(wú)法
          調(diào)用這個(gè)<clinit>方法的。這種方法只能被Java虛擬機(jī)調(diào)用

          clinit>()方法
              前面說(shuō)過(guò),Java編譯器把類(lèi)變量初始化語(yǔ)句和靜態(tài)初始化浯句的代碼都放到class文件的
          <clinit>()方法中,順序就按照它們?cè)陬?lèi)或者接門(mén)聲明中出現(xiàn)的順序。
           example:
            public class Example1 {
              static int width;
              static int height = (int) (Math.random()*6.0);

              static{
               width = (int) (Math.random()*3.0);
              }
           }
          java 編譯器生成下面<clinit>方法:
          0 invokestatic java.lang.Math.random
          3 ldc2_w 6.0 (double)
          6 dmul
          7 d2i
          8 putstatic Example1.height
          11 invokestatic java.lang.Math.random
          14 ldc2_w 3.0 (double) 17 dmul
          18 d2i
          19 putstatic Example1.width
          22 return

          clinit 方法首先執(zhí)行唯一的類(lèi)變量初始化語(yǔ)句初始化heght,然后在靜態(tài)初始化語(yǔ)句中
          初始化width(雖然它聲明在height之前,但那僅僅是聲明了類(lèi)變量而不是類(lèi)變量初始化語(yǔ)句).

           

          除接口以外,初始化一個(gè)類(lèi)之前必須保證其直接超類(lèi)已被初始化,并且該初始化過(guò)程是由 Jvm 保證線(xiàn)程安全的。
          另外,并非所有的類(lèi)都會(huì)擁有一個(gè) <clinit>() 方法。
          1)如果類(lèi)沒(méi)有聲明任何類(lèi)變量,也沒(méi)有靜態(tài)初始化語(yǔ)句,那么它不會(huì)有<clinit>()方法。
          2)如果聲明了類(lèi)變量但是沒(méi)有使用類(lèi)變量初始化語(yǔ)句或者靜態(tài)初始化語(yǔ)句初始它們,那么類(lèi)不會(huì)有<clinit>()方法。 
             example:
                public class example{
                 static int val;
                }
              
          3)如果類(lèi)僅包含靜態(tài) final 變量的類(lèi)變量初始化語(yǔ)句,并且類(lèi)變量初始化語(yǔ)句是編譯時(shí)常量表達(dá)式,類(lèi)不會(huì)有<clinit>()方法。
              example:
              public class Example {
               static final String str ="abc";
               static final int value = 100;
              }
          這種情況java編譯器把 str 和 value 被看做是常量,jvm會(huì)直接使用該類(lèi)的常量池或者在字節(jié)碼中直接存放常量值。該類(lèi)不會(huì)被加載。
           
          如果接口不包含在編譯時(shí)解析成常量的字段初始化語(yǔ)句,接口中就包含一個(gè)<clinit>()方法。
          example:
           interface Example{
            int i =5;
            int hoursOfSleep = (int) (Math.random()*3.0);
            
           }
          字段hoursOfSleep會(huì)被放在<clinit>()方法中(比較詭異???它被看作類(lèi)變量了),而字段i被看作是編譯時(shí)常量特殊處理(JAVA語(yǔ)法規(guī)定,接口中的變量默認(rèn)自動(dòng)隱含是public static final)。
           java 編譯器生成下面<clinit>方法:
          0 invokestatic java.lang.Math.random
          3 ldc2_w 3.0 (double)
          6 dmul
          7 d2i
          8 putstatic Example.hoursOfSleep
          11 return

          主動(dòng)使用和被動(dòng)使用
              在前面講過(guò),Java虛擬機(jī)在首次主動(dòng)使用類(lèi)型時(shí)初始化它們。只有6種活動(dòng)被認(rèn)為是主動(dòng)使
          用:
           1)創(chuàng)建類(lèi)的新實(shí)例,
           2)調(diào)用類(lèi)中聲明的靜態(tài)方法,
           3)操作類(lèi)或者接口中聲明的非常量靜態(tài)字段,
           4)調(diào)用JavaAPI中特定的反射方法
           5)初始化一個(gè)類(lèi)的子類(lèi);
           6)以及指定一個(gè)類(lèi)作為Java虛擬機(jī)啟動(dòng)時(shí)的初始化類(lèi)。
           
             使用一個(gè)非常量的靜態(tài)字段只有當(dāng)類(lèi)或者接口的確聲明了這個(gè)字段時(shí)才是主動(dòng)使用、比如,
          類(lèi)中聲明的字段可能會(huì)被子類(lèi)引用;接口中聲明的字段可能會(huì)被子接口或者實(shí)現(xiàn)了這個(gè)接口的
          類(lèi)引用。對(duì)于子類(lèi)、子接口和實(shí)現(xiàn)接口的類(lèi)來(lái)說(shuō).這就是被動(dòng)使用(使用它們并不會(huì)觸發(fā)
          它們的初始化)。下面的例子說(shuō)明了這個(gè)原理:

          class NewParement{
           static int hoursOfSleep = (int) (Math.random()*3.0);
           
           static{
            System.out.println("new parement is initialized.");
           }
          }

          class NewbornBaby extends NewParement{
           static int hoursOfCry = (int) (Math.random()*2.0);
           
           static{
            System.out.println("new bornBaby is initialized.");
           }
          }


          public class Example1 {
           
           public static void main(String[] args){
            int hours = NewbornBaby.hoursOfSleep;
            System.out.println(hours);
           }
           static{
            System.out.println("example1 is initialized.");
           }
           
          }
          運(yùn)行結(jié)果:
          example1 is initialized.
          new parement is initialized.
          0
          NewbornBaby 沒(méi)有被初始化,也沒(méi)有被加載。


          對(duì)象的生命周期

                  當(dāng)java虛擬機(jī)創(chuàng)建一個(gè)新的類(lèi)實(shí)例時(shí)不管明確的還是隱含的,首先要在堆中為保存對(duì)象的實(shí)例變量分配內(nèi)存,包含所有在對(duì)象類(lèi)中和它超類(lèi)中
          聲明的變量(包括隱藏的實(shí)例變量)都要分配內(nèi)存。其次賦默認(rèn)初值,最后賦予正確的初始值。

          java編譯器為每個(gè)類(lèi)都至少生成一個(gè)實(shí)例初始化方法 "<init>()"與構(gòu)造方法相對(duì)應(yīng)。

          如果構(gòu)造方法調(diào)用同一個(gè)類(lèi)中的另一個(gè)構(gòu)造方法(構(gòu)造方法重載),它對(duì)應(yīng)的init<>():
          1)一個(gè)同類(lèi)init<>()調(diào)用。
          2)對(duì)應(yīng)構(gòu)造方法體代碼的調(diào)用。
          如果構(gòu)造方法不是通過(guò)this()調(diào)用開(kāi)始,且對(duì)象不是Object 它對(duì)應(yīng)的init<>():
          1)一個(gè)超類(lèi)init<>()調(diào)用。
          2)任意實(shí)例變量初始化代碼調(diào)用。
          3)對(duì)應(yīng)構(gòu)造方法體代碼的調(diào)用。
          如果上述對(duì)象是Object,則去掉第一條。如果構(gòu)造方法明確使用super()首先調(diào)用對(duì)應(yīng)超類(lèi)init<>()其余不變。
          下面的例子詳細(xì)說(shuō)明了實(shí)例變量初始化(摘自Java Language Specification)
          class Point{
           int x,y;
           Point(){x=1;y=1;}
          }
          class ColoredPoint extends Point{
           int color = OxFF00FF;
          }
          class Test{
           public static void main(String[] args){
            ColoredPoint cp = new ColoredPoint();
            System.out.println(cp.color);
           }
          }
          首先,為新的ColoredPoint實(shí)例分配內(nèi)存空間,以存儲(chǔ)實(shí)例變量x,y和color;然后將這些變量初始化成默認(rèn)值
          在這個(gè)例子中都是0。
          接下來(lái)調(diào)用無(wú)參數(shù)的ColoredPoint(),由于ColorPoint沒(méi)有聲明構(gòu)造方法,java編譯器會(huì)自動(dòng)提供如下的構(gòu)造方
          法:ColoredPoint(){super();}。
          該構(gòu)造方法然后調(diào)用無(wú)參數(shù)的Point(),而Point()沒(méi)有顯示的超類(lèi),編譯器會(huì)提供一個(gè)對(duì)其無(wú)參數(shù)的構(gòu)造方法的
          隱式調(diào)用:Point(){super();x=1;y=1}。
          因此將會(huì)調(diào)用到Object();Object類(lèi)沒(méi)有超類(lèi),至此遞歸調(diào)用會(huì)終止。接下來(lái)會(huì)調(diào)用Object任何實(shí)例初始化語(yǔ)句
          及任何實(shí)例變量初始化語(yǔ)句。
          接著執(zhí)行Object()由于Object類(lèi)中未聲明這樣的構(gòu)造方法。因此編譯器會(huì)提供默認(rèn)的構(gòu)造方法object(){}。
          但是執(zhí)行該構(gòu)造方法不會(huì)產(chǎn)生任何影響,然后返回。
          接下來(lái)執(zhí)行Point類(lèi)實(shí)例變量初始化語(yǔ)句。當(dāng)這個(gè)過(guò)程發(fā)生時(shí),x,y的聲明沒(méi)有提供任何初始化表達(dá)式,因此這個(gè)
          步驟未采取任何動(dòng)作(x,y 仍為0);
          接下來(lái)執(zhí)行Point構(gòu)造方法體,將x,y賦值為1。
          接下來(lái)會(huì)執(zhí)行類(lèi)ColoredPoint的實(shí)例變量初始化語(yǔ)句。把color賦值0xFF00FF,最后執(zhí)行ColoredPoint構(gòu)造方法體
          余下的部分(super()調(diào)用之后的部分),碰巧沒(méi)有任何語(yǔ)句,因此不需要進(jìn)一步的動(dòng)作,初始化完成。

          與C++不同的是,在創(chuàng)建新的類(lèi)實(shí)例期間,java編程語(yǔ)言不會(huì)為方法分派來(lái)指定變更的規(guī)則。如果調(diào)用的方法在被
          初始化對(duì)象的子類(lèi)中重寫(xiě),那么就是用重寫(xiě)的方法。甚至新對(duì)象被完全初始化前也是如此。編譯和運(yùn)行下面的例子
          class Super{
           Super(){printThree();}
           void printThree{System.out.println("Three");}
          }
          class Test extends Super{
           int three = (int)Math.PI; // That is 3
           public static void main(String args[]){
            Test t = new Test();
            t.printThree();
           }
           void printThree(){System.out.println(three);}
          }
          輸出:
          0
          3
          這表明Super類(lèi)中的printThree()沒(méi)有被執(zhí)行。而是調(diào)用的Test中的printThree()。

           

           
           

          posted on 2010-07-14 16:18 AK47 閱讀(895) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): java相關(guān)

          <2010年7月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆分類(lèi)

          隨筆檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 临西县| 灵山县| 漯河市| 柳河县| 长治市| 靖江市| 亚东县| 乌兰浩特市| 天镇县| 玉山县| 施秉县| 呼伦贝尔市| 黔西县| 贵定县| 武清区| 永修县| 枝江市| 土默特左旗| 林州市| 景德镇市| 易门县| 无锡市| 高平市| 淮滨县| 饶河县| 海伦市| 柯坪县| 剑川县| 商丘市| 凯里市| 乌兰察布市| 贵阳市| 龙门县| 阿克| 孟州市| 中江县| 应用必备| 新郑市| 宁远县| 扶风县| 琼结县|