標籤:java final
final關鍵字既可以用來修飾基本變數,引用也可以用來修飾方法和類。在修飾不同的對象時有不同的含義,很容易搞混淆,在此做一個小結。瞭解一下繼承情況下變數的初始化和類的載入機制,也能讓我們對程式運行時發生的一切有一個全域性的把握。
一。final關鍵字
1.1 final關鍵字修飾變數
final關鍵字類似於C++中的const關鍵字,當它修飾基本變數時表明該變數一旦被初始化為某一值就不能再被改變。當final用於修飾一個引用時情況有點不同,它只能表示該引用一旦初始化時指向某個對象,那麼這個引用的指向就不能再改變了,但是其指向的對象的值卻還是可以改變的,下面的程式示範了這一點。
package lkl;public class Value { public int i; Value(int i){ this.i=i; }}package lkl;import java.util.Random;public class FinalTest { public static Random rand = new Random(45); public final int valueOne=9; ///final修飾普通變數 public final int valueTwo = rand.nextInt(34);///final修飾的普通變數,不一定要用常量賦值 public final Value val = new Value(1); ///final修飾引用 public static final int INT_1=10; ///final和static連用表示一個編譯時間就可以確定的常量 public static void main(String[] args){ FinalTest ft=new FinalTest(); //ft.valueOne++; ///試圖修改final型基本變數的值,Error System.out.println(ft.valueTwo); ft.val.i++; ///修改final引用指向對象的值是可以的 //ft.val=new Value(2);//試圖修改final修飾引用的指向,Error }}
上面的代碼中還出現了static和final共同修飾一個變數的情況,這時這個變數只佔據一段不能被修改的儲存空間,稱為編譯時間常量.
在Java中final類型的變數也可以不在聲明時就初始化,可以將初始化的過程放到構造器中(必須所有的構造器中都有初始化的語句),這樣也可以保證變數在使用前肯定會被初始化.這樣的好處在於可以根據不同的情況對final型變數賦不同的值.這種final變數被稱為”空白final”.
下面的代碼示範了這種情況:
package lkl;import java.util.Random;public class FinalTest { public final int i; public final int j; public FinalTest(){ i=0; j=0; } public FinalTest(int i,int j){ this.i=i; this.j=j; } public static void main(String[] args){ FinalTest ft=new FinalTest(); FinalTest ft1 = new FinalTest(1,2); System.out.println("ft: i ,j "+ft.i+" "+ft.j); System.out.println("ft1:i,j "+ft1.i+" "+ft1.j); }}
最後還有一種final用於修飾形參變數的用法,這時意味著你不能在方法中更改參數引用指向的對象.此時的形參只可讀而不可寫,如下面的代碼所示:
package lkl;import java.util.Random;public class FinalTest { public Value with(final Value val){ val= new Value(2); ///試圖改變val的指向,Error return val; } public Value w(final Value val){ return val; } public int go(final int i){ i++;///試圖改變i的值,Error return i; } public int f(final int i) { return i+1; } public static void main(String[] args){ }}
1.2 final修飾方法和類
final用於修飾方法,則表明不能被子類重寫(覆蓋).這樣可以保證在繼承中方法行為不變.父類中所有的private方法都是隱式指定為final的.對於private方法,如果我們在子類中定義一個與其完全一樣的方法,也不能叫做重寫,只是重寫定義了一個新的方法.但對於final修飾的方法,我們是不可以在子類中進行重寫.所以重寫只是針對在子類中可見的父類介面.
package lkl;public class Base { private void f(){ System.out.println("Base.private()"); } public final void g(){ System.out.println("Base.final()"); }}package lkl;public class OverringBase1 extends Base{ private void f(){ System.out.println("OverringBase1.f()"); } public final void g(){ ///Error System.out.println("OverringBase1.g()"); }}
對於final修飾類來講就要簡單的多,我們只需要知道如果一個類被final修飾則該類不能被繼承就行了,當然此時該類中的所有的域都預設是final的.
二.初始化和類的載入.
Java中的每個類都會有它自己的編譯代碼,然後如果我們想要使用這個類時,就需要載入這個編譯代碼檔案.當一個類的第一個對象被聲明時,或者是類的static 變數被調用時,類就會被初始化,每個類只會被初始化一次.
如果一個類有基類,那麼在載入這個類的過程中就會先載入基類,如果這個基類還有父類,那麼也會先載入他的父類,如此.
在此過程中從最初的基類開始,所有類的static變數會被初始化.
對於其它變數的初始化:建立對象時,首先對象中的所有基本類型都會被設為預設值,對象的引用被設為null,然後如果有初始化塊,則初始化塊會被執行,最後再調用構造器進行初始化,變數會按其次序被初始化.下面的代碼示範了初始化的流程:
package lkl;public class Base { public int i=9; ///先指定一個預設值 public int j; public int k; public int d; public Base(int x){ j=i; i=x; k=i; } { i=10; ///初始化塊 d=i; } public static void main(String[] args){ Base be =new Base(2); ///輸出的值說明初始化塊先被執行,然後再是構造器 System.out.println(be.j+" "+" "+be.k+" "+be.d); }}
Thinking in Java ---final關鍵字總結+初始化和類的載入