fianl 關(guān)鍵字
在 java 中被 final 修飾的域通常指的是“這是無法改變的。不想做改變的可能出于兩種理由:設(shè)計(jì)或效率。
- final 關(guān)鍵字一般使用的三種情況:數(shù)據(jù)、方法、類
final 數(shù)據(jù)
許多編程語言都有某種方法來向編譯器告知這一塊數(shù)據(jù)時(shí)恒定不變的,比如:
一個(gè)永不改變的編譯時(shí)常量
一個(gè)在運(yùn)行時(shí)被初始化的值,而你并不喜歡它被改變
在java 中常量必須是基本類型,并且以關(guān)鍵字 final 修飾,對(duì)常量進(jìn)行定義的時(shí)候必須進(jìn)行賦值。
用于非基本類型對(duì)象引用時(shí),fianl 使其引用恒定不變。但是對(duì)象自身卻是可以修改的。
一個(gè)既是 static 又是 final 的域只占據(jù)一段不能改變的儲(chǔ)存空間,一般用大寫的表示并用下劃線分隔各個(gè)單詞。
class Value{
int i;
public Value(int i) {
this.i = i;
}
}
public class Test {
private static Random rand = new Random(47);
private String id;
public Test(String id) {
this.id = id;
}
// 編譯時(shí)常數(shù)
private final int valueOne = 11;
private static final int VALUE_TWO = 22;
public static final int VALUE_THREE = 33; //典型的 public 常量
//不是編譯時(shí)的常數(shù),而是運(yùn)行時(shí)的常數(shù)
private final int valueFour = rand.nextInt(20);
private static final int VALUE_FIVE = rand.nextInt(20);
private Value value1 = new Value(11);
private final Value value2 = new Value(22); //定義一個(gè)final對(duì)象
private static final Value VALUE_3 = new Value(33);
@Override
public String toString() {
return id + " : " +"valueFour = " +valueFour +", VALUE_FIVE = " + VALUE_FIVE;
}
public static void main(String[] args) {
Test test1 = new Test("test1");
//test1.valueOne++; error 其值不能改變
test1.value1 = new Value(9);
//test1.value2 = new Value(9); error final 使引用恒定不變
test1.value2.i++; // 對(duì)象不是常量,對(duì)象其自身卻是可以修改的
Test test2 = new Test("test2");
System.out.println(test1);
System.out.println(test2);
}
}
輸出為
test1 : valueFour = 15, VALUE_FIVE = 18
test2 : valueFour = 13, VALUE_FIVE = 18
由于 valueOne 和 VALUE_TWO 都是帶有編譯數(shù)值的 final 基本類型,所有他們二者可以用做編譯期的常量,無重大區(qū)別。
VALUE_THREE 是一種更加電信的對(duì)常量進(jìn)行定義的方式:定義為 public,則可以用于包之外;定義為 static,則可以
則強(qiáng)調(diào)只有一份;定義為 final,則說明是一個(gè)常量不能因?yàn)檎J(rèn)為某數(shù)據(jù)時(shí) fianl 的就認(rèn)為在編譯時(shí)可以知道其值。在上述代碼中的 valueFour 和 VALUE_FIVE 再運(yùn)行時(shí)使用隨機(jī)的值來初始化。
value1、value2、VALUE_3 展示了 final 引用的意義。在 main() 方法中可以看到,不能因?yàn)?value2 是 final 的,就認(rèn)為無法改變其值。
main() 方法最后展示了將 final 數(shù)值定義為靜態(tài)和和非靜態(tài)的區(qū)別。注意 VALUE_FIVE 兩次輸出的值是相同的,這是因?yàn)楸?static 修飾的,在類第一次裝載時(shí)已經(jīng)被初始化,而不是每次創(chuàng)建對(duì)象時(shí)都初始化。
空白final
Java 允許生成 空白final 即被聲明為 final 卻又未被給定初值的域(必須在構(gòu)造器中進(jìn)行初始化,不然編譯不通過),這樣我們?cè)谑褂蒙细`活,從而可以做到根據(jù)對(duì)象而有所不同。無論什么情況,編譯器都要確保 final 在使用前必須被初始化。
class A {
private int i;
public A(int i) {
this.i = i ;
}
}
class BlankFinal{
private final int i = 0; //已初始化的final
private final int j ; //空白final基本數(shù)據(jù)類型
private final A a; //空白final引用
public BlankFinal() {
this.j =1; //初始化空白final基本數(shù)據(jù)類型
this.a = new A(1); //初始化空白final引用
}
public BlankFinal(int j){
this.j = j;
this.a = new A(j);
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(5);
}
}
final參數(shù)
Java 允許在參數(shù)列表中以聲明的方式將參數(shù)指定為final,表示在方法中不能對(duì)參數(shù)引用進(jìn)行改變。
class B{
public void say() {
System.out.println("this class is B");
}
}
class FinalArguments{
void withFinal(final B b){
//b = new B(); 不能對(duì)final修飾的引用參數(shù)進(jìn)行改變,編譯出錯(cuò)
}
void withOutFinal(B b){
b = new B();// 這里就編譯就不會(huì)出錯(cuò)
b.say();
}
int f(final int i){
//return i++; 不能對(duì)final修飾的基本數(shù)據(jù)類型參數(shù)進(jìn)行改變,編譯出錯(cuò)
return i+1;
}
public static void main(String[] args) {
FinalArguments fa = new FinalArguments();
fa.withFinal(new B());
fa.withOutFinal(new B());
fa.f(5);
}
}
final 方法
final 修飾方法一般用在防止子類去修改該方法。
- 類中所有的 private 方法都隱式地指定為 final 的。由于子類無法取用 private 方法,所以也就無法對(duì)其進(jìn)行修改。可以為 private 方法聲明 final,但是毫無意義。
final 類
將某個(gè)類整體定義為 final 時(shí),表明當(dāng)前類不再被任何類繼承。出于安全性考慮,不希望該類有子類。
- 無法繼承 fianl 類,但是可以使用組合的方式使用 final 類。
- final 類禁止繼承,所以 fianl 類中所有的方法都被隱式指定為 final??梢栽?final 類的方法中添加 final,但是毫無意義。
final class C{
public void say(){
System.out.println("this is class c");
}
}
//class Cc extends C{} 編譯出錯(cuò),fianl類不能被繼承
class Cc{
public static void main(String[] args) {
C c = new C();
c.say();
}
}