文档

Java™ 教程-Java Tutorials 中文版
嵌套类
Trail: Learning the Java Language
Lesson: Classes and Objects

嵌套类

Java 编程语言允许你在另一个类中定义类。这样的类称为 nested class (嵌套类),如下所示:

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

术语: 嵌套类分为两类:静态和非静态。声明为 static 的嵌套类称为 static nested classes (静态嵌套类)。非静态嵌套类称为 inner classes (内部类)
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}

嵌套类是其封闭类的成员。非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。静态嵌套类无权访问封闭类的其他成员。作为 OuterClass 的成员,可以将嵌套类声明为 privatepublicprotectedpackage private。(回想一下,外部类只能声明为 publicpackage private。)

为什么使用嵌套类?

使用嵌套类的令人信服的理由包括:

静态嵌套类

与类方法和变量一样,静态嵌套类与其外部类相关联。和静态类方法一样,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法:它只能通过对象引用来使用它们。


注意: 静态嵌套类与其外部类(和其他类)的实例成员交互,就像任何其他顶级类一样。实际上,静态嵌套类在行为上是一个顶级类,它嵌套在另一个顶级类中以方便打包。

使用封闭类的类名访问静态嵌套类:

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 实现反序列化,则可能存在兼容性问题。有关在编译内部类时生成的合成结构的更多信息,请参阅 获取方法参数名称 部分中的 隐式和合成参数 部分。


Previous page: Questions and Exercises: Objects
Next page: Inner Class Example