Java內部類學習總結

內部類即在一個類的內部編寫額外的class。編寫完以後會生成OutterClassName$InnerClassName(匿名內部類用數字遞增)的文件。外部類只能使用public,final,默認修飾符。但是內部類還可以使用private,static,protected等等修飾符。

內部類也是一種編程方式,爲什麼要使用內部類呢?
*把相互有關聯的對象組織到一起,而且外部用不到的對象又可以完全對外隱藏細節。看看java的集合對象源碼吧,HashMap的內部結構,你一下就會明白這樣寫的好處。
*每個內部類獨立的繼承或實現,不依賴外部類繼承的類或實現的接口,這樣使多態更完善。同樣看java的集合包源碼,iterator等。
*結合回調寫法使用,常用在監聽器,模板實現等,可以寫出清晰簡潔的代碼,易於閱讀和維護。和回調的結合可以參考另外一篇博文Java回調的概念

內部類的規則比較複雜,重在理解其本質,這樣如果在面試時遇到問題,就不需要硬背而是可以推理出來了:


普通內部類:

  1. 依賴於外部類而存在,必須先有外部類實例。外部類要訪問時,要用new 【外部類對象】【內部類對象】.屬性/方法。
  2. 即使是private,也可以隨意使用外部類的變量和方法。如果要爲什麼可以,那就是編譯器其實偷偷的在初始化的時候給內部類設置了一個隱藏的外部類對象的引用。
  3. 語法上不能有static成員變量和方法(所以也不能有main函數),這是因爲依賴外部類實例,和static是矛盾的。

靜態內部類,又叫嵌套內部類(。。。這是什麼鳥名字啊):

  1. 不依賴於外部類對象存在,可以直接使用【內部類名】.靜態的屬性/方法,如果要使用非靜態的屬性/方法,也需要new,因爲非靜態是和對象一個級別初始化的。靜態是和類一個級別初始化的。
  2. static內部類只能訪問外部類的static變量和方法,可以有自己的main函數。
  3. 個人理解靜態內部類,並不是說爲了使這個類是靜態的,而目的是爲了使用裏面的靜態變量和方法。

*局部內部類是指定義在方法中或作用域中,其可使用的變量和方法,當然也侷限於方法和作用域中了。局部內部類,就相當於方法的局部變量,是不能有修飾符的。
*匿名內部類,同樣不能有修飾符。匿名內部類不能有自己的構建器,如果想要初始化這個內部類時設置其中對象的值,則需要從外部傳遞一個變量進來,這個變量必須是final的否則報錯。怎麼傳遞?作爲方法參數,在方法體內寫匿名內部類。


內部類的繼承與被繼承

  1. 內部類可以實現別的地方的接口,可以繼承別的地方的抽象類。如果把這個內部類設爲private,在外部其他類使用這個有內部類的類提供的get方法時,可以通過upcasting獲得那個接口或者抽象類的引用。但是卻永遠不可能獲得內部類,因爲是Private的所以你連內部類的名字都沒有機會知道。 這樣這個內部類是非常安全的,不可能被downcasting,這是一種編碼策略。可以說內部類和upcasting的思想經常是聯繫在一起使用的。
  2. 可以從一個別的class A去繼承一個類的內部類,繼承的語法必須是extends Outer.Inner。並且你必須寫一個構造函數,把outer作爲參數傳遞給這個構造函數,並調用Outer.super()。爲什麼是super呢?Super()表示,要找到這個類繼承樹的最上層,從那個地方的構造函數開始,把樹上的每個類逐一初始化一直初始化到內部類,這樣才能保證這個class A能夠正確的初始化。注意和super關鍵字的區別。
  3. 內部類是否可以被重寫?一個繼承Outer的類,是沒有辦法重寫Outer裏的內部類的,可以試着這麼理解(不一定對):.class文件是Outer$Inner,他們本來就不存在什麼關聯,何來覆蓋呢?當然java要提供這種機制是可以的,但事實是java不提供這種機制。
    重寫內部類,其實實際是爲了重寫裏面的方法吧。所以我們可以這麼做(靜態內部類不能):首先Outer繼承Outer,然後用一個內部類去繼承Outer的內部類,並且也不需要像2說的一樣必須把Outer作爲參數寫構造函數,爲什麼不用呢?因爲Outer在繼承Outer的時候已經做了這些工作了。這也是爲什麼靜態內部類不能這麼做的原因。
  4. 內部接口,在有沒有static修飾時,都可以直接由別的類去implements Outer.interface,內部接口的目的也是爲了給類提供規範的數據形式。參見Map中的Entry,這樣HashMap在實現entrySet方法時,就必須遵循Entry接口,而Entry也對外隱藏了起來。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章