1. 程式人生 > >java程式設計思想讀書筆記----第十章 內部類

java程式設計思想讀書筆記----第十章 內部類

1、建立內部類

  將類的定義置於外部類的裡面,在外部類中可以直接new一個內部類的事例,通常外部類會有一個方法,該方法會返回一個指向內部類的引用。如果從外部類非靜態方法之外的位置其它類建立某個內部類的物件,必須具體的指明這個物件的型別:OutClassName.InnerClassName。

2、連結到外部類

  當生成一個內部類物件時,此物件與製造它的外圍物件之間就有了一種聯絡,所以它能訪問外圍類的所有成員,而不需要任何特殊條件。此外,內部類還擁有對外部類所有元素的訪問權
  這是如何做到的呢?當某個外圍類的物件建立了一個內部類物件時,此內部類物件會祕密捕獲一個指向外圍類物件的引用。然後,在你訪問外圍類的成員時,就是用那個引用來選擇外圍類的成員。內部類的物件只能在與其外部類物件相關聯

的情況下才能建立(在內部類是非static時)。

3、使用this和new

  如果需要生成對外部類的引用,可以使用在外部類後面緊跟小圓點和this。要想直接建立內部類的物件,必須使用外部類的物件來建立該內部類的物件,即在new表示式中提供外部類物件的引用。但是,如果建立的是巢狀類(靜態內部類),那麼就不需要對外部類物件的引用。

4、內部類與向上轉型

  當將內部類向上轉型為其基類,尤其是介面時,可以很方便地隱藏實現細節

5、在方法和作用域內的內部類

  之前所看到的是內部類的典型用途,然而,內部類語法覆蓋了大量其它的難以理解的技術。例如,可以在一個方法內或者在任意作用域內定義內部類。這麼做有兩個理由

  1. 如前所示,你實現了某型別的介面,於是建立並返回對其的引用。
  2. 你要解決一個複雜的問題,想建立一個類來輔助你的解決方案,但又不希望這個類是公共可用的。

  內部類可能會出現一下幾種情況

  • 一個定義在方法中的類
  • 一個定義在作用域內的類,此作用域在方法的內部
  • 一個實現了介面的匿名類
  • 一個匿名類,它擴充套件了有非預設構造器的類
  • 一個匿名類,它執行欄位初始化
  • 一個匿名類,它通過例項初始化實現構造(匿名類不可能有構造器)

  內部類被定義在作用域內,並不表示類的建立是有條件的,而是在該作用域外,類是不可用的。
  匿名內部類,“建立一個實現了某個介面的匿名類的物件”,通過new表示式返回的物件被自動向上轉型為介面型別。如果基類需要一個有引數的構造器,只需簡單的傳遞合適的引數給基類構造器即可:new Wrapping(x)。如果定義一個匿名內部類,並且希望它使用一個在其外部定義的物件,那麼編譯器會要求引數是final

的。在匿名內部類中不可能有命名構造器,但可以通過例項初始化達到為匿名內部類建立一個構造器的效果,不能過載例項初始化方法。
  匿名內部類與正規的繼承相比有所侷限,因為匿名內部類既可以擴充套件類,也可以實現介面,但是不能兩者皆備。而且如果是實現介面,也只能實現一個介面。

6、巢狀類

  如果不需要內部類物件和其外圍類物件有聯絡,可以將內部類宣告為static,這通常稱為巢狀類。普通的內部類物件隱式地儲存了一個引用,指向建立它的外圍類物件。然而,當內部類是static時就不是這樣了,巢狀類意味著:

  • 要建立巢狀類物件,並不需要外部類物件
  • 不能從巢狀類物件中訪問非靜態的外圍類物件

  巢狀類和普通內部類還有一個區別,非靜態內部類不能有靜態資料和靜態方法,也不能包含巢狀類。

  介面內部的類:正常情況下,不能在介面內部放置任何程式碼,但巢狀類可以作為介面的一部分。放在介面中的任何類都是自動public和static的。因為類是static的,所以只是將巢狀類置於介面的名稱空間內。甚至可以在內部類中實現其外圍介面。

7、為什麼需要內部類

  內部類實現介面和外圍類實現介面有什麼區別呢,答案是:後者不能總是享用到介面帶來的方便,有時需要用到介面的實現。所以內部類最吸引人的地方就是:每個內部類都能獨自的繼承一個(介面的)實現,所以無論外圍類是否已經繼承了某一個(介面的)實現,對於內部類都沒有影響。
  內部類使得多重繼承的解決方案變得完善。介面解決了部分問題,而內部類有效的實現了“多重繼承”。內部類允許繼承多個非介面型別(類或者抽象類)。
  使用內部類還可以獲得一些其他的特性:

  1. 內部類可以有多個例項,每個例項都有自己的資訊狀態,且與外圍類物件的資訊相互獨立。
  2. 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個介面,或繼承同一個類。
  3. 建立內部類物件的時刻並不依賴外圍類物件的建立。
  4. 內部類沒有令人疑惑的“is-a關係”,它是獨立 的個體。

  通過內部類完成閉包和回撥。
  內部類與控制框架,應用程式框架是用於解決某類特殊問題的一個類或者一組類。要運用某個程式框架,通常是繼承一個或多個類,並覆蓋某些方法。模板方法包含演算法的基本結構,並且會呼叫一個或多個可覆蓋的方法,以完成演算法的動作。設計模式總是將變化的事物和保持不變的事物相分離,在這個模式中,模板方法是不變的事物,而可覆蓋的方法是變化的事物。
  “將變化的事物和保持不變的事物分開”,變化的意思就是各種不同的Event物件擁有不同的行為,而你通過建立不同的Event子類來實現不同行為,這正是內部類要做的事,內部類允許:

  • 內部類的實現是通過單個類建立的,從而使得實現的細節被封裝了起來。內部類用來表示解決問題的不同的action();
  • 內部類可以很容易的訪問外圍類的成員

8、內部類的繼承

  因為內部類的物件必須連線到指向其外圍物件的引用,該引用必須被初始化,而在匯出類中不再存在可連結的預設物件,必須要使用特殊的語法來說明這個關係。

enclosingClassReference.super()

9、內部類可以被覆蓋嗎

  如果建立了一個內部類,然後繼承其外圍類並重新定義此內部類時會發生什麼?當繼承某個外部類時,該內部類並沒有發生什麼特別神奇的變化。這兩個內部類是完全獨立的兩個實體。當然,明確的繼承某個內部類也是可以的。

10、區域性內部類

  前面提到過,可以在程式碼塊裡建立內部類,最典型的就是在一個方法體裡面建立內部類。區域性內部類不能有類訪問許可權,因為它不是外圍類的一部分;但它可以訪問當前程式碼塊內的常量,以及此外圍類的所有成員。
  區域性內部類和匿名內部類有相同的行為和能力。既然區域性內部類在方法外是不可見的,為什麼仍然要使用區域性內部類而非匿名內部類呢?唯一的理由是,我們需要一個已命名的構造器,或者需要過載構造器,而匿名內部類只能用於例項初始化。

  內部類也必須生成一個.class檔案來包含class物件的資訊,這些類檔案有嚴格的命名規則:outerClass$innerClass.

ex20:
  在一個包含巢狀(靜態內部)類的類的方法中,可以只引用巢狀類名定義巢狀(靜態內部)類,但在該類外,必須指定外部類和巢狀類。

ex21
  注意,我們使用一個匿名內部類來實現介面I.這樣往往能更清晰地列出介面的所有方法,然後定義巢狀類。