Java中final、finally、finalize的區別與用法
1.簡單區別:
final用於聲明屬性,方法和類,分別表示屬性不可交變,方法不可覆蓋,類不可繼承。
finally是異常處理語句結構的一部分,表示老是執行。
finalize是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,供垃圾收集時的其餘資源回收,例如關閉文件等。
2.中等區別:
雖然這個單詞在Java中都存在,可是並沒太多關聯:
final:java中的關鍵字,修飾符。
A).若是一個類被聲明爲final,就意味着它不能再派生出新的子類,不能做爲父類被繼承。所以,一個類不能同時被聲明爲abstract抽象類的和final的類。
B).若是將變量或者方法聲明爲final,能夠保證它們在使用中不被改變.
1)被聲明爲final的變量必須在聲明時給定初值,而在之後的引用中只能讀取,不可修改。
2)被聲明final的方法只能使用,不能重載。
finally:java的一種異常處理機制。
finally是對Java異常處理模型的最佳補充。finally結構使代碼總會執行,而無論無異常發生。使用finally能夠維護對象的內部狀態,並能夠清理非內存資源。特別是在關閉數據庫鏈接這方面,若是程序員把數據庫鏈接的close()方法放到finally中,就會大大下降程序出錯的概率。
finalize:Java中的一個方法名。
Java技術使用finalize()方法在垃圾收集器將對象從內存中清除出去前,作必要的清理工做。這個方法是由垃圾收集器在肯定這個對象沒被引用時對這個對象調用的。它是在Object類中定義的,所以所的類都繼承了它。子類覆蓋finalize()方法以整理系統資源或者執行其餘清理工做。finalize()方法是在垃圾收集器刪除對象以前對這個對象調用的。
3.詳細區別:
這是一道再經典不過的面試題了,咱們在各個公司的面試題中幾乎都能看到它的身影。final、finally和finalize雖然長得像孿生兄弟同樣,可是它們的含義和用法倒是截然不同。
final關鍵字咱們首先來講說final。它能夠用於如下四個地方:
1).定義變量,包括靜態的和非靜態的。
2).定義方法的參數。
3).定義方法。
4).定義類。
定義變量,包括靜態的和非靜態的。定義方法的參數
第一種狀況:
若是final修飾的是一個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;
若是final修飾的是一個對象,就表示這個變量被賦予的引用是不可變的
這裏須要提醒你們注意的是,不可改變的只是這個變量所保存的引用,並非這個引用所指向的對象。
第二種狀況:final的含義與第一種狀況相同。
實際上對於前兩種狀況,一種更貼切的表述final的含義的描述,那就是,若是一個變量或方法參數被final修飾,就表示它只能被賦值一次,可是JAVA虛擬機爲變量設定的默認值不記做一次賦值。被final修飾的變量必須被初始化。初始化的方式如下幾種:
1.在定義的時候初始化。
2.final變量能夠在初始化塊中初始化,不能夠在靜態初始化塊中初始化。
3.靜態final變量能夠在定義時初始化,也能夠在靜態初始化塊中初始化,不能夠在初始化塊中初始化。
4.final變量還能夠在類的構造器中初始化,可是靜態final變量不能夠。
經過下面的代碼能夠驗證以上的觀點:html
public class FinalTest{ public final int A=10; //在定義時初始化 public final int B;{B=20;} //在初始化塊中初始化 //非靜態final變量不能在靜態初始化塊中初始化 //public final int C;static{//C=30; } //靜態常量,在定義時初始化 public static final int STATIC_D=40; //靜態常量,在靜態初始化塊中初始化 public static final int STATIC_E;static{STATIC_E = 50;} //靜態變量不能在初始化塊中初始化 //public static final int STATIC_F;{STATIC_F=60;} public final int G; //靜態final變量不能夠在構造器中初始化 //public static final int STATIC_H; //在構造器中初始化 public finalTest(){ G=70; //靜態final變量不能夠在構造器中初始化 //STATIC_H=80; //給final的變量第二次賦值時,編譯會報錯 //A=99; //STATIC_D=99; } //final變量未被初始化,編譯時就會報錯 //public final int L; //靜態final變量未被初始化,編譯時就會報錯 //public static final int STATIC_J; }
咱們運行上面的代碼以後出了能夠發現final變量(常量和靜態final變量(靜態常量被初始化時,編譯會報錯。
用final修飾的變量(常量比非final的變量(普通變量擁更高的效率,所以咱們在際編程中應該儘量多的用常量來代替普通變量。
定義方法
當final用來定義一個方法時,它表示這個方法不能夠被子類重寫,可是並不影響它被子類繼承。咱們寫段代碼來驗證一下:java
public class ParentClass{ public final void TestFinal(){ System.out.println("父類--這是一個final方法"); } } public class SubClass extends ParentClass{ //子類沒法重寫(override父類的final方法,不然編譯時會報錯 /* public void TestFinal(){ System.out.println("子類--重寫final方法"); } */ public static void main(String[]args){ SubClass sc = new SubClass(); sc.TestFinal(); } }
這裏須要特殊說明的是,具備private訪問權限的方法也能夠增長final修飾,可是因爲子類沒法繼承private方法,所以也沒法重寫它。編譯器在處理private方法時,是照final方來對待的,這樣能夠提升該方法被調用時的效率。不過子類仍然能夠定義同父類中private方法具一樣結構的方法,可是這並不會產生重寫的效果,並且它們之間也不存在必然聯繫。
定義類
最後咱們再來回顧一下final用於類的狀況。這個你們應該也很熟悉了,由於咱們最經常使用的String類就是final的。因爲final類不容許被繼承,編譯器在處理時把它的所方法都看成final的,所以final類比普通類擁更高的效率。而由關鍵字abstract定義的抽象類含必須由繼承自它的子類重載實現的抽象方法,所以沒法同時用final和abstract來修飾同一個類。一樣的道理,
final也不能用來修飾接口。 final的類的所方法都不能被重寫,但這並不表示final的類的屬性(變量值也是不可改變的,要想作到final類的屬性值不可改變,必須給它增長final修飾,請看下面的例子:程序員
public final class FinalTest{ int i =20; final int j=50; public static void main(String[] args){ FinalTest ft = new FinalTest(); ft.i = 99;/*final類FinalTest的屬性值 i是能夠改變的,由於屬性值i前面沒final修飾*/ //ft.j=49;//報錯....由於j屬性是final的不能夠改變。 System.out.println(ft.i); } }
運行上面的代碼試試看,結果是99,而不是初始化時的10。
finally語句
接下來咱們一塊兒回顧一下finally的用法。finally只能用在try/catch語句中而且附帶着一個語句塊,表示這段語句最終老是被執行。請看下面的代碼:面試
public final class FinallyTest{ public static void main(String[] args){ try{ throw new NullPointerException(); }catch(NullPointerException e){ System.out.println("程序拋出了異常"); }finally{ //這裏總會被執行,不受break,return影響另如數據庫鏈接的close()通常寫在這裏,能夠下降程序的出錯概率 System.out.println("執行了finally語句塊"); } } }
運行結果說明了finally的做用:數據庫
1.程序拋出了異常編程
2.執行了finally語句塊請你們注意,捕獲程序拋出的異常以後,既不加處理,也不繼續向上拋出異常,並非良好的編程習慣,它掩蓋了程序執行中發生的錯誤,這裏只是方便演示,請不要學習。
那麼,沒一種狀況使finally語句塊得不到執行呢?
return、continue、break這個能夠打亂代碼順序執行語句的規律。那咱們就來試試看,這個語句是否能影響finally語句塊的執行:安全
public final class FinallyTest { //測試return語句 //結果顯示:編譯器在編譯return new ReturnClass();時, //將它分紅了兩個步驟,new ReturnClass()和return,前一個建立對象的語句是在finally語句塊以前被執行的, //然後一個return語句是在finally語句塊以後執行的,也就是說finally語句塊是在程序退出方法以前被執行的 public ReturnClass testReturn() { try { return new ReturnClass(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("執行了finally語句"); } return null; } //測試continue語句 public void testContinue(){ for(int i=0; i<3; i++){ try { System.out.println(i); if(i == 1){ System.out.println("con"); } } catch(Exception e) { e.printStackTrace(); } finally { System.out.println("執行了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("執行了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("執行了return語句"); } }
上面這段代碼的運行結果以下:
執行了return語句 執行了finally語句 0 執行了finally語句 1 con 執行了finally語句 2 執行了finally語句 0 執行了finally語句 1 執行了finally語句
很明顯,return、continue和break都沒能阻止finally語句塊的執行。從輸出的結果來看,return語句彷佛在finally語句塊以前執行了,事實真的如此嗎?咱們來想一想看,return語句的做用是什麼呢?是退出當前的方法,並將值或對象返回。若是 finally語句塊是在return語句以後執行的,那麼return語句被執行後就已經退出當前方法了,finally語句塊又如何能被執行呢?所以,正確的執行順序應該是這樣的:編譯器在編譯return new ReturnClass();時,將它分紅了兩個步驟,new ReturnClass()和return,前一個建立對象的語句是在finally語句塊以前被執行的,然後一個return語句是在finally語句塊以後執行的,也就是說finally語句塊是在程序退出方法以前被執行的。一樣,finally語句塊是在循環被跳過(continue和中斷(break以前被執行的
finalize方法
最後,咱們再來看看finalize,它是一個方法,屬於java.lang.Object類,它的定義以下:protected void finalize()throws Throwable{}衆所周知,finalize()方法是GC(garbagecollector運行機制的一部分,在此咱們只說說finalize()方法的做用是什麼呢?finalize()方法是在GC清理它所從屬的對象時被調用的,若是執行它的過程當中拋出了沒法捕獲的異常(uncaughtexception,GC將終止對改對象的清理,而且該異常會被忽略;直到下一次GC開始清理這個對象時,它的finalize()會被再次調用。請看下面的示例:ide
public final class FinallyTest{ //重寫finalize()方法 protected void finalize() throws Throwable{ System.out.println("執行了finalize()方法"); } public static void main(String[] args){ FinallyTest ft = new FinallyTest(); ft = null; System.gc(); } }
運行結果以下:• 執行了finalize()方法
程序調用了java.lang.System類的gc()方法,引發GC的執行,GC在清理ft對象時調用了它的finalize()方法,所以才了上面的輸出結果。調用System.gc()等同於調用下面這行代碼:Runtime.getRuntime().gc();調用它們的做用只是建議垃圾收集器(GC啓動,清理無用的對象釋放內存空間,可是GC的啓動並非必定的,這由JAVA虛擬機來決定。直到 JAVA虛擬機中止運行,些對象的finalize()可能都沒被運行過,那麼怎樣保證所對象的這個方法在JAVA虛擬機中止運行以前必定被調用呢?答案是咱們能夠調用System類的另外一個方法:post
public static void FunFinalizersOnExit(boolean value){ //othercode }
給這個方法傳入true就能夠保證對象的finalize()方法在JAVA虛擬機中止運行前必定被運行了,不過遺憾的是這個方法是不安全的,它會致使有用的對象finalize()被誤調用,所以已不被同意使用了。因爲finalize()屬於Object類,所以所類都這個方法,Object的任意子類均可以重寫(override該方法,在其中釋放系統資源或者作其它的清理工做,如關閉輸入輸出流。經過以上知識的回顧,我想你們對於final、finally、finalize的用法區別已經很清楚了。學習