Java中final、finally和finalize的區(qū)別

1.final關(guān)鍵字

我們首先來說說final宇姚。它可以用于以下四個(gè)地方:

定義變量匈庭,包括靜態(tài)的和非靜態(tài)的。

定義方法的參數(shù)浑劳。

定義方法阱持。

定義類。我們依次來回顧一下每種情況下final的作用魔熏。

1.1 定義變量衷咽,包括靜態(tài)的和非靜態(tài)的。定義方法的參數(shù)

第一種情況:

如果final修飾的是一個(gè)基本類型蒜绽,就表示這個(gè)變量被賦予的值是不可變的镶骗,即它是個(gè)常量;

如果final修飾的是一個(gè)對(duì)象躲雅,就表示這個(gè)變量被賦予的引用是不可變的

這里需要提醒大家注意的是鼎姊,不可改變的只是這個(gè)變量所保存的引用,并不是這個(gè)引用所指向的對(duì)象相赁。

第二種情況:

final的含義與第一種情況相同此蜈。

實(shí)際上對(duì)于前兩種情況,有一種更貼切的表述final的含義的描述噪生,那就是裆赵,如果一個(gè)變量或方法參數(shù)被final修飾,就表示它只能被賦值一次跺嗽,但是JAVA虛擬機(jī)為變量設(shè)定的默認(rèn)值不記作一次賦值战授。

被final修飾的變量必須被初始化。初始化的方式有以下幾種:

在定義的時(shí)候初始化桨嫁。

final變量可以在初始化塊中初始化植兰,不可以在靜態(tài)初始化塊中初始化。

靜態(tài)final變量可以在靜態(tài)初始化塊中初始化璃吧,不可以在初始化塊中初始化楣导。

final變量還可以在類的構(gòu)造器中初始化,但是靜態(tài)final變量不可以畜挨。

通過下面的代碼可以驗(yàn)證以上的觀點(diǎn):

Java代碼

publicclassFinalTest{// 在定義時(shí)初始化 publicfinalintA =10;// 在初始化塊中初始化publicfinalintB; {? B =20; }// 非靜態(tài)final變量不能在靜態(tài)初始化塊中初始化 // public final int C; // static { // C = 30; // }// 靜態(tài)常量筒繁,在定義時(shí)初始化 publicstaticfinalintSTATIC_D =40;// 靜態(tài)常量,在靜態(tài)初始化塊中初始化publicstaticfinalintSTATIC_E;static{? STATIC_E =50;? }// 靜態(tài)變量不能在初始化塊中初始化 // public static final int STATIC_F; // { // STATIC_F = 60; // }publicfinalintG;// 靜態(tài)final變量不可以在構(gòu)造器中初始化 // public static final int STATIC_H; // 在構(gòu)造器中初始化 publicFinalTest(){? G =70;// 靜態(tài)final變量不可以在構(gòu)造器中初始化// STATIC_H = 80;// 給final的變量第二次賦值時(shí)巴元,編譯會(huì)報(bào)錯(cuò)// A = 99;// STATIC_D = 99;}// final變量未被初始化毡咏,編譯時(shí)就會(huì)報(bào)錯(cuò)// public final int I;// 靜態(tài)final變量未被初始化,編譯時(shí)就會(huì)報(bào)錯(cuò)// public static final int STATIC_J;}

我們運(yùn)行上面的代碼之后出了可以發(fā)現(xiàn)final變量(常量)和靜態(tài)final變量(靜態(tài)常量)被初始化時(shí)逮刨,編譯會(huì)報(bào)錯(cuò)呕缭。

用final修飾的變量(常量)比非final的變量(普通變量)擁有更高的效率,因此我們?cè)陔H編程中應(yīng)該盡可能多的用常量來代替普通變量,這也是一個(gè)很好的編程習(xí)慣恢总。

1.2 定義方法當(dāng)final用來定義一個(gè)方法時(shí)迎罗,會(huì)有什么效果呢?正如大家所知片仿,它表示這個(gè)方法不可以被子類重寫纹安,但是它這不影響它被子類繼承。我們寫段代碼來驗(yàn)證一下:

