Java基礎教程(14)--嵌套類
??Java允許在一個類中定義另外一個類,這樣的類被稱為嵌套類,就像下面這樣:
class OuterClass {
...
class NestedClass {
...
}
}
??嵌套類分為兩種:靜態的和非靜態的。聲明為static的嵌套類被稱為靜態嵌套類,非靜態嵌套類則被稱為內部類:
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
??嵌套類是其所在的外部類的成員。內部類可以訪問外部類中的其他成員,即使這個成員被private修飾。靜態嵌套類則沒有訪問外部類中其他成員的權限。作為外部類的一個成員,嵌套類可以被聲明為private、public、protected或者包私有的。
??下面是幾個為什麽要使用嵌套的類原因:
- 能夠將僅在一個地方使用的類合理地組合。如果一個類可能只對於另外一個類有用,此時將前者組合到後者,可以使得程序包更加簡潔。
- 增強封裝性。假如有兩個類A和B,B類需要使用A類中的成員,而恰好該成員又是僅類內部可見(private)的,如果將B定義為A的嵌套類,則B可以使用A的任何成員,而且B也可以聲明為外部不可見(private),將B隱藏起來。
- 能夠使代碼可讀性和維護性更強。嵌套的類代碼相較於頂級類,更靠近它被使用的地方,方便查看。
一.靜態嵌套類
??就像靜態方法和靜態變量一樣,靜態嵌套類是和外部類相關聯的。和靜態方法一樣,靜態嵌套類不能直接引用實例變量和實力方法,只能通過一個對象引用。實際上,可以將靜態嵌套類看作是一個頂級類,只不過將其嵌套在其他類中方便打包。
??靜態嵌套類可以用過外部類的名字去訪問:
OuterClass.StaticNestedClass
??可以使用下面的語法為靜態嵌套類創建對象:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
二.內部類
??就像實例方法和實例變量一樣,內部類與外部類的實例相關聯並且可以直接訪問外部類的方法和成員。並且,因為內部類與外部類的實例相關聯,因此它內部不能定義靜態成員。
??要實例化內部類,必須創建外部類的對象,然後使用這個對象去創建內部類的對象:
OuterClass outerObject = new OuterClass(); 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類的成員變量,內部類FirstLevel的成員變量,以及方法methodInFirstLevel的參數。方法methodInFirstLevel的參數x屏蔽了內部類FirstLevel的成員變量x和ShadowTest類的成員變量x。因此,在表示內部類FirstLevel的成員變量x時,要像下面這樣:
System.out.println("this.x = " + this.x);
??在表示ShadowTest類的成員變量x時,要像下面這樣:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
四.局部類
??局部類是在塊(由大括號包圍的零條或多條語句)中定義的類。經常會在方法體中見到局部類。下面是一個定義在方法中的局部類:
public class OuterClass {
public void method() {
...
class LocalClass {
...
}
}
}
??局部類可以訪問外部類的成員。此外,局部類還可以訪問局部變量。然而,局部類只能訪問由final修飾的局部變量。當一個局部類訪問一個塊中的局部變量或參數時,它就捕獲了這個變量或者參數。從Java8開始,局部類不但可以訪問由final修飾的局部變量和參數,還可以訪問近似final的局部變量和參數。近似final的意思是說這個變量或參數的值自從初始化之後就沒有修改過。此外,局部類中的變量也會屏蔽定義在外部的同名變量或參數。
??局部類中基本不能定義靜態成員。不過也有例外,可以在局部類中定義靜態常變量(常變量是指類型為基本數據類型或者String,被聲明為final,並且使用編譯時常量表達式進行初始化的變量。編譯時常量表達式通常是可以在編譯時計算的字符串或算術表達式)。
??定義在靜態方法中的局部類,只能引用外部類的靜態成員。不能在塊中定義接口,因為接口是天生靜態的。也不能在局部類中定義靜態初始化器或者接口。
五.匿名類
??匿名類讓代碼看上去更加簡潔,它能讓你同時聲明和實例化一個類。它們類似於局部類,只不過匿名類沒有名稱。如果某個局部類只使用一次,可以將它定義為匿名類。
??局部類是類的聲明,而匿名類則是表達式,這意味著匿名類是在一個表達式中定義的。匿名類表達式的語法就像是調用構造器的語法,只不過構造器後面跟的是類的定義。就像下面這樣:
HelloWorldInterface frenchGreeting = new HelloWorldInterface() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
??匿名類的語法包含以下幾部分:
- new操作符;
- 匿名類實現的接口或者繼承的類;
- 包含了構造器參數的小括號。註意,當匿名類實現了某個接口時,由於接口沒有構造器,因此使用一個空的小括號來表示;
- 匿名類的定義體。
??就像局部類一樣,匿名類也可以捕獲變量。下面是幾條規則:
- 匿名類可以訪問它的外部類的成員;
- 匿名類不能訪問外部範圍中沒有使用final修飾或不是近似final的局部變量。
- 匿名類中會屏蔽外部範圍中同名的類型。
??匿名類在成員的定義上和局部類有相同的規則:
- 不能在匿名類中聲明靜態初始化器或成員接口;
- 匿名類中可以有靜態成員,前提是這個靜態成員必須是常變量。
??可以在匿名類中聲明以下元素:
- 域;
- 額外的方法(接口中沒有定義的方法);
- 非靜態初始化塊;
- 局部類。
??不能在匿名類中定義構造方法。
Java基礎教程(14)--嵌套類