內部類別
- 在一般類別中在定義一個類別,依位置可以分為:
- 一般類部類別 (regular inner class)
- 方法類別 (method class)
- 匿名類別 (anonymous class)
- 靜態內部類別 (static inner class)
一般類部類別 (regular inner class)
-
和我們一般建立的類別一模一樣
-
一個外部類別可以有很多個內部類別,同樣的一個內部類別也可以有很多個次內部類別
-
語法:
-
-
編譯後的檔名
- 以上面為例,會產生三個檔案,以$區隔
OuterClass.class
(class OuterClass)
OuterClass$InnerClass.class
(class InnerClass)
OuterClass$InnerClass$SubInnerClass.class
(class SubInnerClass)
-
外部類別存取內部類別
-
-
對 OuterClass 而言,可以直接寫成 InnerClass ic = new InnerClass(); (因為他本來就位於 OuterClass 內。
-
對其他的外部類別來說,InnerClass 是 OuterClass的內部類別,所以需宣告成 OuterClass.InnerClass
一般內部類別的成員存取關係
Q: 為甚麼Inner可以參照到Outer.this.x??
A: 因為執行時期,Outer會提供自己的this參照給Inner所用。另外Outer.sx也可以是因為Outer.this.sx (nonstaic call static)
方法內部類別 (method class)
- 建立的方法內部類別物件的程式碼必須寫在方法類別定義之後
- 編譯之後,方法內部類別的檔名是 test$1Inner.class,其中1是流水號
方法區域變數
不能直接存取方法內部非 Final 的區域變數
匿名類別 (anonymous class)
就是沒有宣告名稱的類別,直接以{…}實作程式碼
適用於只會用到一次,之後便不需再使用的類別
匿名類別實體檔名
- 外部類別$流水號(1,2…n).class
- 匿名類別在實作interface時,也是用implements來實作的。
- 同樣的在實作abstract class時,也會用extends來實作的。(你在反組譯匿名類別的class時會發現)
- 這種寫法的出現,就是為了簡化成式的撰寫方式
- 多行的問題,在sdk1.6_10被修正了,會在編譯時期拋出例外
靜態內部類別 (static innner class)
如同加上static 的變數一樣,只是它屬於某個類別,並不是類別中的實體。
本身並沒有this的參考
若外部類別有宣告static成員的話,因為也是被配置在Globe區,因此外部的靜態成員與靜態內部類別可以互相看見並存取。
而他也是唯一一個內部類別可以同時宣告static和non-static的內部類別。
註:一般內部類別若宣告了static成員就等於是靜態內部類別
靜態內部類別與成員之間的關係
Keyword: final
- 用final宣告的類別、方法、屬性,不能以任何方式修改(只能被初始化一次)
- 類別不可再被繼承
- 方法不能再被複寫
- 屬性給予定值後不可更動
- 若沒在宣告同時初始化的話,則稱為:[blank final variable]
物件型別轉換
-
種類
- 隱含式轉換 (Casts Up)
- 強制型轉換 (Downward Casts)
- 範圍:小(父類別) 轉 大(子類別)
- EX:FatherClass f = (FatherClass)new ChildrenClass();
- 在轉換時會有風險,應先用instanceof來判斷是否可以強制轉型
-
instanceof運算子
- 使用時機:判斷是否可以強制轉型
- 回傳值:true or false
- 語法:
被轉物件變數 instanceof 欲轉類別名稱
Initializer實作區塊
- 實作區塊:指的是{…}
- 若單獨放在class類就是Initializer實作區塊
- 種類
- instance initializer
- static initializer
initializer
- EX:用來初始化instance物件變數
- 編譯時,會將initializer中的程式碼複製到所有建構子中
- 假設建構子中原本就有實作的話,則會將initializer的程式碼複製到原先程式碼的前面
static initializer
- 就是針對static成員的初始化
- 宣告在類別中,而不是在方法裡
- 用來指派或初始static變數
- 於類別載入時執行,且只執行一次
注意:static initialzier中,不可以存取或初始化instance的物件變數
但在instance initializer中,卻可以存取或初始畫static成員變數
覆寫 equals() 和 hashCode()
equal()
- 目的
- Object類別中原本就有提供 equals()
- 比的是兩個物件的記憶體參照位置
- 與實際物件本身的數型沒有任何關係 (所以不管怎麼比較都不會是相同的,因為參照值本來就不同,除非引用同個物件)
- 因此我們必須依照實際的情況,來自定什麼條件(屬性)相等,來實作(覆寫)原本的equals()
覆寫equals()的基本原則
-
使用 == 判斷,也許要比較兩者是否來自同個物件實體
-
比較兩者是否屬於同個類別,用 getClass()
-
用該類別的必要的成員變數來判斷,利用if-else
-
將Ball類別加上我們覆寫的equals()
在覆寫equals()時,一律覆寫hashcode()
- hashcode (雜湊碼),可以想成是物件的編號
- 在同個應用程式執行期間,對同個物件呼叫hashcode(),其回傳值必須一樣
- 若兩物件被equals()定義為相等的話,其hashcode()也必須相等。
- 反之,兩物件不相等,其hashcode()可以不必產生相同的解果(相同也可以)
- 對不同的物件產生不同的hashcode,有可能提升hashtable的效率
- 詳細參考[Effective Java Programming Language Guide,條款8]