Java代碼

publicclassParentClass{? publicfinalvoidTestFinal() {System.out.println("父類--這是一個(gè)final方法");? }}publicclassSubClassextendsParentClass{/** * 子類無法重寫(override)父類的final方法滋戳,否則編譯時(shí)會(huì)報(bào)錯(cuò) */// public void www.gzlij.com TestFinal() { // System.out.println("子類--重寫final方法"); // } public static void main(String[] args) {SubClasssc =newSubClass(); sc.TestFinal();? }}

這里需要特殊說明的是钻蔑,具有private訪問權(quán)限的方法也可以增加final修飾啥刻,但是由于子無法繼承private方法奸鸯,因此也無法重寫它。編譯器在處理private方法時(shí)可帽,是按照final方來對(duì)待的娄涩,這樣可以提高該方法被調(diào)用時(shí)的效率。不過子類仍然可以定義同父類中private方法具有同樣結(jié)構(gòu)的方法映跟,但是這并不會(huì)產(chǎn)生重寫的效果蓄拣,而且它們之間也不存在必然聯(lián)系。

1.3 定義類

最后我們?cè)賮砘仡櫼幌耭inal用于類的情況努隙。這個(gè)大家應(yīng)該也很熟悉了球恤,因?yàn)槲覀冏畛S玫腟tring類就是final的。由于final類不允許被繼承荸镊,編譯器在處理時(shí)把它的所有方法都當(dāng)作final的咽斧,因此final類比普通類擁有更高的效率。而由關(guān)鍵字abstract定義的抽象類含有必須由繼承自它的子類重載實(shí)現(xiàn)的抽象方法躬存,因此無法同時(shí)用final和abstract來修飾同一個(gè)類张惹。同樣的道理,final也不能用來修飾接口岭洲。 final的類的所有方法都不能被重寫宛逗,但這并不表示final的類的屬性(變量)值也是不可改變的,要想做到final類的屬性值不可改變盾剩,必須給它增加final修飾雷激,請(qǐng)看下面的例子:

Java代碼

