Java 内部类的使用

505 阅读4分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

Java:内部类的使用

  1. 在外部类内部使用内部类

在外部类内部使用内部类时,与平常使用普通类没有太大的区别。唯一存在的一个区别是:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。

  1. 在外部类以外使用非静态内部类

如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用 private 访问控制权限,private修饰的内部类只能在外部类内部使用。对于使用其他访问控制符修饰的内部类,则能在访问控制符对应的访问权限内使用。

  • 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
  • 使用 protected 修饰的内部类,可被与外部类处于同一个包中的其他类和外部类的子类所访问。
  • 使用 public 修饰的内部类,可以在任何地方被访问。

在外部类以外的地方定义内部类(包括静态和非静态两种)变量的语法格式如下:

OuterClass.InnerClass varName

在外部类以外的地方使用内部类时,内部类完整的类名应该是 OuterClass.InnerClass。如果外部类有包名,则还应该增加包名前缀。

由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类以外的地方创建非静态内部类实例的语法如下:

outerInstance.new InnerConstructor()

在外部类以外的地方创建非静态内部类实例必须使用外部类实例和 new 来调用非静态内部类的构造器。

非静态内部类的构造器必须使用外部类对象来调用。

在外部类以外的地方创建非静态内部类的子类,则尤其要注意上面的规则:非静态内部类的构造器必须通过其外部类对象来调用

当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。

团子注:存在顺序是,外部类对象 -> 非静态内部类的构造方法 -> 非静态内部类的子类的构造方法

下面程序定义了一个子类继承了 Out 类的非静态内部类 In 类。

public class SubClass extends Out.In {
    // 显式定义 SubClass 的构造器
	public SubClass (Out out) {
	    // 通过传入的 Out 对象显式调用 In 的构造器
	    out.super("hello");
	}
}

非静态内部类 In 类的构造器必须使用外部类对象来调用,代码中 super 代表调用 In 类的构造器,而 out 则代表外部类对象。

团子注:确实有点怪。用外部类实例,调用了 super,居然指向的是内部类 In 的构造方法。

非静态内部类 In 对象和 SubClass 对象都必须持有指向 Outer 对象的引用,区别是创建两种对象时传入 Out 对象的方式不同:当创建非静态内部类 In 类的对象时,必须通过 Outer 对象来调用 new 关键字;当创建 SubClass 类的对象时,必须使用 Outer 对象作为调用者来调用 In 类的构造器。

注意:非静态内部类的子类不一定是内部类,它可以是一个外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象。也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象

  1. 在外部类以外使用静态内部类

因为静态内部类是外部类类相关的,因此创建静态内部类对象时无须创建外部类对象。在外部类以外的地方创建静态内部类实例的语法如下:

OuterClass.InnerClass i = new OuterClass.InnerClass();

因为调用静态内部类的构造器时无须使用外部类对象,所以创建静态内部类的子类也比较简单,下面代码就为静态内部类 StaticIn 类定义了一个空的子类。

public class StaticSubClass extends StaticOut.StaticIn {}

当定义一个静态内部类时,其外部类非常像一个包空间。

注意:相比之下,使用静态内部类比使用非静态内部类要简单很多,只要把外部类当成静态内部类的包空间即可。因此当程序需要使用内部类时,应该优先考虑使用静态内部类。