内部类在 Java 里面算是非常常见的一个功能了,在日常开发中我们肯定多多少少都用过,这里总结一下关于 Java 中内部类的相关知识点和一些使用内部类时需要注意的点。
从种类上说,内部类可以分为四类:普通内部类、静态内部类、匿名内部类、局部内部类。我们来一个个看:
普通内部类
这个是最常见的内部类之一了,其定义也很简单,在一个类里面作为类的一个字段直接定义就可以了,例:
public class InnerClassTest {
public class InnerClassA {
}
}
在这里 InnerClassA 类为 InnerClassTest 类的普通内部类,在这种定义方式下,普通内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象,我们在创建上面代码中的 InnerClassA 对象时先要创建 InnerClassTest 对象,例:
public class InnerClassTest {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;
public InnerClassTest() {
// 在外部类对象内部,直接通过 new InnerClass(); 创建内部类对象
InnerClassA innerObj = new InnerClassA();
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
System.out.println("其内部类的 field1 字段的值为: " + innerObj.field1);
System.out.println("其内部类的 field2 字段的值为: " + innerObj.field2);
System.out.println("其内部类的 field3 字段的值为: " + innerObj.field3);
System.out.println("其内部类的 field4 字段的值为: " + innerObj.field4);
}
public class InnerClassA {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;
// static int field5 = 5; // 编译错误!普通内部类中不能定义 static 属性
public InnerClassA() {
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
System.out.println("其外部类的 field1 字段的值为: " + field1);
System.out.println("其外部类的 field2 字段的值为: " + field2);
System.out.println("其外部类的 field3 字段的值为: " + field3);
System.out.println("其外部类的 field4 字段的值为: " + field4);
}
}
public static void main(String[] args) {
InnerClassTest outerObj = new InnerClassTest();
// 不在外部类内部,使用:外部类对象. new 内部类构造器(); 的方式创建内部类对象
// InnerClassA innerObj = outerObj.new InnerClassA();
}
}
这里的内部类就像外部类声明的一个属性字段一样,因此其的对象时依附于外部类对象而存在的,我们来看一下结果:
我们注意到,内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段,后面我们将从源码里面分析具体的原因。
我们下面来看一下静态内部类。
静态内部类
我们知道,一个类的静态成员独立于这个类的任何一个对象存在,只要在具有访问权限的地方,我们就可以通过 类名.静态成员名 的形式来访问这个静态成员,同样的,静态内部类也是作为一个外部类的静态成员而存在,创建一个类的静态内部类对象不需要依赖其外部类对象。例:
public class InnerClassTest {
public int field1 = 1;
public InnerClassTest() {
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
// 创建静态内部类对象
StaticClass innerObj = new StaticClass();
System.out.println("其内部类的 field1 字段的值为: " + innerObj.field1);
System.out.println("其内部类的 field2 字段的值为: " + innerObj.field2);
System.out.println("其内部类的 field3 字段的值为: " + innerObj.field3);
System.out.println("其内部类的 field4 字段的值为: " + innerObj.field4);
}
static class StaticClass {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;
// 静态内部类中可以定义 static 属性
static int field5 = 5;
public StaticClass() {
System.out.println("创建 " + StaticClass.class.getSimpleName() + " 对象");
// System.out.println("其外部类的 field1 字段的值为: " + field1); // 编译错误!!
}
}
public static void main(String[] args) {
// 无需依赖外部类对象,直接创建内部类对象
// InnerClassTest.StaticClass staticClassObj = new InnerClassTest.StaticClass();
InnerClassTest outerObj = new InnerClassTest();
}
}
结果:
可以看到,静态内部类就像外部类的一个静态成员一样,创建其对象无需依赖外部类对象(访问一个类的静态成员也无需依赖这个类的对象,因为它是独立于所有类的对象的)。但是于此同时,静态内部类中也无法访问外部类的非静态成员,因为外部类的非静态成员是属于每一个外部类对象的,而本身静态内部类就是独立外部类对象存在的,所以静态内部类不能访问外部类的非静态成员,而外部类依然可以访问静态内部类对象的所有访问权限的成员,这一点和普通内部类无异。
匿名内部类
匿名内部类有多种形式,其中最常见的一种形式莫过于在方法参数中新建一个接口对象 / 类对象,并且实现这个接口声明 / 类中原有的方法了:
public class InnerClassTest {
public int field1 = 1;
protected int field