1. 程式人生 > >Java基礎(三)-final關鍵字分析

Java基礎(三)-final關鍵字分析

signed 主題 long java style rri test 引用類型 初始

今天來談談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關鍵字分析