final變數的定義本身並不複雜,就是變數一經初始化就不能再指向其它對象.在c++中它是一個const指標,而
不是指向const變數的指標,const指標的意思是說它只能一直指向初始化時的那個地址.但那個地址中對象本身
是可以修改的.而指向const變數的指標是說所指對象本身是不能修改的.
如:final StringBuffer sb = new StringBuffer("Axman");
sb = new StringBuffer("Sager");//錯誤,sb不能再指向其它對象.
sb.append(" was changed!"); //sb指向的地象本身可以修改.
先說final變數初始化:
很多文章都這麼說:其初始化可以在兩個地方,一是其定義處,二是在建構函式中,兩者只能選其一。
胡說八道!
final變數可以在任何可以被始化的地方被始化,但只能被初始化一次.一旦被初始化後就不能再次賦
值(重新指向其它對象),作為成員變數一定要顯式初始化,而作為臨時變數則可以只定義不初始化(當然也不能引用)
即使是作為一個類中的成員變數,也還可以在初始化塊中初始化,所以"其初始化可以在兩個地方,一是其定義處,
二是在建構函式中,兩者只能選其一"是錯誤的.
作為成員變數時,final欄位可以設計不變類,是不變類的一個必要條件但不是一個充要條件.至少可以保證欄位不
會以setXXX()這樣的方式來改變.但無法保證欄位本身不被修改(除非欄位本身也是不變類);
對於方法參數的final變數:
對於方法參數的變數定義為final,90%以上的文章都說"當你在方法中不需要改變作為參數的物件變數時,明確使
用final進行聲明,會防止你無意的修改而影響到調用方法外的變數。"
胡說八道!
我不知道這個修改是說重新賦值還是修改對象本身,但無論是哪種情況,上面的說法都是錯誤的.
如果是說重新賦值,那麼:
public static void test(int[] x){
x = new int[]{1,2,3};
}
int[] out = new int[]{4,5,6};
test(out);
System.out.println(out[0]);
System.out.println(out[1]);
System.out.println(out[2]);
調用test(out);無論如何也不會影響到外面變數out.你加不加final根本沒有意義.final只會強迫方法內
多聲明一個變數名而已,即把x = new int[]{1,2,3};改成int y = new int[]{1,2,3}; 其它沒有任何實際意義.
如果說是修改對象本身:
public static void test(final int[] x){
x[0] = 100;
}
int[] out = new int[]{4,5,6};
test(out);
System.out.println(out[0]);
難道你用final修飾就不可以修改了?所以說對於方法參數中final是為了不影響調用方法外的變數那是胡說八道的.
那我們到底為什麼要對參數加上final?其實對方法參數加final和方法內變數加上final的作用是相同的,即為了將它們
傳給內部類時保證調用的一致性:
abstract class ABSClass{
public abstract void m();
}
現在我們來看,如果我要實現一個在一個方法中匿名調用ABSClass.應該:
public static void test(final String s){
//或final String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
//其它代碼.
}
實際上這段代碼被編譯成:
public static void test(final String s){
//或final String s = "axman";
ABSClass c = new ABSClass(s){
private final String s;
public ABSClass(String s){
this.s = s;
}
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
//其它代碼.
}
即外部類的變數被作為構造方法的參數傳給了內部類的私人成員.
假如沒有final,那麼:
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
s = "other";
}
};
System.out.println(s);
}
就會編譯成:
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(s){
private final String s;
public ABSClass(String s){
this.s = s;
}
public void m(){
s = "other";
}
};
System.out.println(s);
}
內部類的s重新指向"other"並不影響test的參數或外部定義的那個s.而你看到的
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
s = "other";
}
};
System.out.println(s);
}
在文法上是一個s,在內部類中被改變了,但結果列印的出來的你認為是同一的s卻還是原來的"axman",
你能接收這樣的結果嗎?
所以final從文法上約束了兩個不同變數的一致性