Java内部类包括非静态内部类,局部内部类,匿名内部类,静态内部类四种。前三种非常类似,它们都叫内部类,它们持有对外部对象的引用,最后一种也称为嵌套类,它和外部类没有联系,它更像仅仅写在类内部的正常类。
内部类是一种完善Java单继承机制从而取代多继承机制的产物,通过内部类可以进行灵活的继承而不影响外部类。所以它实际上有很多限制,需要用特殊的语法来进行保证,尽管这些语法可能看起来非常奇怪和不自然。幸运的是,日常对于这些边缘语法使用的也非常少。
四种内部类
非静态内部类
由于内部类实际上比较相似,所以这边将会展开非静态内部类讨论,其他三种只讨论和它的区别。
非静态内部类的共同特性:
- 可访问外部类的所有成员
- 不可以包含static成员,猜测static从设计上是属于类的,因为与原有设计冲突而诞生的规则。
基本形式
public class Main {
public static void main(String[] args) {
A a = new A();
A.B b = a.getB();
b.run();
}
}
class A {
private int val = 10;
public A() {
System.out.println("A()");
}
public B getB() {
return new B();
}
// 隐藏内部类细节
class B {
public B() {
System.out.println("B()");
}
public void run() {
// 可访问外围类的所有成员
System.out.println("B run " + val);
}
}
}
上述是一种内部类最常见的形式,也体现了内部类的两个用途:
- 隐藏内部类细节,作为一种代码的组织形式
- 内部类可以访问外部类的所有成员。
持有外部类的引用
内部类之所以可以访问外部类的所有成员,是因为内部类实际上在内部保存了对外部对象的一个引用,这一点,编译器会帮我们完成。
于是,我们需要记得是,在外部类对象不存在之前,我们不可能新建内部类对象。
特殊语法.new和.this
初始化外部类之前必须初始化内部类。这一点,引申出一种内部类的初始化特殊语法:
A a = new A();
A.B b1 = a.new B();
另外,由于类的嵌套,外部类和内部类中的this必然会产生冲突,所以Java又提供了补充规则,在内部类想访问外部类的对象时,可以使用:
A.this.run()
匿名内部类
匿名内部类是另一种非常常用的类。
class C {
protected int val;
public void run() {
System.out.println(val);
}
}
public class Main {
private static int b = 19;
public static void main(String[] args) {
final int a = 1;
C c = new C() {
// 实例初始化,相当于默认构造器
{
val = 10;
// 访问外部局部变量要求必须是final
System.out.println(a);
// 可以访问外围类的所有成员
System.out.println(b);
}
};
c.run();
}
}
上述代码,本质上是创建了一个继承自C的匿名类的对象。这也是匿名内部类的本质,它拥有非静态内部类的所有特性:
- 它可以访问外围类的所有成员
特殊区别:
- 访问外部局部变量,要求局部变量必须为final
- 由于匿名类没有名字,它也就无法重载构造器,替代的语法是使用代码块进行实例初始化
局部内部类
局部内部类指在代码块,方法中声明的局部类。
相同:
- 它可以访问外围类的所有成员
不同
- 只在作用域范围内有效
- 局部内部类和匿名内部类的唯一区别是,我需要这个内部类有名字,方便在局部复用
静态内部类(嵌套类)
嵌套类与上述内部类区别比较大,它其实可以看错独立于外部类的类,唯一的限制是它被组织在外部类中而已。所以:
- 静态内部类和外部类没有联系,没有持有外部类引用
- 静态内部类只可以访问外部类的静态成员
- 静态内部类可以包含静态成员