初始化和清理

初始化和清理

初始化和清理正是涉及安全的兩個(gè)問(wèn)題。在之前的程序中一大部分錯(cuò)誤都源自于不正確的初始化以及清理工作啦鸣。在Java中具有一系列的初始化機(jī)制保證數(shù)據(jù)對(duì)象的合理初始化,并且采用垃圾回收器機(jī)制保證對(duì)象內(nèi)存的回收問(wèn)題。

1. Java中的初始化機(jī)制

在Java的類定義中主要涉及到類屬性(即靜態(tài)域)常遂,對(duì)象屬性以及方法中的局部變量,對(duì)它們進(jìn)行初始化主要有兩種方法:

  • 在變量定義時(shí)提供初始值挽荠;(基本數(shù)據(jù)類型初始化為false或0克胳, 對(duì)象類型初始化為null)
  • 在變量定義時(shí)不提供初始值,并強(qiáng)制其在定義時(shí)就初始化(否則出現(xiàn)編譯期錯(cuò)誤)圈匆;

第一種方式的優(yōu)點(diǎn)是提供初始值使得編程更加靈活毯欣,而缺點(diǎn)是如果忘記對(duì)其正確初始化,使用變量的默認(rèn)初始值引起錯(cuò)誤而且不易察覺(jué)臭脓。第二種方式的優(yōu)點(diǎn)是強(qiáng)制對(duì)其正確初始化酗钞,缺點(diǎn)是在變量定義時(shí)就進(jìn)行初始化,缺乏靈活性来累,而且定義時(shí)就初始化砚作,后期變量在使用時(shí)再做變化,就會(huì)引起兩次初始化的開(kāi)銷嘹锁。在Java中葫录,類屬性和對(duì)象屬性采用第一種方式,尤其是對(duì)象屬性领猾,在實(shí)例化不同對(duì)象時(shí)米同,變量值需要不同值骇扇,采用第一種方式并引入構(gòu)造器的方式在構(gòu)造器中對(duì)對(duì)象屬性進(jìn)行初始化,增加了編程的靈活性(實(shí)例化不同對(duì)象面粮,其對(duì)象屬性不同少孝,代表著不同對(duì)象的不同狀態(tài)),同時(shí)避免了第二種方式中兩次初始化的開(kāi)銷(為變量提供初始值的初始化開(kāi)銷較邪静浴)稍走。而方法中的局部變量則采用第二種方式,局部變量在方法被調(diào)用時(shí)才會(huì)使用并初始化柴底,并不代表對(duì)象的狀態(tài)婿脸,因此不需要多樣性,只需要滿足方法的邏輯即可柄驻,因此采用第二種方式還可以避免不正確的初始化帶來(lái)的錯(cuò)誤狐树。

方法的重載

方法重載是指方法名稱相同而參數(shù)列表不同,他們代表著同一類的業(yè)務(wù)邏輯鸿脓,但作用對(duì)象可能不同抑钟。為什么引入方法的重載,是因?yàn)樵诔绦蛘Z(yǔ)言中答憔,名稱(變量或方法)代表一個(gè)內(nèi)存地址或者函數(shù)的入口地址味赃,是我們對(duì)內(nèi)存操作的一個(gè)代號(hào),由于內(nèi)存地址不易記憶和識(shí)別虐拓,因此使用帶有一定含義的名稱替代心俗,而在編程過(guò)程中經(jīng)常出現(xiàn)相似的一類邏輯,邏輯大致相同而作用對(duì)象不同蓉驹,需要定義不同的方法城榛,為了區(qū)分則需要較長(zhǎng)的方法名稱造成一定的編碼負(fù)擔(dān),因此引入方法重載機(jī)制态兴,使用相同方法名狠持,參數(shù)列表不同即可區(qū)分不同函數(shù),至于內(nèi)部的方法簽名則是使用的方法名稱加參數(shù)列表瞻润,而編程過(guò)程中無(wú)需考慮減少了負(fù)擔(dān)喘垂。
引入方法重載的同時(shí)也帶來(lái)一定的問(wèn)題,就是在方法調(diào)用時(shí)傳入?yún)?shù)绍撞,系統(tǒng)需要根據(jù)參數(shù)類型解析選擇正確的方法正勒,如果參數(shù)類型與方法的參數(shù)列表中的類型一一對(duì)應(yīng)時(shí)不會(huì)發(fā)生錯(cuò)誤,選擇也很容易傻铣,但是如果出現(xiàn)需要類型轉(zhuǎn)換時(shí)章贞,則會(huì)出現(xiàn)一定問(wèn)題,而系統(tǒng)選擇的原則時(shí)非洲,對(duì)方法調(diào)用的參數(shù)進(jìn)行向上轉(zhuǎn)型鸭限,選擇最近可用方法進(jìn)行調(diào)用蜕径。

