在Java中我们在一个类的内部再定义一个类,如下所示:
class OuterClass {
...
class NestedClass {
...
}
}
那么在上面的例子中我们称OuterClass为外围类(enclosing class),里面的那个类称之为嵌套类(Nested Class).
嵌套类可以分为两种,静态的和非静态的,即静态嵌套类和非静态嵌套类。非静态嵌套类又叫做内部类(Inner Class)。我们通常所说的静态内部类其实是不严格的,严格的说应该叫做静态嵌套类(Static Nested Class)。
class OuterClass {
...
class InnerClass {
...
}
static class StaticNestedClass {
...
}
}
上述代码中的InnerClass就是内部类,StaticNestedClass就是静态嵌套类。
内部类与静态嵌套类虽然都是嵌套类,但在使用上是有一些区别的。
内部类
比如有如下内部类的定义,
class OuterClass {
...
class InnerClass {
...
}
}
OuterClass是InnerClass的外围类,InnerClass是OuterClass的内部类。内部类的实例对象都会绑定一个外围类的实例对象,并且InnerClass可以访问其所绑定的OuterClass的所有成员属性以及方法,包括私有成员属性以及方法。在InnerClass中通过OuterClass.this显式的引用其所绑定的OuterClass的实例。要实例化内部类InnerClass,必须首先实例化其外围类OuterClass,然后用如下的语法创建内部类的实例:
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
注意,上面写得是outerObject.new InnerClass(),而不是new OuterClass.InnerClass();
我们在执行代码OuterClass.InnerClass innerObject = outerObject.new InnerClass()的时候,其实做了两件事,一件事是创建一个内部类的实例innerObject,第二件事是让innerObject绑定outerObject作为其外围类的实例。这样innerObject就可以访问outerObject内的所有成员属性以及方法了。
那如果想直接跳过外围类去初始化内部类会怎么样呢?代码如下所示:
如果执行代码InnerClass innerObject = new InnerClass(),会出现如下的编译错误:
No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass).
编译器给出的错误提示很明确,就是我们没有外围类OuterClass的实例就去初始化内部类了。要写成x.new InnerClass()的形式,其中x是外围类OuterClass的一个实例对象。
静态嵌套类
有些人把静态嵌套类成为静态内部类,其实静态内部类这个称呼不严谨,因为内部类都是非静态的。静态嵌套类与内部类有很大的不同,静态嵌套类说到底就是一个静态类,只不过是其位置位于某个类的内部罢了。
假设有如下静态嵌套类的定义:
class OuterClass {
...
static class StaticNestedClass {
...
}
}
那么我可以像正常使用一个一般的静态类那样使用一个静态嵌套类,只不过要通过其外围类的名字来访问静态嵌套类的名字。所以,外围类更像是静态嵌套类的命名空间。比如要获取静态嵌套类,要使用OuterClass.StaticNestedClass。
如果要创建静态嵌套类的实力对象,使用如下的语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
由于静态嵌套类的本质就是一个静态类,所以其实例对象的初始化不需要也不能像内部类那样需要绑定一个外围类对象。由于静态嵌套类没有像内部类那样绑定外部类对象,所以也就不存在静态嵌套类不能访问其外围类的成员这种说法。
如果我们像初始化内部类那样初始化静态嵌套类,也就是在创建静态嵌套类的时候给其绑定其外围类的实例对象,会怎么样呢?代码如下所示:
可以发现,如果我们在初始化静态嵌套类时强行给其绑定外围类的实例对象,编译器就会报错:
Illegal enclosing instance specification for type OuterClass.StaticNestedClass
我们给静态嵌套类OuterClass.StaticNestedClass指定了非法的外围类的实例。
综上所述,虽然内部类和静态嵌套类都属于嵌套类,但是二者有本质区别:内部类的实例化对象需要绑定一个外围类的实例化对象,而静态嵌套类的实例化对象不能也无法绑定外围类的实例化对象。