1. 修飾變數
該變數既可以是局部變數也可以是成員變數,final關鍵字修飾的成員變數即常量,可以被繼承或者覆蓋和隱藏!主要從以下兩方面來說明問題。
<1> 修飾基礎資料型別 (Elementary Data Type)
final修飾的該變數就是常量,一旦賦值不可以改變其值。
一般情況,final修飾成員變數的話與static結合,使該常量可以被類直接調用,而不必建立對象,節省空間的。再者,如果該常量中不含不確定因素,就稱之為編譯時間期常量,反之為運行期常量。編譯時間期常量被調用時不會載入該類。http://blog.csdn.net/AndroidBluetooth/archive/2011/05/30/6454135.aspx
注意:測試方法(main方法)寫在該類中會載入該類的,所以測試方法放在單獨一個類中。例子:
package mark.zhang;<br />public class Linux {<br />// 修飾成員變數<br />public static final String NAME = "LINUX";<br />static {<br />System.out.println("--Linux---靜態代碼塊");<br />}<br />{<br />System.out.println("--Linux---動態代碼塊");<br />}<br />public Linux() {<br />}<br />public void getName() {<br />// 修飾局部變數<br />final int i = 7;<br />}<br />}<br />class Test {<br />public static void main(String[] args) {<br />System.out.println(Linux.NAME);<br />}<br />}
結果:
LINUX
還有這樣一種情況,不想在聲明常量的時候,給其賦值,那麼怎麼辦?
我們知道,如果在聲明常量的同時不將其賦值,編譯器會給我們一個類似The blank final field NAME may not have been initialized的錯誤。
第一種辦法,在靜態代碼塊中給常量賦值。
package mark.zhang;<br />public class Linux {<br />// 修飾成員變數<br />public static final String NAME;<br />static {<br /> System.out.println("--Linux---靜態代碼塊");<br /> NAME = "LINUX";<br /> }<br />{<br /> System.out.println("--Linux---動態代碼塊");<br />}<br /> public Linux() {<br /> }<br />}<br />class Test {<br /> public static void main(String[] args) {<br /> System.out.println(Linux.NAME);<br /> }<br />}
結果:
--Linux---靜態代碼塊<br />LINUX
分析:由於NAME是含有不確定因素,所以會載入靜態代碼塊,從而將其初始化。
注意:動態代碼塊無法完成final變數的初始化
第二種方法,在構造方法中初始化
如果這樣做,那麼成員常量不可以被static修飾。
static無法修飾構造方法,因為構造方法是在建立對象時被調用的,而static修飾的,只會在載入該類時被載入一次。
package mark.zhang;<br />public class Linux {<br />// 修飾成員變數,這裡沒有static<br />public final String NAME;<br />static {<br />System.out.println("--Linux---靜態代碼塊");<br />}<br />{<br />System.out.println("--Linux---動態代碼塊");<br />}<br />public Linux() {<br />NAME = "LINUX";<br />}<br />}<br />class Test {<br />public static void main(String[] args) {<br />System.out.println(new Linux().NAME);<br />}<br />}
結果:
--Linux---靜態代碼塊<br />--Linux---動態代碼塊<br />LINUX
那麼,有人說了,現在只有一個構造方法,如果再加一個或者n個構造方法呢,難道還要初始化嗎???yes,恭喜你,答對了。必須的,否則你得到類似這樣一個錯誤:
The blank final field NAME may not have been initialized
例子:
public Linux() {<br />NAME = "LINUX";<br />}<br />public Linux(String name) {<br />NAME = "LINUX--LINUX";<br />}
所以,正常情況下,不會使用這種方式。
<2> 修飾參考型別資料
注意:數組也是參考型別。
final修飾的引用不變指的是它的棧記憶體位址不變,即永遠指向那個堆裡的對象,但是堆裡的對象(屬性)是可變的!
樣本:
package mark.zhang;<br />public class Linux {<br />private String name = "linux OS";<br />static {<br />System.out.println("--Linux---靜態代碼塊");<br />}<br />{<br />System.out.println("--Linux---動態代碼塊");<br />}<br />public Linux() {<br />}<br />public void setName(String name) {<br />this.name = name;<br />}<br />public String getName() {<br />return name;<br />}<br />}
測試方法:
class Test {<br />public static void main(String[] args) {<br />final Linux linux = new Linux();<br />System.out.println("linux's name : " + linux.getName());<br />// error: The final local variable linux cannot be assigned. It must be<br />// blank and not using a compound assignment<br />// linux = new Linux();<br />}<br />}
但是我們可以修改其屬性值。
class Test {<br />public static void main(String[] args) {<br />final Linux linux = new Linux();<br />System.out.println("before--" + "linux's name : " + linux.getName());<br />linux.setName("ubuntu10.10");<br />System.out.println("after--" + "linux's name : " + linux.getName());<br />}<br />}
結果:
--Linux---靜態代碼塊<br />--Linux---動態代碼塊<br />before--linux's name : linux OS<br />after--linux's name : ubuntu10.10
2. 修飾類
final關鍵字修飾的類,類中成員方法預設為final,無法被繼承!Java提供的包中,String類就是一個用關鍵字final修飾的類,所以其他類無法繼承他!但是final關鍵字修飾的類可以作為子類,即可以繼承其他類!
3. 修飾方法
final關鍵字修飾的方法,可以被繼承,但是不能被覆蓋!這樣可以保證代碼的安全性。
注意:final和static一樣不可以修飾構造方法。
package mark.zhang;<br />public class Linux {<br />static {<br />System.out.println("--Linux---靜態代碼塊");<br />}<br />{<br />System.out.println("--Linux---動態代碼塊");<br />}<br />public Linux() {<br />}<br />/**<br /> * final修飾的方法可以被子類繼承,但是不可以被子類重寫 子類中也不可以與同名的該方法<br /> */<br />public final void getInfo() {<br />System.out.println("linux2.38");<br />}<br />}<br />class Ubuntu extends Linux {<br />// 一下兩種方式均會包類似下面的錯誤<br />// Cannot override the final method from Linux<br />/*<br /> * public final void getInfo() { System.out.println("ubuntu10.10"); }<br /> */<br />/*<br /> * public void getInfo() { System.out.println("ubuntu10.10"); }<br /> */<br />}<br />class Test {<br />public static void main(String[] args) {<br />Ubuntu ubuntu = new Ubuntu();<br />// 可以繼承父類的final方法<br />ubuntu.getInfo();<br />}<br />}<br />
4. final與abstract
final修飾的類不可以被繼承,然而abstract修飾的類本意就是希望子類去繼承。所以,在Java中不允許final修飾abstract,無論是abstract方法還是abstract類。
如果你這樣子去修飾一個抽象類別的話:
final abstract class Ubuntu {<br />abstract void get();<br />}
編譯器毫不客氣的給你一個錯誤資訊:
The class Ubuntu can be either abstract or final, not both
同樣的道理,final也不可以修飾abstract方法,如:
abstract class Ubuntu {<br />final public abstract void get();<br />}
錯誤:
The abstract method get in type Ubuntu can only set a visibility modifier, one of public or protected
從這句話中我們也可以得出結論:抽象方法不可以是private的,可以是default或者public或者protected。因為private方法子類不可見,更無法繼承或者重寫。
5. final與介面
final不能修飾interface介面
6. final與構造方法
final不可以修飾構造方法