最后需要注意的問(wèn)題,參數(shù)列表中類型相同败京,但順序不同也可以作為重載兜喻,但是需要盡量避免此類的行為。

構(gòu)造器

Java引入構(gòu)造器保證對(duì)對(duì)象屬性的初始化喧枷。構(gòu)造器用于定義的類進(jìn)行實(shí)例化不同的對(duì)象虹统,因此可以在對(duì)象屬性默認(rèn)初始化以后弓坞,在構(gòu)造器中傳入不同參數(shù)的方式對(duì)對(duì)象屬性進(jìn)行不同的初始化隧甚,從而實(shí)例化出不同狀態(tài)的對(duì)象。構(gòu)造器可以理解為一種靜態(tài)方法渡冻,沒(méi)有返回值戚扳,其名稱與類名相同,所以需要不同的構(gòu)造器時(shí)必須重載族吻,這也是引入重載的另一個(gè)原因(也是為什么在這里介紹方法的重載)帽借。構(gòu)造器的主要作用就是對(duì)象的初始化,主要針對(duì)對(duì)象屬性超歌。
類的定義中砍艾,如果沒(méi)有定義構(gòu)造器,則編譯器會(huì)為類構(gòu)造一個(gè)默認(rèn)構(gòu)造器(即無(wú)參構(gòu)造器巍举,其內(nèi)沒(méi)有任何初始化行為)脆荷,而類中如果定義了構(gòu)造器,則不再構(gòu)造默認(rèn)構(gòu)造器(不代表沒(méi)有無(wú)參構(gòu)造器懊悯,可以自己定義)蜓谋。在構(gòu)造器中,其邏輯為首先調(diào)用其父類構(gòu)造器(沒(méi)有指明則調(diào)用父類無(wú)參構(gòu)造器炭分,如果父類沒(méi)有無(wú)參構(gòu)造器則出現(xiàn)編譯期錯(cuò)誤桃焕,也可以指定調(diào)用父類的哪一個(gè)構(gòu)造器,使用super(args)捧毛,且該句位于構(gòu)造器的第一行观堂,否則也會(huì)出現(xiàn)編譯期錯(cuò)誤),然后編寫對(duì)象屬性進(jìn)行初始化的邏輯呀忧,通過(guò)構(gòu)造器的參數(shù)列表定義對(duì)象狀態(tài)的多樣性师痕。

這里尤其注意父類構(gòu)造器的調(diào)用問(wèn)題,如果自定義的類繼承某個(gè)類荐虐,而該類沒(méi)有無(wú)參構(gòu)造器七兜,則自定義的類必須定義構(gòu)造器,因?yàn)榫幾g期給添加的默認(rèn)構(gòu)造器為無(wú)參的且沒(méi)有任何執(zhí)行邏輯的構(gòu)造器福扬,所以其內(nèi)部必然默認(rèn)調(diào)用父類的無(wú)參構(gòu)造器腕铸,而父類不存在無(wú)參構(gòu)造器惜犀,需要明確調(diào)用,因此自定義類必須定義一個(gè)構(gòu)造器狠裹,至于參數(shù)列表是什么類型沒(méi)有限制虽界,但是構(gòu)造器內(nèi)部的第一行必須明確調(diào)用父類的某個(gè)存在的構(gòu)造器。

