1. 程式人生 > >內部類為final

內部類為final

但是 一份 打印 就會 構造方法 sta 技術 fin 參數

1) 從程序設計語言的理論上:局部內部類(即:定義在方法中的內部類),由於本身就是在方法內部(可出現在形式參數定義處或者方法體處),因而訪問方法中的局部變量(形式參數或局部變量)是天經地義的.是很自然的


2) 為什麽JAVA中要加上一條限制:只能訪問final型的局部變量?


3) JAVA語言的編譯程序的設計者當然全實現:局部內部類能訪問方法中的所有的局部變量(因為:從理論上這是很自然的要求),但是:編譯技術是無法實現的或代價極高.


4) 困難在何處?到底難在哪兒?
局部變量的生命周期與局部內部類的對象的生命周期的不一致性!


5) 設方法f被調用,從而在它的調用棧中生成了變量i,此時產生了一個局部內部類對象inner_object,它訪問了該局部變量i .當方法f()運行結束後,局部變量i就已死亡了,不存在了.但:局部內部類對象inner_object還可能 一直存在(只能沒有人再引用該對象時,它才會死亡),它不會隨著方法f()運行結束死亡.這時:出現了一個"荒唐"結果:局部內部類對象 inner_object要訪問一個已不存在的局部變量i!


6) 如何才能實現?當變量是final時,通過將final局部變量"復制"一份,復制品直接作為局部內部中的數據成員.這樣:當局部內部類訪問局部變量 時,其實真正訪問的是這個局部變量的"復制品"(即:這個復制品就代表了那個局部變量).因此:當運行棧中的真正的局部變量死亡時,局部內部類對象仍可以 訪問局部變量(其實訪問的是"復制品"),給人的感覺:好像是局部變量的"生命期"延長了.


那麽:核心的問題是:怎麽才能使得:訪問"復制品"與訪問真正的原始的局部變量,其語義效果是一樣的呢?
當變量是final時,若是基本數據類型,由於其值不變,因而:其復制品與原始的量是一樣.語義效果相同.(若:不是final,就無法保證:復制品與原始變量保持一致了,因為:在方法中改的是原始變量,而局部內部類中改的是復制品)

當 變量是final時,若是引用類型,由於其引用值不變(即:永遠指向同一個對象),因而:其復制品與原始的引用變量一樣,永遠指向同一個對象(由於是 final,從而保證:只能指向這個對象,再不能指向其它對象),達到:局部內部類中訪問的復制品與方法代碼中訪問的原始對象,永遠都是同一個即:語義效 果是一樣的.否則:當方法中改原始變量,而局部內部類中改復制品時,就無法保證:復制品與原始變量保持一致了(因此:它們原本就應該是同一個變量.)

一句話:這個規定是一種無可奈何.也說明:程序設計語言的設計是受到實現技術的限制的.這就是一例. 因為:我就看到不少人都持這種觀點:設計與想法是最重要的,實現的技術是無關緊要的,只要你作出設計與規定,都能實現.

現在我們來看,如果我要實現一個在一個方法中匿名調用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";

class OuterClass$1 extends ABSClass{

private final String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
int x = s.hashCode();

System.out.println(x);

}
};

ABSClass c = new OuterClass$1(s);
//其它代碼.
}


即外部類的變量被作為構造方法的參數傳給了內部類的私有成員.
假如沒有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";

class OuterClass$1 extends ABSClass{

private String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
s = "other";

}
};

ABSClass c = new OuterClass$1 (s);

}

內部類的s重新指向"other"並不影響test的參數或外部定義的那個s.同理如果外部的s重新賦值內部類的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從語法上約束了實際上兩個不同變量的一致性(表現為同一變量).

內部類為final