Java - 嵌套类(内部类&静态嵌套类)

454 阅读4分钟

嵌套类(内部类&静态嵌套类)

Java允许在一个类中定义另一个类,在类中被声明的类称为嵌套类(比如下面的NestedClass类)。

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

嵌套类分为两种:内部类和静态嵌套类,声明格式的主要区别在于有无static关键字修饰。

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

暂且把外面的这个类称为封闭类,嵌套类是封闭类的成员。内部类具有访问封闭类中成员变量和方法的权限,即使封闭类的成员被声明为私有的。而静态嵌套类不具有访问封闭类成员的权限。

作为一个嵌套类,其同时是封闭类的成员,在类中的成员可以被声明为private,publice,protected以及Package私有的。而封闭类或者普通的类只能被声明为publicePackage私有

为什么要用嵌套类?

  1. 嵌套类是一个类进行逻辑分组的方法:如果一个类只在另一个类中使用,那么把这个类嵌入到另一个类中是合乎逻辑的。在日常的工作中,最常见的例子就是Linux上的一个命令通常会提供一个Help文档。我们思考一下,如果这个命令的实现代码是一个命令类,那么这个命令类中会包含文档的相关信息,在这个类中把文档的相关信息作为一个组别,抽象成为一个内部的类来使用,从而这个内部的文档类就成为了一个嵌套类。
  2. 添加了封装:假设有一个A类,A类中的某成员变量和方法是private的,而B类恰恰需要访问A中的该成员变量和方法,那么将B类嵌入到A类中,B类就可以访问到A类中该private的成员变量和方法了。
  3. 增加了可读性和可维护性:在一个顶级类中嵌套类,使代码更靠近使用它的地方。

内部类(非静态嵌套类)

内部类作为封闭类的一个成员,是非静态嵌套类(没有用static关键字声明),它与该类创建的实例相关联,是该类创建的实例的一部分,所以在该内部类中不能声明static的成员(包括变量和方法)。为什么呢?因为在类中被static声明的成员将会只有一个副本存在,无论这个类实例化了多少个对象。内部类作为封闭类实例化对象的一部分,其应该存在很多个副本,所以其内部不能使用static来修饰任何成员。

内部类的实例对象存在于封闭类的实例对象中,其可以访问封闭类的所有属性和方法。

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

在实例化一个内部类对象之前,必须先实例化其封闭类对象。

OuterClass OuterObject = new OuterClass();
OuterClass.InnerClass InnerObject = OuterObject.new InnerClass();

两个特殊的嵌套类:匿名类和本地类(后续再介绍)

静态嵌套类

静态嵌套类与封闭类相关联,像静态方法一样,静态嵌套类不能直接访问其封闭类定义的实例变量和方法,而只能通过对象引用来使用它们,但是可以访问其类变量和静态方法。

事实上,一个静态嵌套类可以看做是一个顶级类,为了包装方便而嵌套在另一个顶级类中。静态嵌套类的实例化方式和其他顶级类实例化的方式是一样的。

StaticNestedClass StaticNestedObject = new StaticNestedClass();

静态内嵌类只能通过对象引用来访问封闭类实例化之后对象的成员变量和方法,静态内嵌类可以访问封闭类的类变量和静态方法。
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);
    }  
}

内部类的序列化问题

后续介绍