Java基礎(三)-final關鍵字分析
今天來談談final關鍵字的作用, 雖然有很多博文關於final進行了很深的研究,但還是要去記錄下談談自己的見解加深下印象。下面直接進入主題:
一、final關鍵字的作用
1、被final修飾的類不能被繼承。
這一點應該很多人都知道也遇到過,經典案例就是java.lang.String類
還有一些常見的類也是被final所修飾的,如下:
基本類型對應的包裝類型(如java.lang.Integer、java.lang.Long等)、字符相關類(java.lang.StringBuilder、java.lang.StringBuffer)、系統類(java.lang.Class、java.lang.System)等。就列這些其他就靠自己平時去發現。
那麽問題來了,a、為什麽final修飾的類不能被繼承?答:這是Java語法定義的,沒法。
b、這樣設計的目的是什麽?答:因為類不需要被拓展類的、實現細節不允許改變,估計是為了安全考慮吧。
2、被final修飾的方法不能被重寫
其實這也是Java語法規定的,沒法做解釋。但是仔細回憶,這種情況跟static關鍵字修飾方法中一個特點類似,也是不能重寫(覆蓋)。
下面我們看案例(代碼經過自己敲出來的才最有印象):
class MyClass{ final void test(){ System.out.println("FinalClass"); } }class MyClass2 extends MyClass { //編譯報錯:Cannot override the final method from MyClass public void test(){ System.out.println("FinalClass"); } }
3、被final修飾的變量不能被“改變”
先說下前提1:被final修飾的變量不像static那樣。它也可以修飾局部變量。
前提2:被final修飾的變量一定要被初始化,否則編譯不通過。
針對前提,我們先通過案例證明:
1 public class FinalTest {2 //編譯失敗,不滿足前提2。The blank final field count may not have been initialized 3 final int count; 4 public static void main(String[] args) { 5 //編譯通過。前提1:被final修飾的變量不像static那樣。它也可以修飾局部變量。 6 final int t = 0; 7 } 8 }
初始化有兩種:直接初始化和在構造函數中初始化(每個構造函數都要初始化即每個實例化對象的入口都要進行初始化)。
public class FinalTest { //直接初始化 final int count = 0; final int num; //構造函數中初始化,如果沒有對num進行初始化,就會編譯錯誤。The blank final field num may not have been initialized public FinalTest(){ num = 0;//註釋這樣就可以看到錯誤提示信息 } public FinalTest(int t){ num = 0; //this();//這兩行左右開啟一樣才不會報錯。 } }
回歸重點,被final修飾的變量,它是什麽不能改變呢?變量值還是變量的引用還是兩者都不能?看似有點玄乎(是不是自己有些沒考慮到),其實也很簡單(平時多留意就行)。依次舉例證明:
案例1(以基本類型為例):
1 public class FinalTest { 2 final int count = 0; 3 4 public int getCount () { 5 //The final field FinalTest.count cannot be assigned 6 return count ++; 7 } 8 9 public static void main(String[] args) { 10 FinalTest t = new FinalTest(); 11 System.out.println(t.getCount()); 12 } 13 }
上面代碼中第六行報錯(The final field FinalTest.count cannot be assigned)了,所以可以得知:對於這種基本類型的變量被final所修飾後,它的值是不能被更改的。
案例2(以對象為例):
1 class Count { 2 int count = 0; 3 public int getCount () { 4 return ++ count; 5 } 6 } 7 8 public class FinalTest { 9 10 public static void main(String[] args) { 11 final Count count1 = new Count(); 12 final Count count2 = new Count(); 13 System.out.println(count1.getCount()); 14 System.out.println(count2.getCount()); 15 //The final local variable count1 cannot be assigned. It must be blank and not using a compound assignment 16 count1 = count2; 17 } 18 }
第16行同樣的報錯信息,但是這個就有點不一樣:對象裏面的成員的值是可以改變的。所以針對這種對象變量而言,被final修飾後不可變的是變量的引用,而不是變量的內容。
總結下這點:被final修飾的基本類型變量,它的值是不可變的。被final修飾的引用類型變量,它的引用地址是不可變的,對象裏的內容是可變的。
二、final關鍵字的拓展
1、在匿名類中使用外部內的變量,則該變量必須是final所修飾的。下面案例中第10就會編譯報錯,提示必須是final修飾的變量。
1 public class FinalTest { 2 3 public static void main(String[] args) { 4 int count = 0; 5 6 Thread thread1 = new Thread(new Runnable() { 7 @Override 8 public void run() { 9 //Cannot refer to the non-final local variable count defined in an enclosing scope 10 count ++; 11 } 12 }); 13 } 14 }
2、其實final還可以修飾形參。這樣做的主要目的是防止無意的修改而影響到調用方法外的變量。如果你沒了解這句就說明上面第三點作用你還沒了解。
1 class Count { 2 int count = 0; 3 public int getCount () { 4 return ++ count; 5 } 6 } 7 8 public class FinalTest { 9 int num = 0; 10 public static void main(String[] args) { 11 final Count count = new Count(); 12 addCount(count); 13 System.out.println(count.count); 14 } 15 public static void addCount(final Count count){ 16 count.getCount(); 17 //count = new Count();//這種就是篡改。 18 } 19 }
3、final變量與普通變量有什麽區別,什麽時候可以相等?看下下面代碼,想下代碼輸出什麽。
1 public class FinalTest2 { 2 3 public static void main(String[] args) { 4 final String str1 = "test"; 5 final String str2 = getContent(); 6 String str3 = "test"; 7 8 String str4 = str1 + ""; 9 String str5 = str2 + ""; 10 11 System.out.println(str3 == str4); 12 System.out.println(str3 == str5); 13 } 14 public static String getContent(){ 15 return "test"; 16 } 17 }
輸出後的結果為true和false。這是為什麽呢?解釋下你就清楚這兩者的區別了。如果是final修飾直接定義的字符串或者是基本類型,它在編譯期間就會確定其值,則編譯器會把它當做常量。所以當有使用到它的地方會直接用常量替換。而其他都是運行時才會確定的值所以依然使用變量去計算。在代碼中str2變量,雖然用是final修飾但是它的值要在的運行時才能確定,所以它相當於普通變量。而str5這種計算方式並不是我們想象的簡單,因為str2在這裏成了普通變量,所以會通過stringBulider去計算整個表達式的值,所以返回也是一個新的str,引用地址變了。所以第12行的輸出為false;
4、final與finally 和finalize的區別
finally是異常處理語句結構的一部分,表示最終執行。
finalize是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,供垃圾收集時的其他資源回收,例如關閉文件等。
Java基礎(三)-final關鍵字分析