標籤:
在Java中,final關鍵字可以用來修飾類、方法和變數(包括成員變數和局部變數)。下面就從這三個方面來瞭解一下final關鍵字的基本用法。
1.修飾類
當用final修飾一個類時,表明這個類不能被繼承。也就是說,如果一個類你永遠不會讓他被繼承,就可以用final進行修飾。final類中的成員變數可以根據需要設為final,但是要注意final類中的所有成員方法都會被隱式地指定為final方法。
在使用final修飾類的時候,要注意謹慎選擇,除非這個類真的在以後不會用來繼承或者出於安全的考慮,盡量不要將類設計為final類。
2.修飾方法
使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現版本中,會將final方法轉為內嵌調用。但是如果方法過於龐大,可能看不到內嵌調用帶來的任何效能提升。在最近的Java版本中,不需要使用final方法進行這些最佳化了。
只有在想明確禁止 該方法在子類中被覆蓋的情況下才將方法設定為final的。
註:類的private方法會隱式地被指定為final方法。
3.修飾變數
修飾變數是final用得最多的地方.首先瞭解一下final變數的基本文法:
對於一個final變數,如果是基礎資料型別 (Elementary Data Type)的變數,則其數值一旦在初始化之後便不能更改;如果是參考型別的變數,則在對其初始化之後便不能再讓其指向另一個對象。
上面的一段代碼中,對變數i和obj的重新賦值都報錯了。
final 用於聲明屬性(常量),方法和類,分別表示屬性一旦被分配記憶體空間就必須初始化
final這個關鍵字的含義是“這是無法改變的”或者“終態的”;
那麼為什麼要阻止改變呢?
java語言的發明者可能由於兩個目的而阻止改變:
1).效率問題:
jdk中的某些類的某些方法,是不允許被使用者覆蓋的,設計者可能認為,所用方法已經是最好的方法,使用者私自覆蓋,或是由於疏忽而覆蓋,就會影響JVM或是系統的系能;
2). 設計所需:
眾所周知,有些情況必須使用final關鍵字,比如方法中的匿名內部類的參數傳遞
【修飾變數】:
final成員變數表示常量,只能被賦值一次,賦值後值不再改變。
【修飾方法】:
final方法不能被子類方法覆蓋,但可以被繼承。
【修飾類】:
final類不能被繼承,沒有子類,final類中所有方法都是final的。(如String類)
1.被final修飾而沒有被static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)
a.在它被聲明的時候賦值
b.在建構函式裡初始化
解釋:
當這個屬性被修飾為final,而非static的時候,它屬於類的執行個體對象的資源(執行個體常量),當類被載入進記憶體的時候這個屬性並沒有給其分配記憶體空間,而只是 定義了一個變數a,只有當類被執行個體化的時候這個屬性才被分配記憶體空間,而執行個體化的時候同時執行了建構函式,所以屬性被初始化了,也就符合了當它被分配記憶體 空間的時候就需要初始化,以後不再改變的條件.
2.被static修飾而沒有被final修飾的類的屬性變數只能在兩種情況下初始化:(可以不初始化)
a.在它被聲明的時候賦值
b.在靜態或非靜態快裡初始化
解釋:
當類的屬性被同時被修飾為static時候,他屬於類的資源(類變數),在類載入後,進行串連時候,分三步: 先驗證;然後準備,準備時,先分配記憶體,接著預設初始化;可以進行解析。最後,進行類初始化,類初始化前,必須保證它的父類已經初始化了,所以最先初始化的是超類,對於介面,不必初始其父介面。類初始化時,它把類變數初始化語句及靜態初始化語句放到類初始化方法中,所以,如果無此兩種語句,也就沒<clinit>類初始化方法,而建構函式是在當類 被執行個體化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.
3.同時被final和static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)
a.在它被定義的時候
b.在類的靜態塊裡初始化
c.特別對於初始化時候調用拋出異常的建構函式,初始時候注意,特別是在實現單例模式時(只能這麼初始化)
class A{ private final static A a;static{try{a=new A();}catch(Exception e){throws new RuntimeException(e); //必須有,不然不能完成常量的正確初始化}}private A() throws Exception{}}
解釋:
當類的屬性被同時被修飾為static和final的時候,他屬於類的資源(類常量),那麼就是類在被載入進記憶體的時候(也就是應用程 序啟動的時候)就要已經為此屬性分配了記憶體,所以此時屬性已經存在,它又被final修飾,所以必須在屬性定義了以後就給其初始化值.而建構函式是在當類 被執行個體化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.
java中的 final變數==常量
【final變數的變與不變】:final表示變數的值或引用不變
有人說final變數在賦值後就不可變,此變數可以是基礎資料型別 (Elementary Data Type)+String或者是對象
那麼這個不變到底指的是什麼呢?
這個不變指的是引用,是地址,而所引用的對象的內容仍然是可變的。註:如果為對象,注意此時類初始化條件
就是說,這個final變數永遠指向某個對象,是一個常量指標,而不是指向常量的指標。
【final關鍵字的具體應用】:
【final+變數】:
在實際應用中,這種形式是非常少見的。
比如logger是可以的,但是貌似並不是非常實用,或許使用者仍然希望通過setter來改變logger變數。
【static+final+變數】:
常量。經常使用。
【final+方法】:
JDK中常用,但是自己並未常用。
【final+類】:
helper類經常使用。
【final用於匿名內部類的參數傳遞】:
在多線程測試時,經常使用。
【final用於方法的參數】:
並不常用。
延伸:
在interface裡面的變數都是public static final 的。所以你可以這樣寫:
public static final int i=10;
或則
int i=10;(可以省略掉一部分)
注意在聲明的時候要給變數賦予初值
解釋:
首先你要弄清介面的含義.介面就是提供一種統一的’協議’,而介面中的屬性也屬於’協議’中的成員.它們是公用的,靜態,最終的常量.相當於全域常量.
抽象類別是不’完全’的類,相當於是介面和具體類的一個中介層.即滿足介面的抽象,也滿足具體的實現.
如果介面可以定義變數,但是介面中的方法又都是抽象的,在介面中無法通過行為來修改屬性。有的人會說了,沒有關係,可以通過實現介面的對象的行為來修改介面中的屬性。這當然沒有問題,但是考慮這樣的情況。如果介面A中有一個public存取權限的靜態變數a。按照java的語義,我們可以不通過實現介面的對象來訪問變數a,通過A.a = xxx;就可以改變介面中的變數a的值了。正如抽象類別中是可以這樣做的,那麼實現介面A的所有對象也都會自動擁有這一改變後的a的值了,也就是說一個地方改變了a,所有這些對象中a的值也都跟著變了。如果可以修改值:這和抽象類別有什麼區別呢,怎麼體現介面更高的抽象層級呢,怎麼體現介面提供的統一的協議呢,那還要介面這種抽象來做什麼呢?所以介面中不能出現變數,如果有變數,就和介面提供的統一的抽象這種思想是抵觸的。所以介面中的屬性必然是常量,只能讀不能改,這樣才能為實現介面的對象提供一個統一的屬性。
通俗的講,你認為是要變化的東西,就放在你自己的實現中,不能放在介面中去,介面只是對一類事物的屬性和行為更高層次的抽象。對修改關閉,對擴充(不同的實現implements)開放,介面是對開閉原則的一種體現。
Java基礎-關鍵字-final