為了簡(jiǎn)化編程在構(gòu)造器的邏輯編寫過(guò)程中還可以調(diào)用重載的其他構(gòu)造器涛菠,使用this(args)的方式莉御,這個(gè)調(diào)用的位置沒(méi)有限定。

最后注意構(gòu)造器可以理解為類的靜態(tài)方法俗冻,沒(méi)有返回值(即void)礁叔,用于實(shí)例化對(duì)象,而實(shí)例化的對(duì)象并不是構(gòu)造器的返回值迄薄。

初始化順序

對(duì)于初始化問(wèn)題可以分為兩個(gè)過(guò)程琅关,即類的初始化和對(duì)象的初始化。類的初始化即類實(shí)例的加載讥蔽,就是在虛擬機(jī)中實(shí)例化該類的class對(duì)象涣易,這個(gè)過(guò)程主要執(zhí)行類屬性的初始化,即靜態(tài)域和靜態(tài)塊的初始化冶伞,其順序?yàn)槭紫雀割惖念惓跏蓟轮ⅲ缓蟀凑章暶黜樞驁?zhí)行靜態(tài)域或者塊的初始化,類的初始化觸發(fā)條件包括兩個(gè)响禽,一個(gè)是使用類的靜態(tài)域或靜態(tài)方法(這一種情況不會(huì)觸發(fā)對(duì)象初始化)徒爹,第二個(gè)是對(duì)象實(shí)例化(即觸發(fā)對(duì)象的初始化);對(duì)象的初始化主要執(zhí)行對(duì)象屬性的初始化金抡,即屬性中的非靜態(tài)域瀑焦,其順序?yàn)榘凑章暶魇紫葓?zhí)行父類的對(duì)象初始化,然后順序執(zhí)行對(duì)象屬性的默認(rèn)初始化或者顯示聲明的初始化(這里可以理解為在子類實(shí)例化一個(gè)對(duì)象時(shí)梗肝,需要首先實(shí)例化一個(gè)父類作為它的一個(gè)屬性包含其中)榛瓮,最后調(diào)用構(gòu)造器。

因此巫击,初始化的順序可以總結(jié)為:父類的類初始化禀晓,子類的類初始化,父類的對(duì)象初始化(先初始化對(duì)象屬性坝锰,然后調(diào)用構(gòu)造器)粹懒,子類的對(duì)象初始化(順序同樣也是先初始化屬性,在調(diào)用構(gòu)造器)

代碼樣例:

package initialization;

public class OrderTest {

    
    //static int a = Child.sField1;   //執(zhí)行語(yǔ)句1
    public static void main(String[] args) {
        System.out.println("before new child");
        //Child child = new Child();    //執(zhí)行語(yǔ)句2
        
    }

}

class Parent{
    private int parentField = getParentField();
    static int sParentField =  getSParentField();
    static{
        System.out.println("parent static block");
    }
    
    
    public Parent(){
        System.out.println("parent constructor");
    }

    private static int getSParentField() {
        System.out.println("getSParentField");
        return 0;
    }

    private int getParentField() {
        System.out.println("getParentField");
        return 0;
    }
}

class Child extends Parent{
    private int field1 = getField1();
    static int sField1 =  getSField1();
    static{
        System.out.println("static block");
    }   
    static int sField2 =  getSField2();
    
    public Child(){
        System.out.println("Child constructor");
    }
    
    private int field2 = getField2();
    private static int getSField1() {
        System.out.println("getSField1");
        return 0;
    }
    
    private static int getSField2() {
        System.out.println("getSField2");
        return 0;
    }
    