publicfinalclassFinalTest{inti =10;finalintj =50;publicstaticvoidmain(String[] args){FinalTest ft =newFinalTest();ft.i =99;// final類FinalTest的屬性值 i是可以改變的,因?yàn)閷傩灾礽前面沒有final修//// ft.j = 49; // 報(bào)錯(cuò)....因?yàn)?j 屬性是final 的不可以改變告私。System.out.println(ft.i);}}

運(yùn)行上面的代碼試試看侥锦,結(jié)果是99,而不是初始化時(shí)的10德挣。

2.finally語(yǔ)句

接下來我們一起回顧一下finally的用法恭垦。這個(gè)就比較簡(jiǎn)單了,它只能用在try/catch語(yǔ)句中并且附帶著一個(gè)語(yǔ)句塊,表示這段語(yǔ)句最終總是被執(zhí)行番挺。請(qǐng)看下面的代碼:

Java代碼

publicfinalclassFinallyTest{publicstaticvoidmain(String[] args){try{thrownewNullPointerException();}catch(NullPointerException e) {System.out.println("程序拋出了異常");}finally{//這里總會(huì)被執(zhí)行唠帝,不受break,return影響另如數(shù)據(jù)庫(kù)連接的close()一般寫在這里,可以降低程序的出錯(cuò)幾率System.out.println("執(zhí)行了finally語(yǔ)句塊");}}}

運(yùn)行結(jié)果說明了finally的作用: 1. 程序拋出了異常 2. 執(zhí)行了finally語(yǔ)句塊請(qǐng)大家注意玄柏,捕獲程序拋出的異常之后襟衰,既不加處理,也不繼續(xù)向上拋出異常粪摘,并不是良好的編程習(xí)慣瀑晒,它掩蓋了程序執(zhí)行中發(fā)生的錯(cuò)誤,這里只是方便演示徘意,請(qǐng)不要學(xué)習(xí)苔悦。

那么,有沒有一種情況使finally語(yǔ)句塊得不到執(zhí)行呢椎咧?大家可能想到了

return玖详、continue、break這三個(gè)可以打亂代碼順序執(zhí)行語(yǔ)句的規(guī)律勤讽。那我們就來試試看蟋座,這三個(gè)語(yǔ)句是否能影響finally語(yǔ)句塊的執(zhí)行:

Java代碼public final class FinallyTest {

// 測(cè)試return語(yǔ)句

結(jié)果顯示:編譯器在編譯return new ReturnClass();時(shí),將它分成了兩個(gè)步驟脚牍,new ReturnClass()和return向臀,前一個(gè)創(chuàng)建對(duì)象的語(yǔ)句是在finally語(yǔ)句塊之前被執(zhí)行的,而后一個(gè)return語(yǔ)句是在finally語(yǔ)句塊之后執(zhí)行的诸狭,也就是說finally語(yǔ)句塊是在程序退出方法之前被執(zhí)行的

publicReturnClasstestReturn(){try{returnnewReturnClass();}catch(Exception e) {e.printStackTrace();}finally{System.out.println("執(zhí)行了finally語(yǔ)句");}returnnull;}// 測(cè)試continue語(yǔ)句publicvoidtestContinue(){for(inti =0; i <3; i++) {try{System.out.println(i);if(i ==1) {continue;}}catch(Exception e) {e.printStackTrace();}finally{System.out.println("執(zhí)行了finally語(yǔ)句");}}}// 測(cè)試break語(yǔ)句publicvoidtestBreak(){for(inti =0; i <3; i++) {try{System.out.println(i);if(i ==1) {break;}}catch(Exception e) {e.printStackTrace();}finally{System.out.println("執(zhí)行了finally語(yǔ)句");}}}publicstaticvoidmain(String[] args){FinallyTest ft =newFinallyTest();// 測(cè)試return語(yǔ)句ft.testReturn();System.out.println();// 測(cè)試continue語(yǔ)句ft.testContinue();System.out.println();// 測(cè)試break語(yǔ)句ft.testBreak();}}classReturnClass{publicReturnClass(){? System.out.println("執(zhí)行了return語(yǔ)句");? }}

很明顯券膀,return、continue和break都沒能阻止finally語(yǔ)句塊的執(zhí)行作谚。從輸出的結(jié)果來看三娩,return語(yǔ)句似乎在 finally語(yǔ)句塊之前執(zhí)行了,事實(shí)真的如此嗎妹懒?我們來想想看雀监,return語(yǔ)句的作用是什么呢?是退出當(dāng)前的方法眨唬,并將值或?qū)ο蠓祷鼗崆啊H绻?finally語(yǔ)句塊是在return語(yǔ)句之后執(zhí)行的,那么return語(yǔ)句被執(zhí)行后就已經(jīng)退出當(dāng)前方法了匾竿,finally語(yǔ)句塊又如何能被執(zhí)行呢瓦宜?因此,正確的執(zhí)行順序應(yīng)該是這樣的:編譯器在編譯return new ReturnClass();時(shí)岭妖,將它分成了兩個(gè)步驟临庇,new ReturnClass()和return反璃,前一個(gè)創(chuàng)建對(duì)象的語(yǔ)句是在finally語(yǔ)句塊

之前被執(zhí)行的,而后一個(gè)return語(yǔ)句是在finally語(yǔ)句塊之后執(zhí)行的假夺,也就是說finally語(yǔ)句塊是在程序退出方法之前被執(zhí)行的淮蜈。同樣,finally語(yǔ)句塊是在循環(huán)被跳過(continue)和中斷(break)之前被執(zhí)行的已卷。

3.finalize方法

最后梧田,我們?cè)賮砜纯磃inalize,它是一個(gè)方法侧蘸,屬于java.lang.Object類裁眯,它的定義如下:Java代碼protected void finalize() throws Throwable { }眾所周知,finalize()方法是GC(garbage collector)運(yùn)行機(jī)制的一部分在此我們只說說finalize()方法的作用是什么呢讳癌?finalize()方法是在GC清理它所從屬的對(duì)象時(shí)被調(diào)用的穿稳,如果執(zhí)行它的過程中拋出了無法捕獲的異常(uncaught exception),GC將終止對(duì)改對(duì)象的清理析桥,并且該異常會(huì)被忽略司草;直到下一次GC開始清理這個(gè)對(duì)象時(shí)艰垂,它的finalize()會(huì)被再次調(diào)用泡仗。請(qǐng)看下面的示例:

Java代碼

publicfinalclassFinallyTest{// 重寫finalize()方法protectedvoidfinalize()throwsThrowable{System.out.println("執(zhí)行了finalize()方法");}publicstaticvoidmain(String[] args){FinallyTest ft =newFinallyTest();ft =null;System.gc();}}

運(yùn)行結(jié)果如下:? 執(zhí)行了finalize()方法

程序調(diào)用了java.lang.System類的gc()方法,引起GC的執(zhí)行猜憎,GC在清理ft對(duì)象時(shí)調(diào)用了它的finalize()方法娩怎,因此才有了上面的輸出結(jié)果。調(diào)用System.gc()等同于調(diào)用下面這行代碼:Java代碼Runtime.getRuntime().gc();調(diào)用它們的作用只是建議垃圾收集器(GC)啟動(dòng)胰柑,清理無用的對(duì)象釋放內(nèi)存空間截亦,但是G的啟動(dòng)并不是一定的,這由JAVA虛擬機(jī)來決定柬讨。直到 JAVA虛擬機(jī)停止運(yùn)行崩瓤,有些對(duì)象的finalize()可能都沒有被運(yùn)行過,那么怎樣保證所有對(duì)象的這個(gè)方法在JAVA虛擬機(jī)停止運(yùn)行之前一定被調(diào)用呢踩官?答案是我們可以調(diào)用System類的另一個(gè)方法:

Java代碼public static void runFinalizersOnExit(boolean value) {//other code} 給這個(gè)方法傳入true就可以保證對(duì)象的finalize()方法在JAVA虛擬機(jī)停止運(yùn)行前一定被運(yùn)行了却桶,不過遺憾的是這個(gè)方法是不安全的,它會(huì)導(dǎo)致有用的對(duì)象finalize()被誤調(diào)用蔗牡,因此已不被贊成使用了颖系。 由于finalize()屬于Object類,因此所有類都有這個(gè)方法辩越,Object的任意子類都可以重寫(override)該方法嘁扼,在其中釋放系統(tǒng)資源或者做其它的清理工作,如關(guān)閉輸入輸出流黔攒。

歡迎工作一到五年的Java工程師朋友們加入Java技術(shù)交流:585550789

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末趁啸,一起剝皮案震驚了整個(gè)濱河市强缘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌不傅,老刑警劉巖欺旧,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蛤签,居然都是意外死亡辞友,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門震肮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來称龙,“玉大人,你說我怎么就攤上這事戳晌■曜穑” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沦偎,是天一觀的道長(zhǎng)疫向。 經(jīng)常有香客問我,道長(zhǎng)豪嚎,這世上最難降的妖魔是什么搔驼? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮侈询,結(jié)果婚禮上舌涨,老公的妹妹穿的比我還像新娘。我一直安慰自己扔字,他們只是感情好囊嘉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著革为,像睡著了一般扭粱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上震檩,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天琢蛤,我揣著相機(jī)與錄音,去河邊找鬼恳蹲。 笑死虐块,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘉蕾。 我是一名探鬼主播贺奠,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼错忱!你這毒婦竟也來了儡率?” 一聲冷哼從身側(cè)響起挂据,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎儿普,沒想到半個(gè)月后崎逃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眉孩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年个绍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浪汪。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巴柿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出死遭,到底是詐尸還是另有隱情广恢,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布呀潭,位于F島的核電站钉迷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钠署。R本人自食惡果不足惜糠聪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踏幻。 院中可真熱鬧枷颊,春花似錦戳杀、人聲如沸该面。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隔缀。三九已至,卻和暖如春傍菇,著一層夾襖步出監(jiān)牢的瞬間猾瘸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工丢习, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牵触,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓咐低,卻偏偏與公主長(zhǎng)得像揽思,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子见擦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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