如果你看过一些JDK和框架源码的话,就经常会发现一般在类的定义中,都会再定义一些其他的类,这些类也同样会被编译成字节码文件,这样的类就被叫做 内部类 ,按照一般的分法,大致可以分为以下四类:
接下来会针对这四种内部类进行详细讲解,旨在解释这些类的特点和应用场景,如果你懒得看的话,可以直接翻到最底下看总结
我们预先定义好一个类,接来下的所有操作都会在这个类中执行:
class Demo { public int pubVal = 1; private int priVal = 1; public static void staticMethod() {} public void publicMethod() {} private void privateMethod() {} } 复制代码
从名字就可以看出,这种内部类是作为类的成员而存在的,其定义位于类的内部,可以类比为成员变量来理解
class Demo { // ... class InternalClass { } // ... } 复制代码
这样就是一个成员内部类了
首先是类定义的约束,实际上,成员内部类的类定义没有任何约束(不涉及static,因为这属于静态内部类的范畴),不仅可以将内部类声明为public、private的,甚至可以将其声明为一个abstract抽象类和interface接口,以及继承外部类也是允许的,其定义是十分宽松的
接着是内部属性和方法,这里就有一些约束了:
也就是说,成员内部类不能存在静态属性和方法,这么做也是符合成员变量的含义
最后是访问上的约束,这里十分重要:
这里的限制就和成员方法是类似的,在理解时可以进行类比
先来说声明,以本文中程序为例,我们可以通过下面的方式来声明一个内部类:
Demo.InternalClass demo 复制代码
注意,如果你将这个内部类声明为private的,外部类依然可以访问,但是外部类之外的其他类是无法访问到的
接着就是通过new来创建,我们在外部类的任何 非静态方法 中,都可以通过new来进行创建,具体格式如下:
Demo.InternalClass demo = new Demo.InternalClass(); 复制代码
同样地,我们可以在外部类中,通过内部类的实例来访问其内部的私有变量和方法
我们刚花了大力气来讲解成员内部类,而局部内部类和成员内部类十分类似,可能一些相同的地方我就一笔带过了
局部内部类位于外部类成员方法的内部,可以类比局部变量
public void publicMethod() { class InternalClass { } } 复制代码
这里的约束基本和成员内部类类似,我就单独说一些不同的地方:
其余的基本和成员内部类类似,把局部内部类当作成员内部类的局部变量版本就好理解了,比如也拥有外部类的指针,使用方法和成员内部类一致
既然是局部内部类,就只能在声明类的方法处来使用,声明和使用方式如下:
public void publicMethod() { class InternalClass { } InternalClass test = new InternalClass(); } 复制代码
同样地,我们依然可以无条件访问内部类中定义的私有属性
这种内部类应该是我们使用的最多的一种,有时候甚至我们已经使用过了却没有发现
匿名内部类没有类的声明,会隐式地继承一个类或实现一个接口
概念比较抽象,我们直接看是如何定义的,这里我们有一个接受一个对象参数的方法:
private void privateMethod() { new Demo() { public int newVal = 20; @Override public void publicMethod() { super.publicMethod(); } }; } 复制代码
这里的Demo可以换成任意一个类或者接口,你会发现这个类没有名字,所以被叫做匿名内部类
如果Demo是一个普通类,则匿名内部类相当于这个类的子类;如果Demo是一个接口或者抽象类,则这个匿名内部类相当于接口或抽象类的实现
想要理解匿名内部类的约束,就需要将整个匿名内部类不要当成一块程序逻辑来看,而应该当成一个对象来处理,整块匿名内部类完全可以当成一个对象,可以调用对象的方法、属性等等
其主要的约束有以下这些:
这里就不进行过多讲解了,完全和普通对象的用法一致,这里就举两个简单的例子:
int val = new Demo() { private int newVal = 20; @Override public void publicMethod() { super.publicMethod(); } }.newVal; Demo demo = new Demo() { private int newVal = 20; @Override public void publicMethod() { super.publicMethod(); } }; 复制代码
静态内部类相当于static修饰的成员内部类,可以当作静态变量来理解
class Demo { // ... static class InternalClass { } // ... } 复制代码
这里的约束就和之前的有很大不同了,如下:
没错,广义上,静态内部类根本没有约束
使用有以下两种情况:
在外部类的方法中使用时,通过如下语句来创建内部类对象:
InternalClass test = new InternalClass(); 复制代码
在外部类之外的其他类可以通过下面的语句来创建内部类对象:
Demo.InternalClass test = new Demo.InternalClass(); 复制代码
最后,为大家总结了一张表,基本上内部类的知识点都在这里了:
成员内部类 | 局部内部类 | 匿名内部类 | 静态内部类 | |
---|---|---|---|---|
类比 | 成员变量 | 局部变量 | 子类或接口的实现类 | 静态变量 |
类定义声明位置 | 类的内部 | 作用域内部 | 任意对象可能出现的位置 | 与成员内部类一致 |
类修饰符 | 除static外任意 | 只允许用abstract修饰 | 无法修饰 | 任意的修饰符均可 |
变量和方法修饰符 | 除static外任意(但是可以用static final修饰变量) | 与成员内部类一致 | 与成员内部类一致 | 任意修饰符均可 |
外部类的访问权限 | 均可 | 均可 | 均可 | 均可 |
对象声明位置 | 只能在外部类的成员方法中,和外部类之外的其余类中声明(如果在外部类之外,要看类访问控制是否允许) | 只能在作用域中声明 | 无需声明 | 任意位置均可 (如果在外部类之外,要看类访问控制是否允许) |
对象创建位置 | 只能在外部类的成员方法中创建 | 只能在作用域中创建 | 立即创建 | 任意位置均可 (如果在外部类之外,要看类访问控制是否允许) |
是否允许有内部类的内部类 | 可以,但是不允许有静态内部类 | 与成员内部类一致 | 与成员内部类一致 | 可以 |