Java的静态嵌套类与非静态内部类

Java语言支持在一个类中定义另一个类,这样被定义的类称为嵌套类。例如:

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

在类OuterClass中定义了一个类NestedClass,类NestedClass就被称为嵌套类。

嵌套类包括两种类型:静态和非静态。

声明了static的嵌套类,称为静态嵌套类。而没有声明static的非静态嵌套类,称为内部类。

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

    class InnerClass {
        ...
    }
}

嵌套类是其外部类的成员。

非静态的内部类,可以访问外部类的静态成员和实例成员,即使成员被声明为private。

静态的嵌套类,不能直接访问外部类的实例成员。

作为外部类的一个成员,嵌套类可以被声明为private、public、protected或者包私有的。
(回想一下,外部类只能被声明为public或者包私有的)

为什么使用嵌套类?

使用嵌套类的原因包括:

逻辑分组:它是一种将只在一个地方使用的类进行逻辑分组的方法。如果一个类A只对另一个类B有用,那么将类A嵌入到类B中并将这两个类放在一起是合理的。而且嵌套之后,包会更加简单,井井有条。

提高封装性:考虑有两个顶级类A和B,其中B需要访问A的私有成员。通过将类B隐藏在类A中,可以将A的成员声明为私有,同时B可以访问它们。另外,B本身也可以隐藏起来,不被外界看到。

使代码更具可读性和可维护性:使得代码更靠近使用它的地方。

静态嵌套类

与类方法、类变量一样,静态嵌套类与其外部类相关联。

同时,和静态类方法类似,静态嵌套类不能直接引用在外部类中定义的实例变量或方法,只能通过对象引用来使用它们。

值得注意的是:静态嵌套类与它的外部类(或其他类)的实例成员进行交互,就像其他任何顶级类一样。实际上,静态嵌套类在表现行为上就是一个顶级类,只是为了包装方便,将该顶级类嵌套在另一个顶级类中。

静态嵌套类需要使用外部类的类名来进行访问:OuterClass.StaticNestedClass。

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

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

(非静态)内部类

与实例方法、实例变量一样,内部类与其外部类的实例相关联,并且可以直接访问外部实例对象的方法和字段。

另外,由于内部类与实例相关联,因此内部类本身不能定义任何静态成员。

换句话说,内部类的实例对象,存在于外部类的实例中。考虑以下类:

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

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

也就是说,非静态内部类的实例,依赖于其外部类实例的存在而存在。如果没有创建外部类实例,就不可能创建非静态内部类实例。

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

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

另外,有两种特殊的内部类:本地类和匿名类。

隐藏

如果在特定范围(如内部类或方法定义)中的类型声明(如成员变量或者参数名),与其所在外部范围中的另一个声明具有相同的名称,则该声明将会隐藏其外部范围的声明。不能仅通过名称引用被隐藏的声明。

下面的例子,演示了这一点:

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时,x指的是所在方法的参数。
如果要引用内部类FirstLevel的成员变量,可以使用关键字this来访问:

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

通过外部类的类名,可以引用包含更大作用域的成员变量。
例如,下面的语句从方法methodInFirstLevel中访问类ShadowTest的成员变量:

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

参考

英文原文:https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html


---转载本站文章请注明作者和出处 二进制之路(binarylife.icu),请勿用于任何商业用途---

留下评论