淺析Java中的final關鍵字

原文出處: 海子html

談到final關鍵字,想必不少人都不陌生,在使用匿名內部類的時候可能會常常用到final關鍵字。另外,Java中的String類就是一個final類,那麼今天咱們就來了解final這個關鍵字的用法。下面是本文的目錄大綱:java

一.final關鍵字的基本用法編程

二.深刻理解final關鍵字安全

如有不正之處,請多多諒解並歡迎指正。架構

一.final關鍵字的基本用法

在Java中,final關鍵字能夠用來修飾類、方法和變量(包括成員變量和局部變量)。下面就從這三個方面來了解一下final關鍵字的基本用法。app

1.修飾類

當用final修飾一個類時,代表這個類不能被繼承。也就是說,若是一個類你永遠不會讓他被繼承,就能夠用final進行修飾。final類中的成員變量能夠根據須要設爲final,可是要注意final類中的全部成員方法都會被隱式地指定爲final方法。dom

在使用final修飾類的時候,要注意謹慎選擇,除非這個類真的在之後不會用來繼承或者出於安全的考慮,儘可能不要將類設計爲final類。性能

2.修飾方法

下面這段話摘自《Java編程思想》第四版第143頁:優化

「使用final方法的緣由有兩個。第一個緣由是把方法鎖定,以防任何繼承類修改它的含義;第二個緣由是效率。在早期的Java實現版本中,會將final方法轉爲內嵌調用。可是若是方法過於龐大,可能看不到內嵌調用帶來的任何性能提高。在最近的Java版本中,不須要使用final方法進行這些優化了。「網站

所以,若是隻有在想明確禁止 該方法在子類中被覆蓋的狀況下才將方法設置爲final的。

注:類的private方法會隱式地被指定爲final方法

3.修飾變量

修飾變量是final用得最多的地方,也是本文接下來要重點闡述的內容。首先了解一下final變量的基本語法:

對於一個final變量,若是是基本數據類型的變量,則其數值一旦在初始化以後便不能更改;若是是引用類型的變量,則在對其初始化以後便不能再讓其指向另外一個對象。

舉個例子:

上面的一段代碼中,對變量i和obj的從新賦值都報錯了。

二.深刻理解final關鍵字

在瞭解了final關鍵字的基本用法以後,這一節咱們來看一下final關鍵字容易混淆的地方。

1.類的final變量和普通變量有什麼區別?

當用final做用於類的成員變量時,成員變量(注意是類的成員變量,局部變量只須要保證在使用以前被初始化賦值便可)必須在定義時或者構造器中進行初始化賦值,並且final變量一旦被初始化賦值以後,就不能再被賦值了。

那麼final變量和普通變量到底有何區別呢?下面請看一個例子:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
     public static void main(String[] args)  {
         String a = "hello2" ;
         final String b = "hello" ;
         String d = "hello" ;
         String c = b + 2 ;
         String e = d + 2 ;
         System.out.println((a == c));
         System.out.println((a == e));
     }
}
1
2
true
false

你們能夠先想一下這道題的輸出結果。爲何第一個比較結果爲true,而第二個比較結果爲fasle。這裏面就是final變量和普通變量的區別了,當final變量是基本數據類型以及String類型時,若是在編譯期間能知道它的確切值,則編譯器會把它當作編譯期常量使用。也就是說在用到該final變量的地方,至關於直接訪問的這個常量,不須要在運行時肯定。這種和C語言中的宏替換有點像。所以在上面的一段代碼中,因爲變量b被final修飾,所以會被當作編譯器常量,因此在使用到b的地方會直接將變量b 替換爲它的  值。而對於變量d的訪問卻須要在運行時經過連接來進行。想必其中的區別你們應該明白了,不過要注意,只有在編譯期間能確切知道final變量值的狀況下,編譯器纔會進行這樣的優化,好比下面的這段代碼就不會進行優化:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
     public static void main(String[] args)  {
         String a = "hello2" ;
         final String b = getHello();
         String c = b + 2 ;
         System.out.println((a == c));
 
     }
 
     public static String getHello() {
         return "hello" ;
     }
}

