1. 程式人生 > >java遺珠之巢狀類

java遺珠之巢狀類

使用內部類的原因

1.合乎只在一個地方備用的邏輯: 如果一個類僅僅被一個別的類使用,那麼把這個類嵌入到使用它的類中非常符合邏輯,巢狀這樣的幫助類是的package變得簡潔。

2.有益於封裝: 如果兩個類A和B,如果B需要訪問A定義為private的成員,那麼B巢狀到A類中,即使A的成員是private的B也可以訪問,另外B類也可以被隱藏起來,不被外部訪問。

3.可讀性好且易於維護 在頂級類中在使用它們的地方巢狀很多小的類利於可讀和維護

靜態內部類

像類變數和類方法一樣,靜態內部類通過它的封閉類來呼叫。並且像類方法一樣,靜態內部類不能直接訪問定義在外部類的例項變數和例項方法,只能通過外部類的物件引用才可以使用它們。

靜態內部類使用外部類或者其他類的例項成員,需要像其他頂級類一樣使用它們。靜態內部類的行為看起來像是一個頂級類一樣,只是僅僅因為包裝便利的關係,才將它巢狀到另一個頂級類內部。

靜態內部類需要使用它的包裝類來訪問。

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

內部類

像例項變數和例項方法一樣,內部類需要通過它的封閉類的例項來呼叫,並且它可以直接訪問物件的方法和欄位,因為內部類是通過外部類的例項關聯,因此它不能定義靜態的成員。

內部類的例項要依託於它的外包類的例項存在

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

要例項化內部類必須例項化外部類,然後在外部類例項的基礎上建立內部類:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

隱匿

如果定義一個型別(比如成員變數或者引數名稱)在一個特定的區域(比如內部類或者方法引數)和封閉區域內的另一個定義重名,那麼這個定義會隱匿掉封閉區域的定義,你無法僅僅通過名字來使用被隱匿掉的定義,可以看如下程式碼

public
class ShadowTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { System.out.println("x = " + x); System.out.println("this.x = " + this.x); System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); } } public static void main(String... args) { ShadowTest st = new ShadowTest(); ShadowTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); } }

輸出如下:

x = 23
this.x = 1
ShadowTest.this.x = 0

這個例子定義了三個名字為x的變數,ShadowTest類的成員變數x,內部類FirstLevel類的成員變數x,和methodInFirstLevel方法的引數x。methodInFirstLevel方法的引數x隱匿了內部類FirstLevel類的成員變數x,因此你在methodInFirstLevel方法中使用x時使用的是方法引數的x,為了使用內部類FirstLevel的成員變數x,你需要this關鍵字來指明是內部類FirstLevel類的成員變數x:

 System.out.println("this.x = " + this.x);

為了使用更大範圍的成員變數,你需要通過指定類名的方式,在此例子中,你在methodInFirstLevel中使用ShadowTest類的成員變數你需要如下做:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

序列化

強烈建議不需要序列化內部類,包括本地類和匿名類。因為編譯器編譯某些構造器是比如內部類,會建立合成構造,由類,方法,欄位和其他與原始碼不對應的構造器組成。合成構造可以在不更改jvm的前提下實現新的java特性,但是合成構造在不同的編譯器下的實現也不通,也就是說生成的.class檔案都是不同,因此序列化內部類後,由不同的JRE反序列化時就會出現問題。