定义
内部类,即定义在类内部的类。这里指的类的内部,可以是作为类的成员存在,也可以存在于类的其他成员中。
分类
在上面的定义中,可能说得很模糊,但是那就是分类的依据。首先,内部内根据是否有static关键字修饰而可以区分为静态内部类和非静态内部类。而非静态内部内又可以根据其出现的位置细分为三种,作为外部类的成员存在的称作为 成员内部类; 定义在方法中的叫做方法内部类, 又称作局部内部类(下面成为方法内部类); 另外还有一种没有名称的内部类叫做匿名内部类。
上述关系可以下述列表表示
- 静态内部类
- 非静态内部类
- 成员内部类
- 方法内部类
- 匿名内部类
上面各种内部类都可以继续在内部嵌套定义其他内部类,但是有一些限制,具体细节在下面详解中会阐明。
各种内部类详解
静态内部类
静态内部类的声明如下代码所示:
static class StaticInnerClass {
// 一些属性
// 构造方法
// 一些其他方法
// 甚至一些内部类
}
创建静态内部类的实例:
StaticInnerClass sic = new TestInnerClass.StaticInnerClass();
在能够访问static成员的地方,还可以简化一下:
StaticInnerClass sic = new StaticInnerClass();
有几点需要注意:
- 静态内部内作为外部类的成员存在,故可以有四种权限状态。
- 关于静态内部类内部成员的权限和普通外部类一样,同样有四种可能:private, public, protected和默认。
- 在静态内部类内部,访问外部成员的规则也和普通静态方法一样,即只能访问外部的静态成员,而不能访问实例成员。
- 在静态内部类内部,可以继续创建上述四种内部类。
成员内部类
成员内部类的声明如下代码所示:
class FieldInnerClass{
// 一些属性
// 构造方法
// 一些其他方法
// 甚至一些其他内部类
}
特别注意如何创建成员内部类的实例:
FieldInnerClass fic = new TestInnerClass().new FieldInnerClass();
成员内部类的一般性质和静态内部类类似,除了下面的区别:
- 相比静态内部类,成员内部类类似于实例方法,可以访问类中所有的成员,即没有静态限制。
- 在成员内部类里面声明的静态成员必须是final的。
- 在成员内部类可以继续定义内部类,但是由于上面第二条,不能创建静态内部类。即在整个内部类中都不能出现static关键字。
请注意静态内部类和成员内部类访问外部类的成员时对其final性质没有要求,这与下面两种内部类不同。
方法内部类
顾名思义,即定义在方法中的内部类。其作用域也仅限于该方法。具体形式如下代码所示:
public void MethodName() {
// 一些变量定义
// 一些其他操作
class ClassName {
// 一些变量定义
// 一些方法定义
}
// 一些关于内部内的操作
}
关于方法内部类有以下几点需要注意:
- 方法内部类自身不能有任何权限修饰符,类似方法中的变量一样,不能用权限修饰符修饰,否则报错。
- 方法内部类自身不能使用static修饰,即使其存在于静态方法中。和第一条一样,目前在方法中还没出现过static变量。
- 方法内部类中的字段可以是静态的(即由static修饰),但是必须是final的(必须由final关键字修饰)。
- 方法内部类的成员的权限修饰符无效,意思是不管方法内部类的成员权限如何,都能够也只能够在其所属的方法内部访问。即使方法内部类的属性是private的,在其所属方法内部也可以访问。
- 与外部类不同的是,方法内部类必须声明后,才能在方法中使用。
- 方法内部类可以访问方法中的变量,在JDK8以前必须要求是明确使用final修饰的常量,但是在JDK8及以后,可以省去final修饰符,但其本质还是常量,所以不能对其进行修改操作。
- 方法内部内访问外部类的成员时,是根据其是否是静态方法有区别,具体规则差异相同于静态内部类和成员内部类的差异。
匿名内部类
匿名内部类没有类名,常作为方法的实参。举一个简单的例子:
// 接口定义
private interface Action {
void doAction();
}
// 其他方法
public static void doAction(Action action) {
action.doAction();
}
public static void testAnonymousInnerClass() {
final int e = 100;
// 匿名内部类的定义
doAction(new Action() {
// 一些变量
@Override
public void doAction() {
// 具体实现
}
});
}
关于匿名内部类有以下几点需要注意:
- 匿名内部类实例只能使用一次,类加载后立马创建实例即被使用,方法调用完毕立马销毁回收。
- 匿名内部类中可以访问方法中的成员,与方法内部类一样,JDK8以前必须是显示使用final修饰的常量,JDK8及其以后可以省略掉关键字。
- 访问外部类的成员时,其受到方法是否是静态的影响。
- 匿名内部类中不能出现static关键字。
- 与前面三种内部类不同的是,其不能拥有构造方法,因为其连类名都没有。
- 和方法内部类一样,内部属性或方法的修饰符都无效,或者说无意义,因为加不加修饰符不影响调用。
为什么方法内部类和匿名内部类访问外部数据(不是外部类的成员)时要求是常量呢?
方法中的局部变量在栈上分配的内存,方法内部类或匿名内部类的实例存放在堆区。在类加载后内部类中存放的是备份,为了备份与原变量保持一致,所以干脆规定方法中的局部变量为常量。
使用目的和场景
经过上面的学习,了解了内部类的使用,但是为什么要使用内部类,也就是说内部类有何存在意义。
我们经常在某个类中需要临时使用一种结构化的数据,但是在其他类中有不会使用到,此时不妨定义为内部类,在其内部使用即可,可以根据该结构化数据作用域的范围,选择合适的内部类定义,比如只在某个方法中使用,即可定义为方法内部类,如果在多个方法中都会使用到,即可定义为静态内部类或成员内部类。
内部类与外部类是独立的关系,可以使用内部类解决不能多继承的问题,可以将多个类定义为其中某个类的内部类,然后单继承实则上实现了多继承。
参考链接:
鸣谢:
https://juejin.im/post/5a903ef96fb9a063435ef0c8#heading-12
https://www.cnblogs.com/chenssy/p/3388487.html