Java編程語言容許你在另外一個類中定義類,這樣的類稱爲嵌套類,以下所示:html
class OuterClass { ... class NestedClass { ... } }
術語:嵌套類分爲兩類:靜態和非靜態,聲明爲
static
的嵌套類稱爲靜態嵌套類,非靜態嵌套類稱爲內部類。
class OuterClass { ... static class StaticNestedClass { ... } class InnerClass { ... } }
嵌套類是其封閉類的成員,非靜態嵌套類(內部類)能夠訪問封閉類的其餘成員,即便它們被聲明爲private
,靜態嵌套類無權訪問封閉類的其餘成員。做爲OuterClass
的成員,能夠將嵌套類聲明爲private
、public
、protected
或包私有(回想一下,外部類只能聲明爲public
或包私有)。java
使用嵌套類的使人信服的理由包括:編程
private
,經過將類B隱藏在類A中,能夠將A的成員聲明爲私有,而且B能夠訪問它們,另外,B自己也能夠不被外界發現。與類方法和變量同樣,靜態嵌套類與其外部類相關聯,和靜態類方法同樣,靜態嵌套類不能直接引用其封閉類中定義的實例變量或方法:它只能經過對象引用來使用它們。segmentfault
注意:靜態嵌套類與其外部類(和其餘類)的實例成員交互,就像任何其餘頂級類同樣,實際上,靜態嵌套類在行爲上是一個頂級類,它已嵌套在另外一個頂級類中以方便打包。
使用封閉的類名訪問靜態嵌套類:api
OuterClass.StaticNestedClass
例如,要爲靜態嵌套類建立對象,請使用如下語法:數組
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
與實例方法和變量同樣,內部類與其封閉類的實例相關聯,而且能夠直接訪問該對象的方法和字段,此外,因爲內部類與實例相關聯,所以自己沒法定義任何靜態成員。數據結構
做爲內部類的實例的對象存在於外部類的實例中,考慮如下類:oracle
class OuterClass { ... class InnerClass { ... } }
InnerClass
的實例只能存在於OuterClass
的實例中,而且能夠直接訪問其封閉實例的方法和字段。編程語言
要實例化內部類,必須首先實例化外部類,而後,使用如下語法在外部對象中建立內部對象:函數
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
有兩種特殊的內部類:局部類和匿名類。
若是特定範圍(例如內部類或方法定義)中的類型聲明(例如成員變量或參數名稱)與封閉範圍中的另外一個聲明具備相同的名稱,而後聲明會影響封閉範圍的聲明,你不能僅經過其名稱引用遮蔽的聲明,如下示例ShadowTest
演示了這一點:
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
中的參數。變量x
定義爲方法methodInFirstLevel
的參數遮蔽內部類FirstLevel
的變量,所以,當你在方法methodInFirstLevel
中使用變量x
時,它引用方法參數,要引用內部類FirstLevel
的成員變量,請使用關鍵字this
來表示封閉範圍:
System.out.println("this.x = " + this.x);
引用成員變量,這些成員變量根據所屬的類名包含更大的做用域,例如,如下語句從方法methodInFirstLevel
訪問類ShadowTest
的成員變量:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
強烈建議不要對內部類(包括局部類和匿名類)進行序列化,當Java編譯器編譯某些構造(如內部類)時,它會建立合成構造,這些是類、方法、字段和其餘在源代碼中沒有相應構造的構造。合成構造使Java編譯器可以在不更改JVM的狀況下實現new
的Java語言功能,可是,合成構造能夠在不一樣的Java編譯器實現之間變化,這意味着.class
文件也能夠在不一樣的實現之間變化。所以,若是序列化內部類,而後使用其餘JRE實現反序列化,則可能存在兼容性問題,有關在編譯內部類時生成的合成構造的更多信息,請參閱獲取方法參數名稱一節中的隱式和合成參數部分。
要看內部類的使用,首先要考慮一個數組,在下面的示例中,你將建立一個數組,用整數值填充它,而後僅按升序輸出數組的偶數索引值。
下面的DataStructure.java
示例包括:
DataStructure
外部類,它包含一個構造函數,用於建立DataStructure
的實例,該實例包含一個數組,該數組填充了連續的整數值(0、一、二、3等等),以及一個方法,該方法打印具備偶數索引值的數組元素。EvenIterator
內部類,它實現了繼承了Iterator< Integer>接口的DataStructureIterator
接口,迭代器用於逐步執行數據結構,一般有方法來測試最後一個元素,檢索當前元素,而後移動到下一個元素。DataStructure
對象(ds
)的main
方法,而後調用printEven
方法來打印具備偶數索引值的數組arrayOfInts
的元素。public class DataStructure { // Create an array private final static int SIZE = 15; private int[] arrayOfInts = new int[SIZE]; public DataStructure() { // fill the array with ascending integer values for (int i = 0; i < SIZE; i++) { arrayOfInts[i] = i; } } public void printEven() { // Print out values of even indices of the array DataStructureIterator iterator = this.new EvenIterator(); while (iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } interface DataStructureIterator extends java.util.Iterator<Integer> { } // Inner class implements the DataStructureIterator interface, // which extends the Iterator<Integer> interface private class EvenIterator implements DataStructureIterator { // Start stepping through the array from the beginning private int nextIndex = 0; public boolean hasNext() { // Check if the current element is the last in the array return (nextIndex <= SIZE - 1); } public Integer next() { // Record a value of an even index of the array Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]); // Get the next even element nextIndex += 2; return retValue; } } public static void main(String s[]) { // Fill the array with integer values and print out only // values of even indices DataStructure ds = new DataStructure(); ds.printEven(); } }
輸出是:
0 2 4 6 8 10 12 14
請注意,EvenIterator
類直接引用DataStructure
對象的arrayOfInts
實例變量。
你可使用內部類來實現輔助類,例如本示例中所示的類,要處理用戶界面事件,你必須知道如何使用內部類,由於事件處理機制會普遍使用它們。
還有兩種類型的內部類,你能夠在方法體內聲明內部類,這些類稱爲局部類,你還能夠在方法體內聲明內部類,而無需命名該類,這些類稱爲匿名類。
你能夠對用於外部類的其餘成員的內部類使用相同的修飾符,例如,你可使用訪問修飾符private
、public
和protected
來限制對內部類的訪問,就像你使用它們來限制對其餘類成員的訪問同樣。