内部类与嵌入类详解
静态嵌入类
基础
声明形式:static class
嵌入类对象创建格式:外部类类名.内部类类名 xx = new 外部类类名.内部类类名()
static作用:声明C不是T的内部类,是外部顶级类,只是嵌入T中,被T单方面使用。
特性
- 不存在顶级外部嵌入类
- 只可在顶级外部类中(内部类中不可定义)、静态嵌入类中、接口中定义【不可定义在接口方法的default实现中,default实现只可使用局部内部类】
解析
官方解释
一个独立类C类嵌入在T类的类体中(单独拿出来写成C.java也是可以的)。正如T的静态方法中没有T的当前实例成员变量一样,C也没有T的当前实例,也没有任何词法封闭类的实例。
个人理解
- 嵌入类只是精简了package结构,使用上与非内部类是一样的——访问T类实例成员变量需要创建实例对象
- 本质区别——非静态的内部类在编译完成之后会隐式保存其外围类引用,指向外围类的实例。嵌入类没有。
特殊情况
① interface的成员类——是隐式的static
静态嵌入类
② 类中的interface成员永远不是内部类
内部类
分类
①成员内部类(non-static 成员类)
②局部内部类(本地类)
③匿名内部类(匿名类)(也是局部内部类的一种,受到局部类规则的限制)
特性
三者共性
- 内部类都不能显式、隐式声明static成员,除非该成员是静态常量变量,即
static final
变量 - 内部类虽然不能声明static成员(除static final)但仍然可以继承外部类的非final变量的静态成员【我个人认为更准确的是可使用外部类的static成员,虽然自己不能声明】
An inner class may inherit
static
members that are not constant variables even though it cannot declare them.
——https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
- 内部类的类名不能与包含它的外部类同名
- 深度内部类允许存在,当然深度static嵌入类也允许存在。
成员内部类特性
- 可访问外部类的所有成员。
①成员内部类&&②局部内部类
- 均可使用
final
修饰,也可使用访问修饰符PPPP
- 均可以声明为abstract类,即二者可被顶级外部、内部类继承(局部类被继承有继承范围限制,只可在其局部范围内——只可能被同为局部内部类继承)
局部、匿名内部类特性
- 二者均不能使用访问修饰符,但是局部类仍可显式继承类和实现接口,而匿名类被所创建的对象的类/接口限制——要么是一个类的子类,要么是一个接口的实现子类。
- 静态环境下的局部类&匿名类(静态方法,静态初始化器,静态变量初始化表达式)不能使用外部类的实例成员变量,否则发生编译错误。但可引用局部类所处静态环境周围的局部变量,但必须用final修饰。【同样适用于lambda表达式主体】
- 局部内部类使用未在内部类中声明的任何外部函数的局部变量、形式参数或异常参数都必须声明为final或有效的final(4.12.4),并且需要在内部类主体前赋值,否则在尝试使用时将发生编译时错误。【原因:局部类、匿名类得到的是外部类、外围函数的局部变量的引用,而不是实际对象,故为了防止内部类获取到的引用值发生改变,必须使用final让该引用不可变。】
- 匿名类内部不能定义任何构造函数、静态成员、静态方法和静态类,其他均可。是唯一无构造函数的类。
- 匿名内部类为局部内部类的一种,所以局部内部类的所有限制同样对匿名内部类生效。
内部类访问外部类原理
实质
构建成员内部类实例对象的时候会从构造器传入一个指向外部类实例对象的引用。故此可以访问
实例
class Outer{
class Inner{
}
}
反编译后:public com.inbreeze.inner.Outer$Inner(com.inbreeze.inner.Outer);
虽然源码定义的内部类构造器是无参的,但在编译时,编译器会隐式声明添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的 Outer this&0 指针便指向了外部类对象,因此可在成员内部类中随意访问外部类成员
实例详解
成员内部类
内部类对外部类成员访问没有任何限制。
内部类调用外部类实例变量及实例方法,不需new外部类实例。
注:内部类构造器被编辑器隐式声明外部类引用的参数,故内部类实例隐式持有外部类实例引用。
成员内部类访问外部类案例
//外部类
class Outer {
private int id = 10;
private void outerMehthod1() {
//外部类调用内部类实例方法,需要this.new Inner()创建内部类实例
Inner inner = this.new Inner();
inner.innerMethod1();
//可简写为:
//this.new Inner().innerMethod1();
}
private void outerMehthod2() {
System.out.println("OuterTest2......");
}
//内部类
public class Inner {
//内部类的id属性,不是外部类的id属性,也没有继承
//Inner实例变量
private int innerId = 100;
private int outerId = Outer.this.id;
//Inner实例方法
public void innerMethod1() {
System.out.println(Inner.this.innerId);
//内部类调用外部类实例变量及实例方法,不需要new外部类实例
//因为,外部类实例一定先于内部类实例创建
//Inner实例依赖于Outer实例创建而创建
System.out.println(Outer.this.id == outerId);
Outer.this.outerMehthod2();
}
}
}
public class Test {
public static void main(String[] args) {
//创建外部类实例对象outer
Outer outer = new Outer();
//通过外部类实例.new 创建内部类实例inner
Outer.Inner inner = outer.new Inner();
}
}
成员内部类被外部类继承案例
class Outer {
Outer(){
System.out.println("Outer...");
}
class Inner{
Inner(){
System.out.println("Inner...");
}
}
}
class InheritInner extends Outer.Inner {
// InheritInner() 无参构造不能通过编译
//一定要加上被继承内部类的外部类实例形参
InheritInner(Outer outer) {
//必须调用,且是第一行
outer.super();
System.out.println("InheritInner...")
//解释:super与this调用者都是当前类,也就是InheritInner类
//outer.super(); == outer.new Inner();
//调用当前类InheritInner的父类Inner的构造函数,这个构造函数是outer内部类的构造函数
}
//psvm
public static void main(String[] args) {
Outer outer = new Outer();
InheritInner obj = new InheritInner(outer);
}
}
//console
Outer...
Inner...
InheritInner...
局部内部类
位置
在方法、初始化器、构造函数、字段初始化表达式中【static、non-static环境均可定义】
作用区域
不能跨{}
使用,只有这个{}
内部可以使用,继承。
static环境下局部类案例
//顶级外部类
public class Outer{
//静态常量变量、静态变量
static final int a3 = 0;
static int a2 = 0;
//实例变量
int a1 = 0;
//static函数,静态环境
static void chineseStatic() {
//被内部类使用的外围局部变量 必须在局部内部类类体前赋值,定义清晰
final int i = 1;
int b = 10;
//static环境下的局部内部类
class inner1 {
//局部内部类不是顶级外部类,不能定义任何静态变量、静态方法、和静态嵌入类,其他的都可以
//可拥有静态常量变量
static final int i1 = 10;
//内部类不能拥有static静态变量
//局部内部类实例变量可被外部类、外围函数的局部变量赋值
//【但不可改变外部类局部基本数据类型变量值,因为可用的都是final定义的】
int i2 = i + b + i1 + 1 + a2 + a3;
//内部类的实例函数
public void say(){
System.out.println(i2);
}
}
inner1 inner1 = new inner1();
inner1.say();
}
匿名内部类
作用
继承某类或是实现某接口二者只能选一,并同时创建实例对象,可重写类、接口的原有函数,还可以写自己的(子类)实例方法、实例变量。
注:匿名内部类不能定义,任何静态变量、静态方法、和静态嵌入类及构造函数,其他都可以
位置
可以是传入的形式参数staticMethod1( new MyInterface(){ } );
可以是定义的普通对象MyClass1 myClass1 = new MyClass(){ };
实现接口继承类的匿名类实例
//接口
interface I1 {
void test();
}
//类
class C1 {
}
public class Test {
public static void main(String[] args) {
//匿名的接口I1的完全实现类
I1 i1 = new I1() {
//只允许static final静态常量变量存在
static final int a = 1;
//自定义的实例变量
int i = 10;
//自定义的实例函数
public void inherit() {
System.out.println("MyMethod" + i);
}
@Override
public void test() {
System.out.println("InterfaceMethod...");
}
};
i1.test();
//匿名的类C1的子类
C1 c1 = new C1() {
//只允许static final静态常量变量存在
static final int a = 1;
//自定义的实例变量
int i = 10;
//自定义的实例函数
public void say() {
System.out.println("dddd" + i);
}
};
}
}
使用场景
1.每个内部类都能独立的继承一个接口的实现,无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
3.方便编写事件驱动程序、线程代码
参考博文
chenssy:java提高篇(十)-----详解匿名内部类
菜鸟教程:Java 内部类详解
The Java® Language Specification Java SE 8 Edition