Nested Classes

Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类,并在此处进行说明:

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

Terminology:

嵌套类分为两类:静态和非静态。声明为static的嵌套类称为静态嵌套类。非静态嵌套类称为内部类。

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}

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

为什么使用嵌套类?

使用嵌套类的令人信服的原因包括:

  • 这是一种将仅在一个地方使用的类进行逻辑分组的方式 :如果一个类仅对另一个类有用,那么将其嵌入该类并将两者保持在一起是合乎逻辑的。嵌套此类“帮助程序类”可使它们的程序包更加简化。

  • 它增加了封装 :考虑两个顶级类 A 和 B,其中 B 需要访问 A 的成员,否则将其声明为private。通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有,而 B 可以访问它们。另外,B 本身可以对外界隐藏。

  • 这可能会导致代码更具可读性和可维护性 :在顶级类中嵌套小类会使代码更靠近使用位置。

静态嵌套类

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

Note:

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

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

OuterClass.StaticNestedClass

例如,要为静态嵌套类创建一个对象,请使用以下语法:

OuterClass.StaticNestedClass nestedObject =
     new OuterClass.StaticNestedClass();

Inner Classes

与实例方法和变量一样,内部类与其所在类的实例相关联,并且可以直接访问该对象的方法和字段。另外,由于内部类与实例相关联,因此它本身不能定义任何静态成员。

作为内部类实例的对象存在于外部类实例中。考虑以下类别:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

InnerClass实例只能存在于OuterClass实例中,并且可以直接访问其封闭实例的方法和字段。

要实例化内部类,必须首先实例化外部类。然后,使用以下语法在外部对象内创建内部对象:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

有两种特殊的内部类:local classesanonymous classes

Shadowing

如果在特定范围(例如内部类或方法定义)中的类型声明(例如成员变量或参数名称)与封闭范围中的另一个声明具有相同的名称,则声明* shadows *封闭范围的声明。您不能仅凭其名称引用带 shade 的声明。以下示例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);

Serialization

强烈建议不要使用Serialization内部类,包括localanonymous类。当 Java 编译器编译某些构造(例如内部类)时,它会创建* synthetic 构造*;它们是类,方法,字段以及其他在源代码中没有相应构造的构造。合成构造使 Java 编译器无需更改 JVM 就可以实现新的 Java 语言功能。但是,合成构造在不同的 Java 编译器实现中可能有所不同,这意味着.class文件在不同的实现中也可能有所不同。因此,如果序列化一个内部类,然后使用其他 JRE 实现对其进行反序列化,则可能会遇到兼容性问题。有关在编译内部类时生成的综合构造的更多信息,请参见部分获取方法参数的名称中的部分隐式和综合参数