Java 高级类(下) —— 内部类和匿名类
Java內部類(Inner Class),類似的概念在C++里也有,那就是嵌套類(Nested Class),乍看上去內部類似乎有些多余,它的用處對于初學者來說可能并不是那么顯著,但是隨著對它的深入了解,你會發現Java的設計者在內部類身上的確是用心良苦。學會使用內部類,是掌握Java高級編程的一部分,它可以讓你更優雅地設計你的程序結構。下面從以下幾個方面來介紹:
?
第一次見面
[java]?view plaincopy?? ??在這個例子里類Content和GDestination被定義在了類Goods內部,并且分別有著protected和private修飾符來控制訪問級別。Content代表著Goods的內容,而GDestination代表著Goods的目的地。它們分別實現了兩個接口Content和Destination。在后面的main方法里,直接用Contents c和Destination d進行操作,你甚至連這兩個內部類的名字都沒有看見!這樣,內部類的第一個好處就體現出來了——隱藏你不想讓別人知道的操作,也即封裝性。
???? 同時,我們也發現了在外部類作用范圍之外得到內部類對象的第一個方法,那就是利用其外部類的方法創建并返回。上例中的cont()和dest()方法就是這么做的。那么還有沒有別的方法呢?當然有,其語法格式如下
[java]?view plaincopy???? ?注意在創建非靜態內部類對象時,一定要先創建起相應的外部類對象。至于原因,也就引出了我們下一個話題——非靜態內部類對象有著指向其外部類對象的引用
?
一、常規內部類
常規內部類沒有用static修飾且定義在在外部類類體中。
1.常規內部類中的方法可以直接使用外部類的實例變量和實例方法。
2.在常規內部類中可以直接用內部類創建對象
對剛才的例子稍作修改:
[java]?view plaincopy????? 修改的部分用紅色顯示了。在這里我們給Goods類增加了一個private成員變量valueRate,意義是貨物的價值系數,在內部類Content的方法value()計算價值時把它乘上。我們發現,value()可以訪問valueRate,這也是內部類的第二個好處 ——一個內部類對象可以訪問創建它的外部類對象的內容,甚至包括私有變量!這是一個非常有用的特性,為我們在設計時提供了更多的思路和捷徑。要想實現這個功能,內部類對象就必須有指向外部類對象的引用。 Java編譯器在創建內部類對象時,隱式的把其外部類對象的引用也傳了進去并一直保存著。這樣就使得內部類對象始終可以訪問其外部類對象,同時這也是為什么在外部類作用范圍之外向要創建內部類對象必須先創建其外部類對象的原因。
??? 有人會問,如果內部類里的一個成員變量與外部類的一個成員變量同名,也即外部類的同名成員變量被屏蔽了,怎么辦?沒事,Java里用如下格式表達外部類的引用:
[java]?view plaincopy有了它,我們就不怕這種屏蔽的情況了。
?
可以再看一個例子:
[java]?view plaincopy執行結果如下:
[java]?view plaincopy?
二、靜態內部類
???? 和普通的類一樣,內部類也可以有靜態的。不過和非靜態內部類相比,區別就在于靜態內部類沒有了指向外部的引用。這實際上和C++中的嵌套類很相像了,Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用這一點上,當然從設計的角度以及以它一些細節來講還有區別。
???? 除此之外,在任何非靜態內部類中,都不能有靜態數據,靜態方法或者又一個靜態內部類(內部類的嵌套可以不止一層)。不過靜態內部類中卻可以擁有這一切。這也算是兩者的第二個區別吧。
下面看一個例子:
[java]?view plaincopy執行結果如下:
[java]?view plaincopy可以看到 MyOuter2.MyInner si = new MyOuter2.MyInner(); 中并不需要寫成
[java]?view plaincopy?
三、局部內部類
???????? 在方法體或語句塊(包括方法、構造方法、局部塊或靜態初始化塊)內部定義的類稱為局部內部類。
??????? 局部內部類不能加任何訪問修飾符,因為它只對局部塊有效。
1.局部內部類只在方法體中有效,就想定義的局部變量一樣,在定義的方法體外不能創建局部內部類的對象
2.在方法內部定義類時,應注意以下問題:
1)、方法定義局部內部類同方法定義局部變量一樣,不能使用private、protected、public等訪問修飾說明符修飾,也不能使用static修飾,但可以使用final和abstract修飾;
2)、方法中的內部類可以訪問外部類成員。對于方法的參數和局部變量,必須有final修飾才可以訪問;
3)、static方法中定義的內部類可以訪問外部類定義的static成員;
?
下面看一個實例:
[java]?view plaincopy執行結果如下:
[java]?view plaincopy?
四、匿名內部類?
??????定義類的最終目的是創建一個類的實例,但是如果某個類的實例只是用一次,則可以將類的定義與類的創建,放到與一起完成,或者說在定義類的同時就創建一個類。以這種方法定義的沒有名字的類成為匿名內部類。
聲明和構造匿名內部類的一般格式如下:
[java]?view plaincopy1.匿名內部類可以繼承一個類或實現一個接口,這里的ClassOrInterfaceName是匿名內部類所繼承的類名或實現的接口名。但匿名內部類不能同時實現一個接口和繼承一個類,也不能實現多個接口。如果實現了一個接口,該類是Object類的直接子類,匿名類繼承一個類或實現一個接口,不需要extends和implements關鍵字。
2.由于匿名內部類沒有名稱,所以類體中不能定義構造方法,由于不知道類名也不能使用關鍵字來創建該類的實例。實際上匿名內部類的定義、構造、和第一次使用都發生在同樣一個地方。此外,上式是一個表達式,返回的是一個對象的引用,所以可以直接使用或將其復制給一個對象變量。例:
[java]?view plaincopy?
下面接著舉例子:
[java]?view plaincopy執行結果如下:
[java]?view plaincopy?
在java的事件處理的匿名適配器中,匿名內部類被大量的使用。例如在想關閉窗口時加上這樣一句代碼:
[java]?view plaincopy?
有一點需要注意的是,匿名內部類由于沒有名字,所以它沒有構造函數(但是如果這個匿名內部類繼承了一個只含有帶參數構造函數的父類,創建它的時候必須帶上這些參數,并在實現的過程中使用super關鍵字調用相應的內容)。如果你想要初始化它的成員變量,有下面幾種方法:
1)如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明為final。?
2)將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有構造函數了。?
3)在這個匿名內部類中使用初始化代碼塊。
?
為什么需要內部類?
java內部類有什么好處?為什么需要內部類?
??? 首先舉一個簡單的例子,如果你想實現一個接口,但是這個接口中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎么辦?這時候,你可以建一個內部類實現這個接口。由于內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直接實現這個接口的功能。
??? 不過你可能要質疑,更改一下方法的不就行了嗎?
??? 的確,以此作為設計內部類的理由,實在沒有說服力。
??? 真正的原因是這樣的,java中的內部類和接口加在一起,可以的解決常被C++程序員抱怨java中存在的一個問題——沒有多繼承。實際上,C++的多繼承設計起來很復雜,而java通過內部類加上接口,可以很好的實現多繼承的效果。
?
?java內部類總結
1、?在方法間定義的非靜態內部類:
1)外圍類和內部類可互相訪問自己的私有成員。
2)內部類中不能定義靜態成員變量。
????? 在外部類作用范圍之外向要創建內部類對象必須先創建其外部類對象
2、?在方法間定義的靜態內部類:
1)只能訪問外部類的靜態成員。
?????? 靜態內部類沒有了指向外部的引用
3、在方法中定義的局部內部類:
1)該內部類沒有任何的訪問控制權限
2)外圍類看不見方法中的局部內部類的,但是局部內部類可以訪問外圍類的任何成員。
3)方法體中可以訪問局部內部類,但是訪問語句必須在定義局部內部類之后。
4)局部內部類只能訪問方法體中的常量,即用final修飾的成員。
4、在方法中定義的匿名內部類:
1)沒有構造器,取而代之的是將構造器參數傳遞給超類構造器
??????當你只需要創建一個類的對象而且用不上它的名字時,使用匿名內部類可以使代碼看上去簡潔清楚。
總結
以上是生活随笔為你收集整理的Java 高级类(下) —— 内部类和匿名类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 摘葡萄,摘草莓也用上了收割机,世界观又一
- 下一篇: Java 三大特性 —— 多态