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

1.final關(guān)鍵字

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

  1. 定義變量,包括靜態(tài)的和非靜態(tài)的。
  2. 定義方法的參數(shù)。
  3. 定義方法。
  4. 定義類。我們依次來回顧一下每種情況下final的作用。

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

第一種情況:
如果final修飾的是一個(gè)基本類型,就表示這個(gè)變量被賦予的值是不可變的,即它是個(gè)常量;
如果final修飾的是一個(gè)對象,就表示這個(gè)變量被賦予的引用是不可變的
這里需要提醒大家注意的是,不可改變的只是這個(gè)變量所保存的引用,并不是這個(gè)引用所指向的對象。
第二種情況:
final的含義與第一種情況相同。
實(shí)際上對于前兩種情況,有一種更貼切的表述final的含義的描述,那就是,如果一個(gè)變量或方法參數(shù)被final修飾,就表示它只能被賦值一次,但是JAVA虛擬機(jī)為變量設(shè)定的默認(rèn)值不記作一次賦值。
被final修飾的變量必須被初始化。初始化的方式有以下幾種:

  1. 在定義的時(shí)候初始化。
  2. final變量可以在初始化塊中初始化,不可以在靜態(tài)初始化塊中初始化。
  3. 靜態(tài)final變量可以在靜態(tài)初始化塊中初始化,不可以在初始化塊中初始化。
  4. final變量還可以在類的構(gòu)造器中初始化,但是靜態(tài)final變量不可以。
    通過下面的代碼可以驗(yàn)證以上的觀點(diǎn):
    Java代碼
