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
来限制对内部类的访问,就像你使用它们来限制对其余类成员的访问同样。