Documentation

The Java™ Tutorials
Hide TOC
Nested Classes嵌套类
Trail: Learning the Java Language
Lesson: Classes and Objects

Nested Classes嵌套类

The Java programming language allows you to define a class within another class.Java编程语言允许您在另一个类中定义一个类。Such a class is called a nested class and is illustrated here:此类类称为嵌套类,如下所示:

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

Terminology:术语: Nested classes are divided into two categories: non-static and static.嵌套类分为两类:非静态类和静态类。Non-static nested classes are called inner classes.非静态嵌套类称为内部类Nested classes that are declared static are called static nested classes.声明为static的嵌套类称为静态嵌套类
class OuterClass {
    ...
    class InnerClass {
        ...
    }
    static class StaticNestedClass {
        ...
    }
}

A nested class is a member of its enclosing class.嵌套类是其封闭类的成员。Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private.非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。Static nested classes do not have access to other members of the enclosing class.静态嵌套类无权访问封闭类的其他成员。As a member of the OuterClass, a nested class can be declared private, public, protected, or package private.作为OuterClass的成员,嵌套类可以声明为privatepublicprotectedpackage private(Recall that outer classes can only be declared public or package private.)(请记住,外部类只能声明为publicpackage private。)

Why Use Nested Classes?为什么要使用嵌套类?

Compelling reasons for using nested classes include the following:使用嵌套类的令人信服的原因包括:

Inner Classes内部类

As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's methods and fields.与实例方法和变量一样,内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段。Also, because an inner class is associated with an instance, it cannot define any static members itself.此外,由于内部类与实例相关联,因此它本身无法定义任何静态成员。

Objects that are instances of an inner class exist within an instance of the outer class.作为内部类实例的对象存在于外部类的实例中。Consider the following classes:考虑下面的类:

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

An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.InnerClass的实例只能存在于OuterClass的实例中,并且可以直接访问其封闭实例的方法和字段。

To instantiate an inner class, you must first instantiate the outer class.要实例化内部类,必须首先实例化外部类。Then, create the inner object within the outer object with this syntax:然后,使用以下语法在外部对象中创建内部对象:

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

There are two special kinds of inner classes:有两种特殊的内部类: local classes and anonymous classes.

Static Nested Classes静态嵌套类

As with class methods and variables, a static nested class is associated with its outer class.与类方法和变量一样,静态嵌套类与其外部类相关联。And like static class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class: it can use them only through an object reference.和静态类方法一样,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法:它只能通过对象引用来使用它们。Inner Class and Nested Static Class Example demonstrates this.内部类和嵌套静态类示例演示了这一点。


Note: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class.静态嵌套类与其外部类(和其他类)的实例成员进行交互,就像其他顶级类一样。In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.实际上,静态嵌套类在行为上是一个顶级类,为了便于打包而嵌套在另一个顶级类中。Inner Class and Nested Static Class Example also demonstrates this.内部类和嵌套静态类示例也演示了这一点。

You instantiate a static nested class the same way as a top-level class:以与顶级类相同的方式实例化静态嵌套类:

StaticNestedClass staticNestedObject = new StaticNestedClass();

Inner Class and Nested Static Class Example内部类和嵌套静态类示例

The following example, OuterClass, along with TopLevelClass, demonstrates which class members of OuterClass an inner class (InnerClass), a nested static class (StaticNestedClass), and a top-level class (TopLevelClass) can access:以下示例OuterClassTopLevelClass一起演示了OuterClass的哪些类成员—内部类(InnerClass)、嵌套静态类(StaticNestedClass)和顶级类(TopLevelClass)—可以访问:

OuterClass.java

public class OuterClass {

    String outerField = "Outer field";
    static String staticOuterField = "Static outer field";

    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }

    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            // Compiler error: Cannot make a static reference to the non-static
            //     field outerField
            // System.out.println(outerField);
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }

    public static void main(String[] args) {
        System.out.println("Inner class:");
        System.out.println("------------");
        OuterClass outerObject = new OuterClass();
        OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        innerObject.accessMembers();

        System.out.println("\nStatic nested class:");
        System.out.println("--------------------");
        StaticNestedClass staticNestedObject = new StaticNestedClass();        
        staticNestedObject.accessMembers(outerObject);
        
        System.out.println("\nTop-level class:");
        System.out.println("--------------------");
        TopLevelClass topLevelObject = new TopLevelClass();        
        topLevelObject.accessMembers(outerObject);                
    }
}

TopLevelClass.java

