# 嵌套类
Java 编程语言允许您在另一个类中定义一个类。这样的一个类称为 嵌套类,这里说明如下:
class OuterClass {
...
class NestedClass {
...
}
}
2
3
4
5
6
TIP
术语:嵌套类分为静态和非静态两类。被声明 static 的嵌套类称为 静态嵌套类 。非静态嵌套类称为 内部类。
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
2
3
4
5
6
7
8
9
嵌套类是其封闭类的成员。非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有的。静态嵌套类无法访问封闭类的其他成员。为一体的一个部件 OuterClass,一个嵌套类可以声明 private、public、protected 或包专用。(回想一下,外部类只能被声明 public 或包私有)。
# 为什么要使用嵌套类?
使用嵌套类的令人信服的原因包括:
这是一种仅在一个地方使用的类进行逻辑分组的方法
如果类仅对另一个类有用,那么将它嵌入该类并将其保持在一起就是合乎逻辑的。嵌套这样的 “helper classes” 使他们的包更加精简。
封装
考虑两个顶级类 A 和 B,其中 B 需要访问否则将被声明的 A 成员 private。通过在 A 类中隐藏 B 类,A 的成员可以被声明为 private,B 可以访问它们。另外,B 本身也可以从外界隐藏起来。
易读易维护
将顶级类中的小类嵌套将代码更接近使用的位置。
# 静态嵌套类
与类方法和变量一样,静态嵌套类与其外部类相关联。和静态类方法一样,静态嵌套类也不能直接引用到其包围类中定义的实例变量或方法:它只能通过对象引用使用它们。
TIP
静态嵌套类与其外层类(和其他类)的实例成员一样,与任何其他顶级类一样。实际上,静态嵌套类是行为上的顶级类,已经嵌套在另一个顶级类中以便于打包。
使用封闭类名访问静态嵌套类
OuterClass.StaticNestedClass
例如,要为静态嵌套类创建一个对象,请使用以下语法
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
2
# 内部类
与实例方法和变量一样,内部类与其封闭类的实例相关联,并可以直接访问该对象的方法和字段。另外,因为内部类与一个实例相关联,所以它不能定义任何静态成员。
内部类的实例的对象存在于外部类的实例中。考虑以下几类:
class OuterClass {
...
class InnerClass {
...
}
}
2
3
4
5
6
OuterClass 可以直接访问其封闭实例的方法和字段。
要实例化一个内部类,必须首先实例化外部类。然后,使用以下语法在外部对象内创建内部对象:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
内部类有两种特殊类: 本地类 和 匿名类。
# Shadowing(阴影?)
如果(例如内类或方法的定义)一个类型(例如作为成员变量或参数名称)在特定范围的声明具有相同的名称作为在封闭范围另一声明,则声明阴影的声明的封闭范围。您不能仅通过其名称来引用阴影声明。以下示例 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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
程序输出
x = 23
this.x = 1
ShadowTest.this.x = 0
2
3
请注意系统变量使用 this 方法进行区分。
# 序列化
强烈不鼓励内部类的序列化,包括 本地 和 匿名类。当 Java 编译器编译某些构造(如内部类)时,会创建 合成结构; 这些是在源代码中没有相应构造的类、方法、字段和其他结构。合成构造使 Java 编译器能够实现新的 Java 语言功能,而不会更改 JVM。然而,合成结构可以在不同的 Java 编译器实现之间变化,这意味着 .class 文件也可以在不同的实现之间变化。因此,如果序列化内部类,然后使用不同的 JRE 实现对其进行反序列化,则可能会遇到兼容性问题。有关在编译内部类时生成的合成结构的更多信息,请参阅“ 获取方法参数名称 ”部分中的“ 隐式和合成参数 ”部分 。