    private int getField1(){
        System.out.println("getField1");
        return 0;
    }
    private int getField2(){
        System.out.println("getField2");
        return 0;
    }
    

}

在只有執(zhí)行語(yǔ)句1時(shí)顷级,即只會(huì)觸發(fā)類的初始化凫乖,其輸出結(jié)果為:

getSParentField
parent static block
getSField1
static block
getSField2
before new child

在只有執(zhí)行語(yǔ)句2時(shí),即實(shí)例化對(duì)象時(shí),其輸出結(jié)果為:

before new child
getSParentField
parent static block
getSField1
static block
getSField2
getParentField
parent constructor
getField1
getField2
Child constructor

可以看出即使在構(gòu)造器之后聲明的屬性也會(huì)在構(gòu)造器之前初始化帽芽。

最后注意類的初始化删掀,即靜態(tài)域和靜態(tài)塊的初始化只執(zhí)行一次,后續(xù)的使用靜態(tài)域以及實(shí)例化對(duì)象都不會(huì)再次出發(fā)類的初始化导街,只執(zhí)行后續(xù)的對(duì)象初始化披泪。

2. Java中的終結(jié)處理和垃圾回收

在Java中,通過(guò)new操作生成的對(duì)象其內(nèi)存都分配在堆空間中搬瑰,Java通過(guò)垃圾回收器負(fù)責(zé)回收程序中不再使用的對(duì)象所占的內(nèi)存款票。垃圾回收器只負(fù)責(zé)回收程序中廢棄對(duì)象占用的內(nèi)存,然而程序運(yùn)行過(guò)程中還會(huì)占用一些其他資源需要釋放泽论,比如打開(kāi)的文件艾少,連接的網(wǎng)絡(luò)等。

不可使用的終結(jié)方法finalize()方法

類似于C++中的析構(gòu)函數(shù)佩厚,Java中引入了finalize()方法姆钉,其工作原理假定為:一旦垃圾回收器準(zhǔn)備好釋放對(duì)象所占用的內(nèi)存空間说订,首先調(diào)用對(duì)象的finalize()方法抄瓦,并且在下一次垃圾回收動(dòng)作發(fā)生時(shí)釋放對(duì)象占用的內(nèi)存空間。但是該方法并不一定得到調(diào)用陶冷,所以在該方法中清理除內(nèi)存以外的資源钙姊,是不合理的。

Java中垃圾回收問(wèn)題存在以下特點(diǎn):

  • 對(duì)象可能不被垃圾回收
  • 垃圾回收并不等于析構(gòu)
  • 垃圾回收只與內(nèi)存有關(guān)

Java中垃圾回收器的工作是在內(nèi)存面臨不夠用的時(shí)候才會(huì)觸發(fā)GC埂伦,此時(shí)回收不再使用對(duì)象的內(nèi)存煞额,而當(dāng)一個(gè)對(duì)象不再使用時(shí),并不能保證一定會(huì)被回收沾谜,也就不能保證finalize()函數(shù)會(huì)被調(diào)用膊毁。

public class FinalizeTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new FinalizeTest();
        //System.gc();
    }
    
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        System.out.println("finalize");
    }

}

如果不顯式調(diào)用Sysetem.gc(),此時(shí)就不會(huì)觸發(fā)GC基跑,finalize()方法也不會(huì)被調(diào)用婚温。垃圾回收只與內(nèi)存有關(guān),因此對(duì)象相關(guān)的其他資源需要我們通過(guò)合理的方式顯式清理媳否。

垃圾回收器的工作方式

Java中使用垃圾回收器回收通過(guò)new操作生成的而不再使用的對(duì)象所占用的內(nèi)存空間栅螟,從而簡(jiǎn)化編程。而垃圾回收面臨三個(gè)問(wèn)題:

  • 何時(shí)回收
  • 回收哪些對(duì)象的內(nèi)存
  • 如何回收

