咨詢電話:023-6276-4481
熱門文章
電 話:023-6276-4481
郵箱:broiling@qq.com
地址:重慶市南岸區(qū)亞太商谷6幢25-2
我們首先來說說final。它可以用于以下四個地方:
定義變量,包括靜態(tài)的和非靜態(tài)的。
定義方法的參數(shù)。
定義方法。
定義類。我們依次來回顧一下每種情況下final的作用。
第一種情況:
如果final修飾的是一個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;
如果final修飾的是一個對象,就表示這個變量被賦予的引用是不可變的
這里需要提醒大家注意的是,不可改變的只是這個變量所保存的引用,并不是這個引用所指向的對象。
第二種情況:
final的含義與第一種情況相同。
實際上對于前兩種情況,有一種更貼切的表述final的含義的描述,那就是,如果一個變量或方法參數(shù)被final修飾,就表示它只能被賦值一次,但是JAVA虛擬機為變量設定的默認值不記作一次賦值。
被final修飾的變量必須被初始化。初始化的方式有以下幾種:
在定義的時候初始化。
final變量可以在初始化塊中初始化,不可以在靜態(tài)初始化塊中初始化。
靜態(tài)final變量可以在靜態(tài)初始化塊中初始化,不可以在初始化塊中初始化。
final變量還可以在類的構造器中初始化,但是靜態(tài)final變量不可以。
通過下面的代碼可以驗證以上的觀點:
Java代碼
public class FinalTest { // 在定義時初始化 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)常量,在定義時初始化 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變量不可以在構造器中初始化 // public static final int STATIC_H; // 在構造器中初始化 public FinalTest() { G = 70;// 靜態(tài)final變量不可以在構造器中初始化// STATIC_H = 80;// 給final的變量第二次賦值時,編譯會報錯// A = 99;// STATIC_D = 99;}// final變量未被初始化,編譯時就會報錯// public final int I;// 靜態(tài)final變量未被初始化,編譯時就會報錯// public static final int STATIC_J;}
我們運行上面的代碼之后出了可以發(fā)現(xiàn)final變量(常量)和靜態(tài)final變量(靜態(tài)常量)被初始化時,編譯會報錯。
用final修飾的變量(常量)比非final的變量(普通變量)擁有更高的效率,因此我們在際編程中應該盡可能多的用常量來代替普通變量,這也是一個很好的編程習慣。
Java代碼
public class ParentClass { public final void TestFinal() { System.out.println("父類--這是一個final方法"); } } public class SubClass extends ParentClass { /** * 子類無法重寫(override)父類的final方法,否則編譯時會報錯 */ // public void www.gzlij.com TestFinal() { // System.out.println("子類--重寫final方法"); // } public static void main(String[] args) { SubClass sc = new SubClass(); sc.TestFinal(); } }
這里需要特殊說明的是,具有private訪問權限的方法也可以增加final修飾,但是由于子無法繼承private方法,因此也無法重寫它。編譯器在處理private方法時,是按照final方來對待的,這樣可以提高該方法被調(diào)用時的效率。不過子類仍然可以定義同父類中private方法具有同樣結構的方法,但是這并不會產(chǎn)生重寫的效果,而且它們之間也不存在必然聯(lián)系。
最后我們再來回顧一下final用于類的情況。這個大家應該也很熟悉了,因為我們最常用的String類就是final的。由于final類不允許被繼承,編譯器在處理時把它的所有方法都當作final的,因此final類比普通類擁有更高的效率。而由關鍵字abstract定義的抽象類含有必須由繼承自它的子類重載實現(xiàn)的抽象方法,因此無法同時用final和abstract來修飾同一個類。同樣的道理,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是可以改變的,因為屬性值i前面沒有final修//// ft.j = 49; // 報錯....因為 j 屬性是final 的不可以改變。System.out.println(ft.i); } }
運行上面的代碼試試看,結果是99,而不是初始化時的10。
接下來我們一起回顧一下finally的用法。這個就比較簡單了,它只能用在try/catch語句中并且附帶著一個語句塊,表示這段語句最終總是被執(zhí)行。請看下面的代碼:
Java代碼
public final class FinallyTest {public static void main(String[] args) {try {throw new NullPointerException(); } catch (NullPointerException e) { System.out.println("程序拋出了異常"); } finally {//這里總會被執(zhí)行,不受break,return影響另如數(shù)據(jù)庫連接的close()一般寫在這里,可以降低程序的出錯幾率System.out.println("執(zhí)行了finally語句塊"); } } }
運行結果說明了finally的作用: 1. 程序拋出了異常 2. 執(zhí)行了finally語句塊請大家注意,捕獲程序拋出的異常之后,既不加處理,也不繼續(xù)向上拋出異常,并不是良好的編程習慣,它掩蓋了程序執(zhí)行中發(fā)生的錯誤,這里只是方便演示,請不要學習。
那么,有沒有一種情況使finally語句塊得不到執(zhí)行呢?大家可能想到了
return、continue、break這三個可以打亂代碼順序執(zhí)行語句的規(guī)律。那我們就來試試看,這三個語句是否能影響finally語句塊的執(zhí)行:
Java代碼public final class FinallyTest {
// 測試return語句
結果顯示:編譯器在編譯return new ReturnClass();時,將它分成了兩個步驟,new ReturnClass()和return,前一個創(chuàng)建對象的語句是在finally語句塊之前被執(zhí)行的,而后一個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í)行。從輸出的結果來看,return語句似乎在 finally語句塊之前執(zhí)行了,事實真的如此嗎?我們來想想看,return語句的作用是什么呢?是退出當前的方法,并將值或對象返回。如果 finally語句塊是在return語句之后執(zhí)行的,那么return語句被執(zhí)行后就已經(jīng)退出當前方法了,finally語句塊又如何能被執(zhí)行呢?因此,正確的執(zhí)行順序應該是這樣的:編譯器在編譯return new ReturnClass();時,將它分成了兩個步驟,new ReturnClass()和return,前一個創(chuàng)建對象的語句是在finally語句塊
之前被執(zhí)行的,而后一個return語句是在finally語句塊之后執(zhí)行的,也就是說finally語句塊是在程序退出方法之前被執(zhí)行的。同樣,finally語句塊是在循環(huán)被跳過(continue)和中斷(break)之前被執(zhí)行的。
最后,我們再來看看finalize,它是一個方法,屬于java.lang.Object類,它的定義如下:Java代碼protected void finalize() throws Throwable { }眾所周知,finalize()方法是GC(garbage collector)運行機制的一部分在此我們只說說finalize()方法的作用是什么呢?finalize()方法是在GC清理它所從屬的對象時被調(diào)用的,如果執(zhí)行它的過程中拋出了無法捕獲的異常(uncaught exception),GC將終止對改對象的清理,并且該異常會被忽略;直到下一次GC開始清理這個對象時,它的finalize()會被再次調(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(); } }
運行結果如下:? 執(zhí)行了finalize()方法
程序調(diào)用了java.lang.System類的gc()方法,引起GC的執(zhí)行,GC在清理ft對象時調(diào)用了它的finalize()方法,因此才有了上面的輸出結果。調(diào)用System.gc()等同于調(diào)用下面這行代碼:Java代碼Runtime.getRuntime().gc();調(diào)用它們的作用只是建議垃圾收集器(GC)啟動,清理無用的對象釋放內(nèi)存空間,但是G的啟動并不是一定的,這由JAVA虛擬機來決定。直到 JAVA虛擬機停止運行,有些對象的finalize()可能都沒有被運行過,那么怎樣保證所有對象的這個方法在JAVA虛擬機停止運行之前一定被調(diào)用呢?答案是我們可以調(diào)用System類的另一個方法:
Java代碼public static void runFinalizersOnExit(boolean value) {//other code} 給這個方法傳入true就可以保證對象的finalize()方法在JAVA虛擬機停止運行前一定被運行了,不過遺憾的是這個方法是不安全的,它會導致有用的對象finalize()被誤調(diào)用,因此已不被贊成使用了。 由于finalize()屬于Object類,因此所有類都有這個方法,Object的任意子類都可以重寫(override)該方法,在其中釋放系統(tǒng)資源或者做其它的清理工作,如關閉輸入輸出流。