public class TopLevelClass {

    void accessMembers(OuterClass outer) {     
        // Compiler error: Cannot make a static reference to the non-static
        //     field OuterClass.outerField
        // System.out.println(OuterClass.outerField);
        System.out.println(outer.outerField);
        System.out.println(OuterClass.staticOuterField);
    }  
}

This example prints the following output:此示例打印以下输出:

Inner class:
------------
Outer field
Static outer field

Static nested class:
--------------------
Outer field
Static outer field

Top-level class:
--------------------
Outer field
Static outer field

Note that a static nested class interacts with the instance members of its outer class just like any other top-level class.请注意,静态嵌套类与其外部类的实例成员进行交互,就像其他顶级类一样。The static nested class StaticNestedClass can't directly access outerField because it's an instance variable of the enclosing class, OuterClass.静态嵌套类StaticNestedClass无法直接访问outerField,因为它是封闭类OuterClass的实例变量。The Java compiler generates an error at the highlighted statement:Java编译器在突出显示的语句处生成错误:

static class StaticNestedClass {
    void accessMembers(OuterClass outer) {
       // Compiler error: Cannot make a static reference to the non-static
       //     field outerField
       System.out.println(outerField);
    }
}

To fix this error, access outerField through an object reference:要修复此错误,请通过对象引用访问outerField

System.out.println(outer.outerField);

Similarly, the top-level class TopLevelClass can't directly access outerField either.同样,顶级类TopLevelClass也不能直接访问outerField

Shadowing阴影

If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope.如果特定范围(如内部类或方法定义)中的类型声明(如成员变量或参数名)与封闭范围中的另一个声明具有相同的名称,则该声明将隐藏封闭范围的声明。You cannot refer to a shadowed declaration by its name alone.不能仅通过名称引用带阴影的声明。The following example, ShadowTest, demonstrates this:以下示例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);
    }
}

The following is the output of this example:以下是此示例的输出:

x = 23
this.x = 1
ShadowTest.this.x = 0

This example defines three variables named x: the member variable of the class ShadowTest, the member variable of the inner class FirstLevel, and the parameter in the method methodInFirstLevel.此示例定义了三个名为x的变量:类ShadowTest的成员变量、内部类FirstLevel的成员变量和方法methodInFirstLevel中的参数。The variable x defined as a parameter of the method methodInFirstLevel shadows the variable of the inner class FirstLevel.定义为methodInFirstLevel方法参数的变量x将隐藏内部类FirstLevel的变量。Consequently, when you use the variable x in the method methodInFirstLevel, it refers to the method parameter.因此,在methodInFirstLevel方法中使用变量x时,它引用方法参数。To refer to the member variable of the inner class FirstLevel, use the keyword this to represent the enclosing scope:要引用内部类FirstLevel的成员变量,请使用关键字this表示封闭范围:

System.out.println("this.x = " + this.x);

Refer to member variables that enclose larger scopes by the class name to which they belong.引用按其所属类名括起较大作用域的成员变量。For example, the following statement accesses the member variable of the class ShadowTest from the method methodInFirstLevel:例如,以下语句从methodInFirstLevel方法访问ShadowTest类的成员变量:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

Serialization系列化

Serialization of inner classes, including local and anonymous classes, is strongly discouraged.强烈反对内部类(包括局部类和匿名类)的序列化When the Java compiler compiles certain constructs, such as inner classes, it creates synthetic constructs; these are classes, methods, fields, and other constructs that do not have a corresponding construct in the source code.当Java编译器编译某些构造时,例如内部类,它会创建合成构造;这些是在源代码中没有相应构造的类、方法、字段和其他构造。Synthetic constructs enable Java compilers to implement new Java language features without changes to the JVM.合成构造使Java编译器能够实现新的Java语言特性,而无需更改JVM。However, synthetic constructs can vary among different Java compiler implementations, which means that .class files can vary among different implementations as well.然而,合成结构在不同的Java编译器实现中可能有所不同,这意味着.class文件在不同的实现中也可能有所不同。Consequently, you may have compatibility issues if you serialize an inner class and then deserialize it with a different JRE implementation.因此,如果序列化内部类,然后使用不同的JRE实现对其进行反序列化,则可能会出现兼容性问题。See the section Implicit and Synthetic Parameters in the section Obtaining Names of Method Parameters for more information about the synthetic constructs generated when an inner class is compiled.有关编译内部类时生成的合成构造的更多信息,请参阅获取方法参数名称一节中的隐式参数和合成参数一节。


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