匿名內部類 ( 明白了匿名類就理解了函數回調,(此處與線程無關))有位老兄說:(匿名一是為了簡化代碼,而是告訴GC我這個對象只用一次,用完給我回收了)
關於JAVA內部類:一個內部類的定義是定義在另一個類內部的類。
存在它的原因是:
1.一個內部類的對象能夠訪問建立它的對象的實現,包括私人資料。即內部類執行個體對包含它的哪個類的執行個體來說,是特權的。
2.對於同一個包中的其他類來說,內部類能夠隱藏起來,換句話說,內部類不管方法的可見度如何,那怕是public,除了包容類,其他類都無法使用它。
3.匿名內部類可以很方便的定義回調。
4.使用內部類可以非常方便的編寫事件驅動程式。
其實它真正的目的僅僅為了定義回調--進一步就是事件驅動。
介面和回調:編程一個常用的模式是回調模式,在這種模式中你可以指定當一個特定時間發生時回調對象上的方法。
java的匿名內部類的文法規則看上去有些古怪,不過如同匿名數組一樣,當你只需要建立一個類的對象而且用不上它的名字時,使用內部類可以使代碼看上去簡潔清楚。它的文法規則是這樣的:
new interfacename(){......}; 或 new superclassname(){......};
下面接著前面繼續舉例子:
public class Goods3 {
public Contents cont(){
return new Contents(){
private int i = 11;
public int value() {
return i;
}
};
}
}
這裡方法cont()使用匿名內部類直接返回了一個實現了介面Contents的類的對象,看上去的確十分簡潔。
在java的事件處理的匿名適配器中,匿名內部類被大量的使用。例如在想關閉視窗時加上這樣一句代碼:
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
有一點需要注意的是,匿名內部類由於沒有名字,所以它沒有建構函式(但是如果這個匿名內部類繼承了一個只含有帶參數建構函式的父類,建立它的時候必須帶上這些參數,並在實現的過程中使用super關鍵字調用相應的內容)。如果你想要初始化它的成員變數,有下面幾種方法:
如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明為final。
將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有建構函式了。
在這個匿名內部類中使用初始化代碼塊。
為什麼需要內部類?
java內部類有什麼好處?為什麼需要內部類?
首先舉一個簡單的例子,如果你想實現一個介面,但是這個介面中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎麼辦?這時候,你可以建一個內部類實現這個介面。由於內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直接實現這個介面的功能。
不過你可能要質疑,更改一下方法的不就行了嗎?
的確,以此作為設計內部類的理由,實在沒有說服力。
真正的原因是這樣的,java中的內部類和介面加在一起,可以的解決常被C++程式員抱怨java中存在的一個問題——沒有多繼承。實際上,C++的多繼承設計起來很複雜,而java通過內部類加上介面,可以很好的實現多繼承的效果。
————————————————————————————————————
Java 1.1通過對Java語言規範進行修改,顯著簡化了一些實用結構的實現。在那些修改中,最令人信服的就是內部類和匿名類。如運用得當,它們可使程式更易理解和維護。下面來看看這些特性具體是如何工作的,如何正確使用它們,以及如何避免一些常見的錯誤。
內部類
簡單地說,“內部類”是在另一個類的內部聲明的類。從Java 1.1開始,你可在一個類中聲明另一個類,這與聲明欄位和方法非常相似。封裝了內部類聲明的類就稱為“外部類”。
實際上,Java語言規範還允許你做更多的事情,包括:
在另一個類或者一個介面中聲明一個類。
在另一個介面或者一個類中聲明一個介面。
在一個方法中聲明一個類。
類和介面聲明可嵌套任意深度。
清單A是類和介面的一些空白聲明,它示範了這些可能性。
使用一個import語句,你可像使用其他任何標準類那樣省略package名稱。此外,在外部類中,可利用簡單名稱來引用所有內部類和介面(參見清單A中的new語句)。注意從Method1中引用Inner2仍需指定Interface1,因為Inner2在一個不同的層級上。
表A總結了清單A中聲明的每個內部類和介面的完全限定名稱。用了import語句之後,就可採用較短的形式。當然,在外部類中,你還可省略外部類的名稱。
名稱
類/介面
Inner1 mypackage.Inner1
Interface1 mypackage.Interface1
Inner2 mypackage.Interface1.Inner2
Interface2 mypackage.Interface1.Interface2
Inner3 Inner3對於Method1來說是local的,所以它不可在方法外部存取
引用內部類
內部類最自然的一種應用就是聲明只在另一個類的內部使用的類,或者聲明與另一個類密切相關的類。如清單B所示,它是一個鏈表的簡單實現。由於Node類通常只在LinkedList的範圍內使用,所以最好將Node聲明為LinkedList的一個內部類。
適用於類成員的存取控制修改符也適用於內部類;也就是說,內部類可以具有package、protected、private和public存取權限,它們的語義和正常的語義沒有什麼不同。由於Node要在LinkedList的外部使用,所以把它聲明為public。
然而,修飾符static具有不同的含義。應用於內部類時,它聲明的類具有與其他類相同的語義,也就是可進行執行個體化,並像一個標準類那樣使用。惟一的區別就是它擁有對外部類的所有靜態成員的完整存取。清單C展示了一個簡單的程式,它建立一個鏈表,並將它列印到標準輸出裝置。
非靜態內部類
如果內部類沒有指定static修飾符,就擁有對外部類的所有成員的完整存取,包括執行個體欄位和方法。為實現這一行為,非靜態內部類儲存著對外部類的執行個體的一個隱式引用。
所以,對一個非靜態內部類進行執行個體化需要採用不同文法的new語句:
.new
這種形式的new語句要求外部類的一個執行個體,使內部類能在那個執行個體的上下文中建立。注意清單A聲明了幾個非靜態內部類,並用標準的new語句在Method1中執行個體化它們。
之所以能那樣做,是因為Method1是外部類的一個執行個體方法,所以new語句會在外部類的一個執行個體的上下文中隱式地執行。只有在外部類的外部或者在其他對象的上下文中執行個體化一個非靜態內部類時,才需要使用修改過的文法。
但是,非靜態內部類具有一些限制。尤其是,它們不能聲明靜態初始化列表和靜態成員,除非是在常量欄位中。此外,方法內部聲明的內部類不能存取方法的局部變數和參數,除非它們被初始化成final。
匿名類
匿名類是不能有名稱的類,所以沒辦法引用它們。必須在建立時,作為new語句的一部分來聲明它們。
這就要採用另一種形式的new語句,如下所示:
new <類或介面> <類的主體>
這種形式的new語句聲明一個新的匿名類,它對一個給定的類進行擴充,或者實現一個給定的介面。它還建立那個類的一個新執行個體,並把它作為語句的結果而返回。要擴充的類和要實現的介面是new語句的運算元,後跟匿名類的主體。
如果匿名類對另一個類進行擴充,它的主體可以訪問類的成員、覆蓋它的方法等等,這和其他任何標準的類都是一樣的。如果匿名類實現了一個介面,它的主體必須實現介面的方法。
注意匿名類的聲明是在編譯時間進行的,執行個體化在運行時進行。這意味著for迴圈中的一個new語句會建立相同匿名類的幾個執行個體,而不是建立幾個不同匿名類的一個執行個體。
從技術上說,匿名類可被視為非靜態內部類,所以它們具有和方法內部聲明的非靜態內部類一樣的許可權和限制。
如果要執行的任務需要一個對象,但卻不值得建立全新的對象(原因可能是所需的類過於簡單,或者是由於它只在一個方法內部使用),匿名類就顯得非常有用。匿名類尤其適合在Swing應用程式中快速建立事件處理常式。
清單D就是一個非常簡單的Swing應用程式,它展示了與匿名類有關的幾個概念。這個例子建立了兩個匿名類。第一個對java.awt.event.WindowAdapter進行擴充,並在應用程式視窗關閉時調用應用程式的onClose方法。
即使onClose聲明為private,匿名類也能調用它,因為匿名類本質上是應用程式類的一個內部類。第二個匿名類實現了java.awt.ActionListener介面,它在一個按鈕被按下後關閉應用程式視窗。注意匿名類可以訪問本地變數frame。這是由於匿名類在與frame相同的方法內部聲明。然而,frame要被聲明為final,否則會產生編譯錯誤。
更最佳化的代碼
內部和匿名類是Java 1.1為我們提供的兩個出色的工具。它們提供了更好的封裝,結果就是使代碼更容易理解和維護,使相關的類都能存在於同一個原始碼檔案中(這要歸功於內部類),並能避免一個程式產生大量非常小的類(這要歸功於匿名類)。