這段代碼的輸出結果爲false。

2.被final修飾的引用變量指向的對象內容可變嗎?

在上面提到被final修飾的引用變量一旦初始化賦值以後就不能再指向其餘的對象,那麼該引用變量指向的對象的內容可變嗎?看下面這個例子:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
     public static void main(String[] args)  {
         final MyClass myClass = new MyClass();
         System.out.println(++myClass.i);
 
     }
}
 
class MyClass {
     public int i = 0 ;
}

這段代碼能夠順利編譯經過而且有輸出結果,輸出結果爲1。這說明引用變量被final修飾以後,雖然不能再指向其餘對象,可是它指向的對象的內容是可變的。

3.final和static

不少時候會容易把static和final關鍵字混淆,static做用於成員變量用來表示只保存一份副本,而final的做用是用來保證變量不可變。看下面這個例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
     public static void main(String[] args)  {
         MyClass myClass1 = new MyClass();
         MyClass myClass2 = new MyClass();
         System.out.println(myClass1.i);
         System.out.println(myClass1.j);
         System.out.println(myClass2.i);
         System.out.println(myClass2.j);
 
     }
}
 
class MyClass {
     public final double i = Math.random();
     public static double j = Math.random();
}

運行這段代碼就會發現,每次打印的兩個j值都是同樣的,而i的值倒是不一樣的。從這裏就能夠知道final和static變量的區別了。

4.匿名內部類中使用的外部局部變量爲何只能是final變量?

這個問題請參見上一篇博文中《Java內部類詳解》中的解釋,在此處再也不贅述。

5.關於final參數的問題

關於網上流傳的」當你在方法中不須要改變做爲參數的對象變量時,明確使用final進行聲明,會防止你無心的修改而影響到調用方法外的變量「這句話,我我的理解這樣說是不恰當的。

由於不管參數是基本數據類型的變量仍是引用類型的變量,使用final聲明都不會達到上面所說的效果。

看這個例子就清楚了:

上面這段代碼好像讓人以爲用final修飾以後,就不能在方法中更改變量i的值了。卻不知,方法changeValue和main方法中的變量i根本就不是一個變量,由於java參數傳遞採用的是值傳遞,對於基本類型的變量,至關於直接將變量進行了拷貝。因此即便沒有final修飾的狀況下,在方法內部改變了變量i的值也不會影響方法外的i。

再看下面這段代碼:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
     public static void main(String[] args)  {
         MyClass myClass = new MyClass();
         StringBuffer buffer = new StringBuffer( "hello" );
         myClass.changeValue(buffer);
         System.out.println(buffer.toString());
     }
}
 
class MyClass {
 
     void changeValue( final StringBuffer buffer) {
         buffer.append( "world" );
     }
}

運行這段代碼就會發現輸出結果爲 helloworld。很顯然,用final進行修飾並無阻止在changeValue中改變buffer指向的對象的內容。有人說假如把final去掉了,萬一在changeValue中讓buffer指向了其餘對象怎麼辦。有這種想法的朋友能夠本身動手寫代碼試一下這樣的結果是什麼,若是把final去掉了,而後在changeValue中讓buffer指向了其餘對象,也不會影響到main方法中的buffer,緣由在於java採用的是值傳遞,對於引用變量,傳遞的是引用的值,也就是說讓實參和形參同時指向了同一個對象,所以讓形參從新指向另外一個對象對實參並無任何影響。

http://cxy.liuzhihengseo.com/460.html

問啊-定製化IT教育平臺,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com 下載問啊APP,參與官方懸賞,賺百元現金。


QQ羣290551701 彙集不少互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!

相關文章
相關標籤/搜索