walterwing |
|
|||
日歷
統計
導航常用鏈接留言簿(1)隨筆分類隨筆檔案搜索最新評論
閱讀排行榜評論排行榜 |
內部類的概念與使用實在是有些繁雜,因為他本身涉及到java內部一些很基礎的知識,包括修飾符、作用域等等,在網上很難搜索到一篇全面、準確的關于內部類的總結,所以在這里拋磚引玉一下,希望能對自己、對大家都有所幫助。 一. 概念介紹 1. 什么是內部類 簡單的說,內部(inner)類指那些類定義代碼被置于其它類定義中的類;而對于一般的、類定義代碼不嵌套在其它類定義中的類,稱為頂層(top-level)類。對于一個內部類,包含其定義代碼的類稱為它的外部(outer)類。 2. 內部類都有哪幾種 一般來講,都是把內部類分為四種:靜態成員類(static member classes)、非靜態成員類(nonstatic member classes)、局部類(local classes)、匿名類(anonymous classes) 2.1. 靜態成員類:所謂靜態成員類,就是用static來修飾的,定義于外部類頂層的內部類 例: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 2.2. 非靜態成員類:顧名思義,就是沒有用static來修飾的,定義于外部類頂層的內部類 例: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 2.3. 局部類:定義于外部類的代碼塊或方法內部的內部類。局部類還可以進一步細分為兩種: 2.3.1. 局部靜態成員類:定義于外部類的靜態方法或靜態初始化代碼段中的局部類 2.3.2. 局部成員類:定義于外部類的實例方法或實例初始化代碼段中的局部類 例: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 也就是說,局部類也有“靜態”和“非靜態”之分,取決于他所處的方法或代碼段。 2.4. 匿名類:表面上來看,匿名類就是沒有類名的局部類。但其實二者還是有區別的。局部類,是創建一個新的類;而匿名類,是針對已經定義好的接口或類,將其實現(接口)或擴展(類)并實例化。 例: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 注:繼承自某個類的匿名類,和實現某個接口的匿名類,在表現形式上略有差別: 2.4.1 繼承自某個類的匿名類: new class-name ( [ argument-list ] ) { class-body } 2.4.2. 實現某接口的匿名類: new interface-name () { class-body } 二. 語法規則 1. 靜態成員類: 1.1. 可以做的: 像靜態方法或靜態字段一樣,可以用public, private, protected來修飾,不加則為默認(package) 可以訪問外部類的任一靜態字段或靜態方法 可以用OuterClass.InnerClass的方式來引用 1.2. 不可以做的: 像外部類的靜態方法一樣,不可以訪問外部類的非靜態方法或非靜態字段 2. 非靜態成員類: 2.1. 可以做的: 可以用public, private, protected來修飾,不加則為默認(package) 可以訪問外部類的任一字段和函數(不管是否static),這是因為非靜態成員類的實例包含外部類的引用。 當非靜態內部類中所定義的某個成員變量和外部類中的變量重名時,外部類的變量將被屏蔽,此時可以用OuterClass.this.來得到被屏蔽的外部類變量 有兩種方式來創建非靜態成員類的實例。第一種比較常用,就是在外部類的方法內部直接創建: InnerClass inner = new InnerClass(); 另外一種則是通過表達式enclosingInstance.new MemberClass(args)來創建。具體見2.2. 2.2. 不可以做的: 不能在非靜態成員類內部定義static字段、方法或內部類。這是因為完全可以將這些東西移到外部類中去;但是可以定義static final常量 在第三方類里面(指非外部類的某個其他類)不可以用OuterClass.InnerClass inner = new OuterClass.InnerClass(); 的方法來創建非靜態成員類的實例。這是因為非靜態成員類實例是依賴于外部類實例而存在的(包含外部類的引用),但如果我們有了一個外部類的實例outerClass,則可以用下面的方法來創建: OuterClass.InnerClass innerClass = outerClass.new InnerClass(); 3. 局部類: 3.1. 可以做的: 能且只能訪問所屬代碼段中聲明為final的變量。這是因為局部變量在其所屬的代碼段(譬如某個函數)執行完畢后就會被回收,而一個局部類的實例卻可以在其類定義所屬代碼段執行完畢后依然存在,如果它可操控非final的局部變量,用戶就可以通過該實例修改已不存在的局部變量,無意義。 能且只能被final, abstract修飾 3.2. 不可以做的: 不能被public, private, protected修飾,也不能被static修飾。這是因為局部類只在定義它的代碼段中可見,不能在它所屬代碼段之外的代碼中使用 同理,局部類內部不能定義static成員 不能以局部類形式定義一個接口。因為局部類只在其所屬代碼段中可見,定義這樣的接口無意義 4.匿名類 當前僅當匿名類處于非靜態代碼段時,他會有外部類的引用(即可以訪問外部類任意資源)。局部類也是如此,不同的是匿名類在聲明處的同時被初始化。 匿名類還有一個限制,就是不能同時實現多個接口,也不能同時實現一個接口并擴展一個基類。這是因為匿名類的聲明和實例化是同時發生的,它不像通常的類的聲明那樣,可以通過implement關鍵字指定多個接口,或者通過exteds關鍵字來指定基類。對一個匿名類而言,他只能通過指定接口或基類的名字,來告訴編譯器說:現在我要實現這個接口(or擴展這個基類),因此一個匿名類能且只能實現一個接口或者擴展一個基類 三. 內部類的使用及其意義 1. 靜態成員類 靜態成員類的通常用法是作為一個public helper class,與他的OuterClass結合在一起使用,為OuterClass服務。 比如一個Calculator類,他有各種操作,這些操作就可以存儲在他的靜態內部類Operation里,客戶端就可以通過Calculator.Operation.PLUS或者Calculator.Operation.MINUS等來指定各種操作 那一個private的靜態成員類可以用來干什么呢?可以用來代表外部類所需要的某個組件。 比如,對于Map接口,他內部就有一個Entry類,一個Entry對象對應著一個鍵-值對,該對象上的方法(getKey, getValue, setValue)也不需要訪問外部類的資源,將Entry定義為private的靜態成員類類就是最合適的。 2. 非靜態成員類 一種通常的用法是把非靜態成員類作為一個Adapter,使外部類的實例看起來提供了某個不相關的類的實例的功能。 比如,collection接口的各種實現類,一般就是用非靜態成員類來實現他們的iterator: ![]() ![]() ![]() ![]() ![]() ![]() ![]() 關于靜態成員類和非靜態成員類,有一點需要注意的是:如果你的內部類不需要訪問外部類的成員變量和函數(尤其是指非static類型的),那么盡量用靜態成員類。為什么?因為每個非靜態成員類的實例都將保存他的外部類的引用,這不僅造成時空上的浪費,而且可能導致當外部類實際上已經可以被垃圾回收器回收的時候,卻因為某個非靜態成員類實例還保留著該外部類的引用而不能回收該外部類。 3. 匿名類 匿名類用的地方應該是這四種內部類中最多的,至少我是這樣。我經常用匿名類的地方就是在事件響應的代碼中。 Effective Java中總結了三種匿名類的用途: 3.1. To create function objects on the fly 3.2. To create process objects, such as Runnable, Thread, TimerTask instances 3.3. To use within static factory methods 總結, 1.當內部類需要在方法外部仍然可見時,使用成員類(靜態or非靜態);當內部類比較長,放在方法內部會影響程序可讀性時,使用成員類(靜態or非靜態); 2. 如果需要在內部類內部定義靜態成員,只能使用靜態成員類(其他三個都不支持);如果成員類的每個實例都需要外部類的引用,定義為非靜態的,否則,就要定義成靜態的; 3. 假設我們需要在方法內部定義一個局部類或者匿名類,如果我們只需要在這一個位置使用內部類實例,并且已經有預先定義好的基類或接口,那就使用匿名類;否則,使用局部類 |
![]() |
|
Copyright © This is Wing | Powered by: 博客園 模板提供:滬江博客 |