public class FinalTest {     
// 在定義時(shí)初始化     
public final int A = 10;
// 在初始化塊中初始化
public final int B;     
{          
      B = 20;     
}
// 非靜態(tài)final變量不能在靜態(tài)初始化塊中初始化     
// public final int C;     
// static {          
// C = 30;     
// }
// 靜態(tài)常量,在定義時(shí)初始化     
public static final int STATIC_D = 40;
// 靜態(tài)常量,在靜態(tài)初始化塊中初始化
public static final int STATIC_E;     
static {       
   STATIC_E = 50;   
  }
// 靜態(tài)變量不能在初始化塊中初始化    
 // public static final int STATIC_F;    
 // {          
// STATIC_F = 60;    
 // }
public final int G;
// 靜態(tài)final變量不可以在構(gòu)造器中初始化     
// public static final int STATIC_H;     
// 在構(gòu)造器中初始化          
public FinalTest() 
{             
  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的變量(普通變量)擁有更高的效率,因此我們在際編程中應(yīng)該盡可能多的用常量來代替普通變量,這也是一個(gè)很好的編程習(xí)慣。

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

Java代碼

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

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

1.3 定義類

最后我們再來回顧一下final用于類的情況。這個(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修飾,請看下面的例子:
Java代碼

public final class FinalTest {
int i = 10;
final int j = 50;
public static void main(String[] args) {
FinalTest ft = new FinalTest();
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語句

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

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

運(yùn)行結(jié)果說明了finally的作用: 1. 程序拋出了異常 2. 執(zhí)行了finally語句塊請大家注意,捕獲程序拋出的異常之后,既不加處理,也不繼續(xù)向上拋出異常,并不是良好的編程習(xí)慣,它掩蓋了程序執(zhí)行中發(fā)生的錯(cuò)誤,這里只是方便演示,請不要學(xué)習(xí)。
那么,有沒有一種情況使finally語句塊得不到執(zhí)行呢?大家可能想到了
return、continue、break這三個(gè)可以打亂代碼順序執(zhí)行語句的規(guī)律。那我們就來試試看,這三個(gè)語句是否能影響finally語句塊的執(zhí)行:
Java代碼public final class FinallyTest {
// 測試return語句
結(jié)果顯示:編譯器在編譯return new ReturnClass();時(shí),將它分成了兩個(gè)步驟,new ReturnClass()和return,前一個(gè)創(chuàng)建對象的語句是在finally語句塊之前被執(zhí)行的,而后一個(gè)return語句是在finally語句塊之后執(zhí)行的,也就是說finally語句塊是在程序退出方法之前被執(zhí)行的

public ReturnClass testReturn() {
try {
return new ReturnClass();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("執(zhí)行了finally語句");
}
return null;
}
// 測試continue語句
public void testContinue() {
for (int i = 0; i < 3; i++) {
try {
System.out.println(i);
if (i == 1) {
continue;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("執(zhí)行了finally語句");
}
}
}
// 測試break語句
public void testBreak() {
for (int i = 0; i < 3; i++) {
try {
System.out.println(i);
if (i == 1) {
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("執(zhí)行了finally語句");
}
}
}
public static void main(String[] args) {
FinallyTest ft = new FinallyTest();
// 測試return語句
ft.testReturn();
System.out.println();
// 測試continue語句
ft.testContinue();
System.out.println();
// 測試break語句
ft.testBreak();
}
}
class ReturnClass {   
  public ReturnClass() {     
     System.out.println("執(zhí)行了return語句");  
   }
}

很明顯,return、continue和break都沒能阻止finally語句塊的執(zhí)行。從輸出的結(jié)果來看,return語句似乎在 finally語句塊之前執(zhí)行了,事實(shí)真的如此嗎?我們來想想看,return語句的作用是什么呢?是退出當(dāng)前的方法,并將值或?qū)ο蠓祷?。如?finally語句塊是在return語句之后執(zhí)行的,那么return語句被執(zhí)行后就已經(jīng)退出當(dāng)前方法了,finally語句塊又如何能被執(zhí)行呢?因此,正確的執(zhí)行順序應(yīng)該是這樣的:編譯器在編譯return new ReturnClass();時(shí),將它分成了兩個(gè)步驟,new ReturnClass()和return,前一個(gè)創(chuàng)建對象的語句是在finally語句塊
之前被執(zhí)行的,而后一個(gè)return語句是在finally語句塊之后執(zhí)行的,也就是說finally語句塊是在程序退出方法之前被執(zhí)行的。同樣,finally語句塊是在循環(huán)被跳過(continue)和中斷(break)之前被執(zhí)行的。

3.finalize方法

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

public final class FinallyTest {
// 重寫finalize()方法
protected void finalize() throws Throwable {
System.out.println("執(zhí)行了finalize()方法");
}
public static void main(String[] args) {
FinallyTest ft = new FinallyTest();
ft = null;
System.gc();
}
}

運(yùn)行結(jié)果如下:? 執(zhí)行了finalize()方法
程序調(diào)用了java.lang.System類的gc()方法,引起GC的執(zhí)行,GC在清理ft對象時(shí)調(diào)用了它的finalize()方法,因此才有了上面的輸出結(jié)果。調(diào)用System.gc()等同于調(diào)用下面這行代碼:Java代碼Runtime.getRuntime().gc();調(diào)用它們的作用只是建議垃圾收集器(GC)啟動(dòng),清理無用的對象釋放內(nèi)存空間,但是G的啟動(dòng)并不是一定的,這由JAVA虛擬機(jī)來決定。直到 JAVA虛擬機(jī)停止運(yùn)行,有些對象的finalize()可能都沒有被運(yùn)行過,那么怎樣保證所有對象的這個(gè)方法在JAVA虛擬機(jī)停止運(yùn)行之前一定被調(diào)用呢?答案是我們可以調(diào)用System類的另一個(gè)方法:
Java代碼public static void runFinalizersOnExit(boolean value) {//other code} 給這個(gè)方法傳入true就可以保證對象的finalize()方法在JAVA虛擬機(jī)停止運(yùn)行前一定被運(yùn)行了,不過遺憾的是這個(gè)方法是不安全的,它會(huì)導(dǎo)致有用的對象finalize()被誤調(diào)用,因此已不被贊成使用了。 由于finalize()屬于Object類,因此所有類都有這個(gè)方法,Object的任意子類都可以重寫(override)該方法,在其中釋放系統(tǒng)資源或者做其它的清理工作,如關(guān)閉輸入輸出流。
參考
Java中final、finally、finalize的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • 1、.java源文件: 一個(gè)以”.java“為后綴的源文件:只能有一個(gè)與文件名相同的類,可以包含其他類。 2、類方...
    Hughman閱讀 1,738評論 1 9
  • (一)Java部分 1、列舉出JAVA中6個(gè)比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,241評論 0 62
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等,對于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,644評論 0 3
  • 她問:“我想得到男友全部的愛和關(guān)注,他也很在乎我,但我就是感覺不到那種很投入的感覺。是我太多心了,還是他不夠愛我?...
    一點(diǎn)樂生活閱讀 533評論 0 1

友情鏈接更多精彩內(nèi)容