對(duì)于第一個(gè)問(wèn)題就是之前所說(shuō)的在Java虛擬機(jī)的內(nèi)存不夠用時(shí)就會(huì)觸發(fā)一次GC篱竭,回收不再使用對(duì)象的內(nèi)存力图。而回收的自然是不再使用對(duì)象的內(nèi)存,如何搜尋不再使用的對(duì)象掺逼,Java中沒(méi)有使用引用計(jì)數(shù)法吃媒,而是使用追溯其存活堆棧或靜態(tài)存儲(chǔ)區(qū)之中的引用,不再被引用的對(duì)象自然是待處理的“對(duì)象”赘那,而對(duì)于回收算法則較為復(fù)雜惑朦,主要包括“停止-復(fù)制”和“標(biāo)記-清掃”算法,前者可以使得回收之后的內(nèi)存相對(duì)整齊漓概,便于內(nèi)存分配漾月,但是復(fù)制過(guò)程的消耗較大,而“標(biāo)記-清掃”則相對(duì)輕量級(jí)胃珍,但是一段時(shí)間以后內(nèi)存則會(huì)變得碎片化梁肿,因此Java考慮到不同時(shí)期不同對(duì)象的內(nèi)存分配以及分布特點(diǎn),采用了自適應(yīng)的觅彰,分代的吩蔑,停止-復(fù)制,標(biāo)記-清掃式垃圾回收器填抬,具體的垃圾回收過(guò)程可以參考相關(guān)書籍烛芬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市飒责,隨后出現(xiàn)的幾起案子赘娄,更是在濱河造成了極大的恐慌,老刑警劉巖宏蛉,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遣臼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拾并,警方通過(guò)查閱死者的電腦和手機(jī)揍堰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗅义,“玉大人屏歹,你說(shuō)我怎么就攤上這事≈耄” “怎么了蝙眶?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)继控。 經(jīng)常有香客問(wèn)我械馆,道長(zhǎng),這世上最難降的妖魔是什么武通? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任霹崎,我火速辦了婚禮,結(jié)果婚禮上冶忱,老公的妹妹穿的比我還像新娘尾菇。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布派诬。 她就那樣靜靜地躺著劳淆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪默赂。 梳的紋絲不亂的頭發(fā)上沛鸵,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音缆八,去河邊找鬼曲掰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奈辰,可吹牛的內(nèi)容都是我干的栏妖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奖恰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吊趾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瑟啃,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤论泛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后翰守,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體孵奶,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年蜡峰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朗恳。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡湿颅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粥诫,到底是詐尸還是另有隱情油航,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布怀浆,位于F島的核電站谊囚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏执赡。R本人自食惡果不足惜镰踏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沙合。 院中可真熱鬧奠伪,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至滤否,卻和暖如春脸狸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藐俺。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工肥惭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人紊搪。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓蜜葱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親耀石。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牵囤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 初始化和清理正是涉及安全的兩個(gè)問(wèn)題 一揭鳞、用構(gòu)造器確保初始化構(gòu)造器:在java中提供構(gòu)造器,確保每個(gè)對(duì)象都會(huì)得到初始...
    whyshang閱讀 385評(píng)論 0 0
  • *構(gòu)造器是特殊的方法梆奈,它沒(méi)有返回值野崇。這個(gè)和返回值為空(void)明顯不同。 *區(qū)分重載的方法是必須有個(gè)獨(dú)一無(wú)二的參...
    e條蟲(chóng)閱讀 215評(píng)論 0 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法亩钟,類相關(guān)的語(yǔ)法乓梨,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法清酥,異常的語(yǔ)法扶镀,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,631評(píng)論 18 399
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司焰轻,掛了不少臭觉,但最終還是拿到小米、百度辱志、阿里蝠筑、京東、新浪揩懒、CVTE什乙、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評(píng)論 11 349
  • Sunny飛鏡閱讀 94評(píng)論 0 0