Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
Java 编程语言允许你在另一个类中定义类。这样的类称为 nested class (嵌套类),如下所示:
class OuterClass { ... class NestedClass { ... } }
static
的嵌套类称为 static nested classes (静态嵌套类)。非静态嵌套类称为 inner classes (内部类)。
class OuterClass { ... static class StaticNestedClass { ... } class InnerClass { ... } }
嵌套类是其封闭类的成员。非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。静态嵌套类无权访问封闭类的其他成员。作为 OuterClass
的成员,可以将嵌套类声明为 private
,public
,protected
或 package private。(回想一下,外部类只能声明为 public
或 package private。)
使用嵌套类的令人信服的理由包括:
这是一种对仅在一个地方使用的类的逻辑分组方法:如果一个类只对另一个类有用,那么将它嵌入该类并将两者保持在一起是合乎逻辑的。嵌套这样的 "帮助类" 使得它们的包更加简化。
增加封装:考虑两个顶级类 A 和 B,其中 B 需要访问 A 的声明为 private
成员,。通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有,并且 B 可以访问它们。另外,B 本身可以对外面隐藏。
它可以导致更可读和可维护的代码:在顶级类中嵌套小类会使代码更接近于使用它的位置。
与类方法和变量一样,静态嵌套类与其外部类相关联。和静态类方法一样,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法:它只能通过对象引用来使用它们。
使用封闭类的类名访问静态嵌套类:
OuterClass.StaticNestedClass
例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
与实例方法和变量一样,内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段。此外,由于内部类与实例相关联,因此无法定义任何静态成员本身。
作为内部类实例的对象,存在于一个外部类的实例中。考虑以下类:
class OuterClass { ... class InnerClass { ... } }
InnerClass
的实例只能存在于 OuterClass
的实例中,并且可以直接访问其封闭实例的方法和字段。
要实例化内部类,必须首先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
有两种特殊的内部类:local classes (局部类) 和 anonymous classes (匿名类)。
如果特定范围(例如内部类或方法定义)中的类型声明(例如成员变量或参数名称)与封闭范围中的另一个声明具有相同的名称,则声明 shadows (遮蔽) 封闭范围的声明。你不能仅通过其名称引用被遮蔽的声明。以下示例 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
中的参数。定义为方法 methodInFirstLevel
的参数的变量 x
会遮蔽内部类 FirstLevel
的变量。因此,当你在方法 methodInFirstLevel
中使用变量 x
时,它引用方法参数。要引用内部类 FirstLevel
的成员变量,请使用关键字 this
来表示封闭范围:
System.out.println("this.x = " + this.x);
通过它们所属的类名,来引用包含于更大范围的成员变量。例如,以下语句从方法 methodInFirstLevel
访问类 ShadowTest
的成员变量:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
序列化强烈建议不要使用 内部类,包括 local (局部类) 和 anonymous (匿名类) 。当 Java 编译器编译某些构造(如内部类)时,它会创建 synthetic constructs (合成结构);这些是在源代码中没有相应结造的类,方法,字段和其他结构。合成构造使 Java 编译器能够在不更改 JVM 的情况下实现新的 Java 语言功能。但是,合成构造在不同的 Java 编译器实现中可能不同,这意味着 .class
文件也可以在不同的实现之间变化。因此,如果序列化内部类,然后使用其他 JRE 实现反序列化,则可能存在兼容性问题。有关在编译内部类时生成的合成结构的更多信息,请参阅 获取方法参数名称 部分中的 隐式和合成参数 部分。