目录
局部内部类可以有的成分 及 访问外部类的格式 及 局部内部类在类中可以出现的位置:
接口的匿名内部类 及 访问父接口、外部类的格式(重点,使用最多!)。
静态内部类的特点 (重点! 使用较多!):
- 静态内部类是定义在 外部类 中的成员位置,与 外部类 中的静态代码块、静态变量、静态常量、静态方法 为同一级别 的内部类。有static修饰。
- 与其他类一样,类中该有的成分,静态内部类都可以有!
- 可以在静态内部类中直接访问类 外部类 中的所有静态成员,包括私有的。但是不能直接访问类中的实例成员!(除非在静态内部类的成员方法中创建 外部类的对象。)
- 可以和静态方法、静态变量、静态常量、一样 添加任意的访问修饰符,因为静态内部类就是属于类中的一个静态成员!
- 静态内部类的作用域和其他 成员一样,为整个类体。
- 在其他类中 创建静态内部类的对象的语法 :外部类名.静态内部类名 对象引用名 = new 外部类名.静态内部类构造器(); (或者变相的提供方法,给调用者返回静态内部类的对象即可。)
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循的是就近原则。如果想访问外部类的成员,则可以使用 < 外部类名.静态成员> 去访问。
- 外部类访问静态内部类的静态成员,直接用 静态内部类名.静态成员。访问静态内部类的实例成员,则 静态内部类 对象引用 = new 静态内部类构造器();再访问即可!
- 静态内部类可以定义:构造器、成员方法、成员变量、成员常量、代码块、静态内部枚举、静态内部接口、静态内部注解(几乎没用过)。(接口中也可以定义静态内部类中)
- 静态内部类应用场景:比如 定义了 一个 BigHospital 类(大医院类),又想在 Hospital 类中定义 Ophthalmology 类(眼科类)或者 Orthopedics类(骨科)或 Gynecology(妇科)等 ……
静态内部类中可以有的成分及访问外部类的格式:
(只要是一个类中可以有的,静态内部类都可以有!静态内部类的.class文件是属于外部类的.class文件里面,所以自然也就能创建外部类的对象访问外部类的实例成员,和直接访问外部类的静态成员!):
public class Outer{ {} static {} private static final String SC = "静态常量"; private static String sa = "静态变量"; private final String IC = "实例常量"; private String ia = "实例变量"; interface O_Interface{public abstract void m();} enum O_Enum{E;} @interface ONote{} public static String sM(){return "静态方法";} public String iM(){return "实例方法";} public static class Inter{ {} static {} interface I_Interface{public abstract void m();} enum I_Enum{E;} @interface INote{} private static final String SC = "静态内部类静态常量"; private static String sa = "静态内部类静态变量"; private final String IC = "静态内部类实例常量"; private String ia = "静态内部类实例变量"; public static String iSM(){ String sc = Inter.SC; return "静态方法";} public void iiM(){ System.out.println(Outer.SC); System.out.println(Outer.sa); System.out.println(new Outer().IC); System.out.println(new Outer().ia); System.out.println(sM()); System.out.println(new Outer().iM()); new O_Interface() { @Override public void m() { System.out.println("外部类的内部接口"); } }.m(); System.out.println("外部类的内部枚举O_Enum.E = " + O_Enum.E); } } } class Test{ public static void main(String[] args) { Outer.Inter inter = new Outer.Inter(); System.out.println("inter.getClass().getName() = " + inter.getClass().getName()); inter.iiM(); } }
test:
静态内部类注意事项:
- 静态内部类的的访问权限修饰符是什么,静态内部类的默认的无参构造器就是什么!!!
实例内部类的特点 (重点,使用较多):
- 实例内部类是定义在外部类的成员位置,并且没有用static修饰!!
- 实例内部类可以直接访问外部类的所有成员,包括私有的!
- 实例内部类和实例变量、实例常量、实例方法 一样,可以用任意修饰符修饰,因为它的地位就是一个成员,和实例变量、实例常量、实例方法的地位是一样,都是属于对象的!!
- 实例内部类的作用域,和外部类其他成员一样,为整个类体!
- 实例内部类中可以定义:构造代码块、构造器、实例方法、实例变量、实例常量、静态常量。(为什么可以定义静态常量?因为静态常量是在 常量池里面的!常量池里面的东西是可以被堆、栈、方法区 共享的(不是字符串常量池!!!))
- (实例内部类 不能定义 静态方法、静态变量、静态代码块、(因为对象在堆中的、堆中没有静态域 )静态内部枚举、静态内部接口、静态内部注解这些内容,一定义就报错!(堆中没有方法区,字节码文件是在加载到方法区中的!))
- 外部类访问实例内部类的静态常量,则直接 用 实例内部类名.静态常量即可。如果是在外部类的实例方法中 访问 实例内部类 中的 实例成员就需要创建对象,直接 [外部内名.]实例内部类名 对象引用 = new 实例内部类构造器();再访问即可。如果是在外部类的静态方法中 访问实例内部类中的实例成员 创建对象的方式就不一样 :创建对象的方式是: [外部类名.]实例内部类名 对象引用 = new 外部类构造器().new 实例内部类构造器();
- 如果在其他类中访问实例内部类创建对象的方式是:外部类名.实例内部类名 对象引用 = new 外部类构造器().new 实例内部类构造器(); (或者先创建外部类对象,再用对象.new 实力内部类构造器(); 或者 在外部类中 提供可以获取 实例内部类的对象的方法 也是一样的!)
- 如果外部类 和 实例内部类的实例成员重名的时候,实力内部类里面访问的话,默认遵循 就近原则,如果想访问外部类的实例成员,则可以通过 外部类名.this.实例成员 来访问即可。
- 如果实例内部类 想 访问外部类的 父类的成员的时候,则可以通过 外部类名.super.成员 即可访问。
- 实例内部类引用场景:比如定义了 一个 Person 类(人类) ,而由想在 Person 类 中定义一个 Heart 类(心脏)。这时候就可以考虑使用实例内部类 or 静态内部类 都是可以的。
实例内部类可以有的成分 及 访问外部类的格式:
public class Outer{ public static final String S_CONST = "外部类的静态常量"; public static String sVariable = "外部类的静态变量"; public final String I_CONST = "外部类的实例常量"; public String iVariable = "外部类的实例变量"; public static String sM(){return "外部类的静态方法";} public String iM(){return "外部类的实例方法";} protected interface O_Interface{public abstract void m();} protected enum O_Enum{ENUM;} private @interface O_Comment{} public class InstanceInter{ {} public static final String S_CONST = "实例内部类的静态常量"; public final String I_CONST = "实例内部类的实例常量"; public String iVariable = "实例内部类的实例变量"; public String iM(){return "实例内部类的实例方法";} public void access(){ System.out.println("this.getClass().getName() = " + this.getClass().getName()); System.out.println("Outer.S_CONST = " + Outer.S_CONST); System.out.println("Outer.sVariable = " + Outer.sVariable); System.out.println("Outer.this.I_CONST = " + Outer.this.I_CONST); System.out.println("Outer.this.iVariable = " + Outer.this.iVariable); System.out.println("Outer.super.equals(null) = " + Outer.super.equals(null)); System.out.println("sM() = " + sM()); System.out.println("Outer.this.iM() = " + Outer.this.iM()); new O_Interface() { @Override public void m() { System.out.println("实现外部类 内部接口"); } }.m(); System.out.println("外部类 内部枚举O_Enum.ENUM = " + O_Enum.ENUM); } } } class Test{ public static void main(String[] args) { // test Outer.InstanceInter instanceInter = new Outer().new InstanceInter(); instanceInter.access(); } }
test:
实例内部类注意事项:
- 实例内部类的的访问权限修饰符是什么,实例内部类的默认的构造器就是什么!!!(它创建实例内部类需要传入一个参数,不难看出这里很明显就是 Outer 的对象啊!所有的实例内部类是需要外部类的对象类创建对象的!)
局部内部类的特点 ( 重点,使用较少。 ):
- 局部内部类是定义在外部类的位置,可以定义在 if分支 、else if 分支、 else 分支、switch分支、 for 循环、while循环、do-while循环、成员方法、构造器、代码块、try语句体、catch语句体、finally语句体中!
- 局部内部类不能添加static 修饰!
- 不能添加访问修饰符,因为局部内部类的地位就是等同于局部变量,局部内部类是不能使用访问修饰符的。但是可以使用final修饰、且只能最多选择使用final修饰!,因为局部变量也可以使用fianl修饰!
- 局部内部类的默认无参构造器修饰符 是 缺省的!!
- 局部内部类的作用范围:仅仅是在局部内部类定义的代码块、分支、语句体 范围中!不能在本类中的其他代码块中、分支中、语句体中创建非当前局部内部类的对象!其他类也不能创建。局部内部类是属于当前作用范围!其他不能访问!
- 局部内部类访问外部类:如果局部内部类是在实例方法中、构造器中、构造代码块中定义 则可以直接访问外部类的所有成员(包括私有的)。如果局部内部类是在静态方法中、静态代码块中定义 则 可以直接访问外部类的静态成员(包括私有的)!如果想访问外部类的实例成员,则需要创建外部类的对象才能访问!(不能在构造代码块中、构造器中创建当前类的对象、会出现 StackOverFlowError 堆栈溢出错误!,在构造代码块中、构造器中定义的局部内部类里面也不能创建当前外部类的对象,也会出现 StackOverFlowError 堆栈溢出错误!但是可以在构造代码块中、构造器中创建非当前类的对象使用! )
- 外部类访问局部内部类中的内容:必须在和局部内部类在一个作用域的前提下,创建局部内部类对象即可访问!
- 如果外部类和局部内部类中的成员重名时,需遵循就近原则,如果想访问外部类的实例成员时,可以使用 外部类名.this.实例成员 才能访问。
- 如果局部内部类想访问外部类的父类的成员的时候:可以使用 外部类名.super.成员即可访问!
- 局部内部类中可以定义:实例变量、实例方法、实例常量、构造代码块、构造器、静态常量! (和实例内部类一样!)
- 局部内部类中不可以定义:静态方法、静态变量、静态代码块、静态内部枚举、静态内部接口、静态内部注解 这些内容!(和实例内部类一样!)
- 局部内部类应用场景:如果这个类不想被外部使用,且非要创建一个类才能完成功能的前提下。比如:创建一个 Bank (银行) 类 , 创建一个 takeNumer(取号) 的 成员方法, 然后再在 takeNumer方法里面 创建 一个 TackNumerMachine (取号机) 类。(使用场景极少,语法极偏,根据 特殊业务需求 决定)
局部内部类可以有的成分 及 访问外部类的格式 及 局部内部类在类中可以出现的位置:
class Test { public static void main(String[] args) { new Outer(); } } public class Outer { public static final String S = "Outer 静态常量"; public static String SI = "Outer 静态变量"; public final String IC = "Outer 实例常量"; public String ia = "Outer 实例变量"; public Outer() { final class Topo_0{} } { class Topo_1 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { System.out.println("this.getClass().getName() = " + this.getClass().getName()); System.out.println("Outer.S = " + Outer.S); System.out.println("Outer.SI = " + Outer.SI); System.out.println("Outer.this.IC = " + Outer.this.IC); System.out.println("Outer.this.ia = " + Outer.this.ia); System.out.println("Outer.super.equals(null) = " + Outer.super.equals(null)); } } new Topo_1().tSM(); } static { class Topo_2 { public static final String S2 = "静态代码块局部内部类静态常量2"; public final String SI2 = "静态代码块局部内部类实例常量2"; public String sa2 = "静态代码块局部内部类变量2"; public void tSM2() { System.out.println("this.getClass().getName() = " + this.getClass().getName()); System.out.println("S2 = " + S2); System.out.println("SI2 = " + SI2); System.out.println("sa2 = " + sa2 + "\n") ; } } new Topo_2().tSM2(); } public static void staticM() { final class Topo_3 { } } public void instanceM() { class Topo_4 { } } public void otherM() { if (true) { final class Topo_4 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } else if (true) { final class Topo_5 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } else { final class Topo_6 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } int i = 2; switch (i) { case 2: final class Topo_7 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } break; default: final class Topo_8 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } break; } int[] iArr = new int[0]; for (int i1 : iArr) { final class Topo_9 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } for (int j = 0; j < 1; j++) { final class Topo_10 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } while (i == 2){ final class Topo_11{final class Topo_8 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } }} } do { final class Topo_12{ final class Topo_8 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } }while (i == 2); try { System.out.println("try-catch-finally"); final class Topo_13{ public String topo_13; final class Topo_14 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { System.out.println(Topo_13.this.topo_13); } } } } catch (Exception e) { final class Topo_15{ public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } finally { final class Topo_16 { public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } synchronized (this){ final class Topo_17{ public static final String S = "局部内部类静态常量"; public final String SI = "局部内部类实例常量"; public String sa = "局部内部类变量"; public void tSM() { } } } } }
test:
匿名内部类的特点总结(重点 !使用最多!):
- 匿名内部类:顾名思义,就是没有名字的内部类。它和局部内部类很像,但是局部内部类有名字,匿名内部类没有名字。也可以说匿名内部类属于局部内部类,但它不是局部内部类!!!!
- 匿名内部类本质上还是一个类,同时还是一个对象!匿名内部类是在定义在外部类的局部外置,跟 局部内部类 能 定义的位置 完全 一样,换句话说,能定义局部内部类的局部位置就能定义匿名内部类!
- 创建匿名内部类的语法规则(比较特别):接收匿名内部类的类型可以是----> [父类or类or抽象类or接口or父接口 = ],一般都是 new 什么就拿什么接收对象,因为 多态的特性,可能会调用不到实现的成员or父类的成员。(原因 就是因为接收的类型 编译不到 调用的成员!往往是父类中没有此成员。而是new 后面的类型中的 独有的成员)
[父类or抽象类or类or接口or父接口 对象引用名 = ] new 类or抽象类or接口(形参列表){ 方法重写or实现 匿名内部类成员 };
- 匿名内部类在外部类的 静态方法、静态代码块 中 只能访问外部类的静态成员(包括私有的)。非静态成员需要创建外部类的对象才可以访问!!!!
- 匿名内部类在外部类的 实例方法、构造代码块(访问 实例变量时 没有 给初始值的时候一般都是默认值,因为构造代码块 先于 构造器中 初始化语句 执行,后于 静态代码块执行!!)、构造器中 既 可以访问外部类的静态成员(包括私有的),也可以访问外部类的实例成员!!!!
- 不能添加访问修饰符,因为匿名内部类的地位就是一个局部变量。但是 匿名内部类的对象引用可以添加 final 关键字。这样子 这个 对象引用名 指向的对象地址 就不能 再指向其他对象地址!!
- 匿名内部类的 隐式的默认的 构造器的访问修饰符 是 缺省 的!
- 匿名内部类的作用域:仅仅是在定义它的 方法 or 分支语句块 or 循环语句块 or 代码块中 的作用域中!,和局部内部完全一样!!!
- 其他类不能访问匿名内部类,因为匿名内部类的作用域是局部的,而不是整个类体的!!!
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则如果想访问外部类的成员,可以使用 外部类名.this.成员 才能访问!
- 如果匿名内部类想访问外部类的父类的成员的时候:可以使用 外部类名.super.成员 即可访问!
- 如果匿名内部类是一个接口的实现类,访问父接口的内容的时候和普通实现类访问的方式一样!
- 如果匿名内部类是一个类、抽象类的继承类的话、访问父类的内容和普通继承类访问的方式一样!
- 匿名内部类中可以定义:实例变量、实例方法、实例常量、构造代码块、静态常量!(由于匿名内部类没有类名,匿名内部类无法显式的编写构造器,那么也就意味着,匿名内部类没有构造器重载这个说法!(如果在有必要的情况下 想要 初始化实例变量可以使用构造代码块进行!(一般在匿名内部类中都是直接赋值很少先声明再初始化!)))
- 匿名内部类中不可以定义:静态方法、静态变量、静态代码块、静态内部枚举、静态内部接口、静态内部注解 这些内容!(和实例内部类、局部内部类一样!)
- 匿名内部类的应用场景:当需要使用到接口中成员的时候但是又不想去写实现类实现接口,并且 只想 使用一次,并且不想让其他人使用这次的对接口的实现时候,就可以使用匿名内部类来 实现接口并实现接口中的抽象方法来 解决(接口中的默认方法,如果有必要使用的情况下,可以选择性的重写!)。
- 或者当需要使用到抽象类中成员的时候但是又不想去写 子类去继承它,并且 只想 使用一次,并且不想让其他人使用这次的对抽象类的抽象方法的实现与抽象类成员的继承的时候,就可以使用匿名内部类来 继承抽象类并实现抽象方法来 解决。(抽象父类中的方法,如果有必要使用的情况下,可以选择性的重写!)
- 或者当需要使用到一个类中的方法的时候但是觉得它的这个方法不能满足功能需要但是又不想去写一个子类继承它,并且 只想 使用一次,并且不想让其他人使用这次的对父类的方法重写,就可以使用匿名内部类 来继承父类 来解决。(父类中的其他方法,如果有必要使用的情况下,可以选择性的重写!)
接口的匿名内部类 及 访问父接口、外部类的格式(重点,使用最多!)。
interface ParentInterface {
/*public static final*/ String P_CONST = "父接口中的静态常量";
/**
* 父接口中的抽象方法
*/
/*public abstract*/ void pInterfaceAbstractMethod();
/**
* 静态方法
*/
/*public */
static void pStaticMethod() {
String pConst = P_CONST;
System.out.println("JDK 1.8 (包括1.8)后 父接口中的静态方法 ");
}
/**
* 默认方法
*/
/*public*/
default void pDefaultMethod() {
pPrivateMethod();
System.out.println("JDK 1.8 (包括1.8)后 父接口中的默认方法 ");
}
/**
* JDK 1.9 (包括1.9)后 父接口中的私有方法
*/
private void pPrivateMethod() {
System.out.println("JDK 1.9 (包括1.9)后 父接口中的私有方法 ");
}
}
外部类:
public class Outer {
public static final String STATIC_CONST = "STATIC_CONST 外部类静态常量";
public final String INSTANCE_CONST = "INSTANCE_CONST 外部类实例常量";
public static String sVariable = "外部类静态变量";
public String iV = "外部类实例变量";
public static void sM(){
System.out.println("外语类静态方法");
}
public void iM(){
System.out.println("外部类实例方法");
}
public static void main(String[] args) {
new Outer().testAnonymityInnerOne();
}
public void testAnonymityInnerOne(){
ParentInterface anonymityOne = new ParentInterface() {
public static final String STATIC_CONST = "匿名内部类 one 静态常量";
public final String INSTANCE_CONST = "匿名内部类 two 实例常量";
public String iV;
{
this.iV = "匿名内部类 one 实例变量";
System.out.println("匿名内部类 one 构造代码块");
}
// 实现方法
@Override
public void pInterfaceAbstractMethod() {
System.out.println("Outer.STATIC_CONST = " + Outer.STATIC_CONST);
System.out.println("Outer.this.INSTANCE_CONST = " + Outer.this.INSTANCE_CONST);
System.out.println("Outer.this.iV = " + Outer.this.iV);
System.out.println("iV = " + iV);
System.out.println("sVariable = " + sVariable);
sM();
Outer.this.iM();
ParentInterface.super.pDefaultMethod();
System.out.println("获取外部类父类 class名字 " + Outer.this.getClass().getSuperclass());
System.out.println("获取当前匿名内部类父类 class名字 " + this.getClass().getSuperclass());
System.out.println("当前匿名内部类 class名字 = " + this.getClass() + "\n");
}
public void iM(){
System.out.println("匿名内部类 实例方法");
}
};
anonymityOne.pInterfaceAbstractMethod();
anonymityOne.pDefaultMethod();
System.out.println(ParentInterface.P_CONST);
ParentInterface.pStaticMethod();
}
}
test:
接口的匿名内部类 编译器编译机制 解读理解:
/*
匿名内部类其实也有类名只是看不见,编译器在处理匿名内部类的时候 也会分配类名:
比如这时的这个匿名内部类是第一个匿名内部类。匿名内部类是有编号的!
(一个类中可以出现 0 ~ n 个匿名内部类!)
// 那么它的类名就是 外部类名$1 ($是是来辨别内部类的)并且它也有class文件
javac编译器在JRE、JVM中编译的时候如果是接口的匿名内部类它会
让这个匿名内部类 隐式继承 Object 再实现 ParentInterface
那么它完整的类就是:
class Outer$1 extends Object implements ParentInterface{
public static final String STATIC_CONST = "匿名内部类 one 静态常量";
public final String INSTANCE_CONST = "匿名内部类 two 实例常量";
public String iV;
{
this.iV = "匿名内部类 one 实例变量";
System.out.println("匿名内部类 one 构造代码块");
}
// 实现方法
@Override
public void pInterfaceAbstractMethod() {
System.out.println("Outer.STATIC_CONST = " + Outer.STATIC_CONST);
System.out.println("Outer.this.INSTANCE_CONST = " + Outer.this.INSTANCE_CONST);
System.out.println("Outer.this.iV = " + Outer.this.iV);
System.out.println("iV = " + iV);
System.out.println("sVariable = " + sVariable);
sM();
Outer.this.iM();
ParentInterface.super.pDefaultMethod();
System.out.println("获取外部类父类 class名字 " + Outer.this.getClass().getSuperclass());
System.out.println("获取当前匿名内部类父类 class名字 " + this.getClass().getSuperclass());
System.out.println("当前匿名内部类 class名字 = " + this.getClass() + "\n");
}
public void iM(){
System.out.println("匿名内部类 实例方法");
}
};
然后通过 Outer$1中的默认无参构造器 通过super();
调用父类Object的无参构造器 又 立即马上 创建了 Outer$1的 对象实例,
并且把对象内存地址返回给了 anonymityOne这个对象引用名!!
ParentInterface anonymityOne = new ParentInterface(){} ;
ParentInterface anonymityOne 这句话只是编译器在检查编译器编译的Outer$1与
ParentInterface有没有 继承关系 | 实现关系,有就编译通过,没有就报错!
new ParentInterface(){} 这句话在 JVM 运行时
根据平时的理解就等同于 new Outer$1(); Outer$1 在内部就是通过 super();
先初始化父类Object ,
再通过默认构造器初始化 Outer$1 这个匿名内部类,
并且 立即马上 把在堆中开好的对象的空间地址 让 anonymityOne 这个对象引用名指向了它
由于接收的数据类型是父接口 ParentInterface ,
所以 写代码时(也就是编译时) 就可以访问 父接口 和 Object 根基父类的 非私有成员。
(因为Object父类是隐式继承的,所以Object父类在任何时候都是可以访问到的!),
如果是拿Object来接收匿名内部类的对象的话,
因为多态的特性,那么就只能访问 Object 父类中的内容,
而不能访问父接口中的成员,编译器编译不到的,编译器看的是左边的类型,
才让 开发者 调用可以通过编译的成员!
虽然 开发者 知道可以访问父接口的成员 ,但是编译器不会编译到!
编译都不通过,那么自然就调用不到父接口中的非私有成员!
只有在运行时,才会 以 右边的运行类型来运行!所以这也就是为什么使用了多态机制之后,
在调用子类|实现类独有的成员时,要向下转型的原因之一!
只有通过编译代码才能运行,通过编译是前提条件!
而Outer$1 这个匿名内部类又在 Outer这个 类 局部里面的,
所以也就自然能访问到 Outer 这个类的所有成员
(包括Outer类继承下来的、它自己独有的成员都可以访问到、
当然,静态方法中不能访问实例成员 这本身就是 Java的JVM类加载机制 的特点!
所有想在Outer静态方法、静态代码块中访问Outer的实例成员,必须要创建Outer的对象才可以!
而匿名内部类中又不能有静态方法、静态代码块,所以就不用担心
不能访问外部类的成员的情况发生!)!
*/
Outer$1.class 文件反编译 :![](https://i-blog.csdnimg.cn/blog_migrate/dc8aa8e73694aa230e7caabf4ac57afc.png)
抽象类的匿名内部类(重点,使用较多!)。
abstract class AbstractParentClass {
protected String name;
protected AbstractParentClass(String name) {
this.name = name;
System.out.println("this.name = " + this.name + "\n");
}
/**
* 抽象父类抽象方法
*/
protected abstract void pClassAbstractMethod();
protected void instanceMethod(){
System.out.println(" 抽象类实例方法 ");
}
}
外部类:
public class Outer{
public static final String STATIC_CONST = "STATIC_CONST 外部类静态常量";
public final String INSTANCE_CONST = "INSTANCE_CONST 外部类实例常量";
public static String sVariable = "外部类静态变量";
public String iV = "外部类实例变量";
public static void sM(){
System.out.println("外语类静态方法");
}
public void iM(){
System.out.println("外部类实例方法");
}
public static void main(String[] args) {
new Outer().testAnonymityInnerTwo();
}
public void testAnonymityInnerTwo(){
AbstractParentClass anonymityTwo = new AbstractParentClass("匿名内部类 继承 抽象父类 并实现抽象方法") {
public static final String STATIC_CONST = "匿名内部类 two 静态常量";
public final String INSTANCE_CONST = "匿名内部类 two 实例常量";
public String iV = "匿名内部类 two 实例变量";
{
System.out.println("匿名内部类 Two 构造代码块");
}
// 实现方法
@Override
protected void pClassAbstractMethod() {
System.out.println("Outer.STATIC_CONST = " + Outer.STATIC_CONST);
System.out.println("Outer.this.INSTANCE_CONST = " + Outer.this.INSTANCE_CONST);
System.out.println("Outer.this.iV = " + Outer.this.iV);
System.out.println("iV = " + iV);
System.out.println("sVariable = " + sVariable + "\n");
System.out.println("this.getClass().getSuperclass() = " + this.getClass().getSuperclass());
System.out.println("this.getClass() = " + this.getClass());
// 调用父类方法, 匿名内部类已经 继承下来了所以 super 和 this 和 直接访问,没有任何区别
super.instanceMethod();
this.instanceMethod();
}
public void iM(){
System.out.println("匿名内部类 实例方法");
}
};
anonymityTwo.pClassAbstractMethod();
}
}
test:
抽象类的匿名内部类 编译器编译机制 解读理解:
/*
匿名内部类其实也有类名只是看不见,编译器在处理匿名内部类的时候 也会分配类名:
比如现在的这个匿名内部类是第二个匿名内部类。匿名内部类是有编号的!
(一个类中可以出现 0 ~ n 个匿名内部类!)
// 那么它的类名就是 外部类名$2 ($是是来辨别内部类的)并且它也有class文件
javac编译器在JRE、JVM中 编译的时候会让它 继承 AbstractParentClass 抽象类
那么它完整的类就是:
class Outer$2 extends AbstractParentClass {
public static final String STATIC_CONST = "匿名内部类 two 静态常量";
public final String INSTANCE_CONST = "匿名内部类 two 实例常量";
public String iV = "匿名内部类 two 实例变量";
{
System.out.println("匿名内部类 one 构造代码块");
}
// 实现方法
@Override
protected void pClassAbstractMethod() {
}
public void iM(){
System.out.println("匿名内部类 实例方法");
}
};
然后 通过 Outer$2中的默认无参构造器 通过super(); 调用抽象父类的无参构造器 又 立即马上
创建了 Outer$2的 对象实例,
并且把对象内存地址返回给了 anonymityTwo这个对象引用名!!
AbstractParentClass anonymityTwo =
new AbstractParentClass("匿名内部类 继承 抽象父类 并实现抽象方法") {}
AbstractParentClass anonymityTwo 这句话只是javac编译器在检查javac编译器编译的Outer$2
与 AbstractParentClass 有没有 继承关系 | 实现关系
new AbstractParentClass("匿名内部类 继承 抽象父类 并实现抽象方法"){} 这句话
在 JVM 运行时
根据平时的理解就等同于 new Outer$2(String name);
Outer$2 在内部的默认的无参构造器中就是通过 super(name);
先初始化父类AbstractParentClass ,父类再初始化 根基父类 Object
,然后逐层返回,到匿名内部类(子类)的时候,
再通过默认构造器初始化 Outer$2 这个匿名内部类,
并且 立即马上 把在堆中开好的对象的空间地址 让 anonymityTwo 这个对象引用名指向了它
由于接收的数据类型是父接口 AbstractParentClass ,
所以 写代码时(也就是编译时) 就可以访问 抽象父类中的 和 Object 根基父类的 非私有成员。
(因为Object父类是隐式继承的,所以Object父类在任何时候都是可以访问到的!),
如果是拿Object来接收匿名内部类的对象的话,
因为多态的特性,那么就只能访问 Object 父类中的内容,
而不能访问抽象父类中的成员, 编译器编译不到的,
编译器看的是 赋值运算符 左边的类型,才让 开发者 调用可以通过编译的成员!
虽然 开发者 知道可以访问父接口的成员 ,但是编译器不会编译到!
编译都不通过,那么自然就调用不到抽象父类中的非私有成员!
只有在JVM运行时,才会 以 右边的运行类型来运行!所以这也就是为什么使用了多态机制之后,
在调用子类|实现类独有的成员时,要向下转型的原因之一!
只有通过编译代码才能运行,通过编译是前提条件!
而Outer$2 这个匿名内部类又在 Outer这个 类 局部里面的,
所以也就自然能访问到 Outer 这个类的所有成员
(包括Outer类继承下来的、它自己独有的成员都可以访问到、
当然,静态方法中不能访问实例成员 这本身就是 Java的JVM类加载机制 的特点!
所有想在Outer静态方法、静态代码块中访问Outer的实例成员,必须要创建Outer的对象才可以!
而匿名内部类中又不能有静态方法、静态代码块,
所以就不用担心 不能访问外部类的成员的情况发生!)!
*/
Outer$2.class 文件反编译 :
普通类的 匿名内部类(重点:使用较少 。):
class ParentClass{
public String name;
public ParentClass(String name) {
this.name = name;
System.out.println("this.name = " + this.name + "\n");
}
public void commonMethod(){
System.out.println(6 << 3);
}
public String otherMethod(){
return "其他方法";
}
}
外部类:
public class Outer{
public static final String STATIC_CONST = "STATIC_CONST 外部类静态常量";
public final String INSTANCE_CONST = "INSTANCE_CONST 外部类实例常量";
public static String sVariable = "外部类静态变量";
public String iV = "外部类实例变量";
public static void sM(){
System.out.println("外语类静态方法");
}
public void iM(){
System.out.println("外部类实例方法");
}
public static void main(String[] args) {
new Outer().testAnonymityInnerThree();
}
public void testAnonymityInnerThree(){
ParentClass anonymityThree = new ParentClass("匿名内部类 正在 继承父类并且 重写方法") {
public static final String STATIC_CONST = "匿名内部类 three 静态常量";
public final String INSTANCE_CONST = "匿名内部类 three 实例常量";
public String iV;
{
this.iV = "匿名内部类 three 实例变量";
System.out.println("匿名内部类 three 构造代码块");
}
@Override
public void commonMethod() {
System.out.println("Outer.STATIC_CONST = " + Outer.STATIC_CONST);
System.out.println("Outer.this.INSTANCE_CONST = " + Outer.this.INSTANCE_CONST);
System.out.println("Outer.this.iV = " + Outer.this.iV);
System.out.println("iV = " + iV);
System.out.println("sVariable = " + sVariable+"\n");
System.out.println("this.getClass().getSuperclass() = " + this.getClass().getSuperclass());
System.out.println("this.getClass() = " + this.getClass()+"\n");
// 调用父类方法, 匿名内部类已经 继承下来了所以 super 和 this 和 直接访问,没有任何区别
System.out.println("父类中的: ------> " + otherMethod());
iM();
}
public void iM(){
System.out.println("匿名内部类Three 实例方法");
}
};
anonymityThree.commonMethod();
}
}
test:
普通类 的匿名内部类 编译器编译机制 解读理解:
/*
匿名内部类其实也有类名只是看不见,编译器在处理匿名内部类的时候 也会分配类名:
比如现在的这个匿名内部类是第三个匿名内部类。匿名内部类是有编号的!
(一个类中可以出现 0 ~ n 个匿名内部类!)
// 那么它的类名就是 外部类名$3 ($是是来辨别内部类的)并且它也有class文件
javac 编译器在JRE、JVM中编译的时候会让它 继承 ParentClass 类
那么它完整的类就是:
class Outer$3 extends ParentClass{
public static final String STATIC_CONST = "匿名内部类 three 静态常量";
public final String INSTANCE_CONST = "匿名内部类 three 实例常量";
public String iV;
{
this.iV = "匿名内部类 three 实例变量";
System.out.println("匿名内部类 three 构造代码块");
}
@Override
public void commonMethod() {
}
public void iM(){
System.out.println("匿名内部类Three 实例方法");
}
};
然后 通过 Outer$3中的默认无参构造器 通过super(); 调用 父类的无参构造器
又 立即马上 创建了 Outer$3的 对象实例,
并且把对象内存地址返回给了 anonymityThree这个对象引用名!!
ParentClass anonymityThree =
new ParentClass("匿名内部类 正在 继承父类并且 重写方法") {}
ParentClass anonymityThree 这句话只是javac编译器在检查编译器编译的
Outer$3与 ParentClass 有没有 继承关系 | 实现关系
new ParentClass("匿名内部类 正在 继承父类并且 重写方法") {} 这句话在 JVM 运行时
根据平时的理解就等同于 new Outer$3(String name); Outer$3
在内部的默认无参构造器中就是通过 super(name);
先初始化父类ParentClass ,父类再初始化 根基父类 Object
,然后逐层返回,到匿名内部类(子类)的时候,
再通过默认构造器初始化 Outer$3 这个匿名内部类,
并且 立即马上 把在堆中开好的对象的空间地址 让 anonymityThree 这个对象引用名指向了它
由于接收的数据类型是父接口 ParentClass ,
所以 写代码时(也就是编译时) 就可以访问 父类中的 和 Object 根基父类的 非私有成员。
(因为Object父类是隐式继承的,所以Object父类在任何时候都是可以访问到的!),
如果是拿Object来接收匿名内部类的对象的话,
因为多态的特性,那么就只能访问 Object 父类中的内容,
而不能访问 父类中的成员, 编译器编译不到的,编译器看的是 赋值运算符 左边的类型,
才让 开发者 调用可以通过编译的成员!
虽然 开发者 知道可以访问父接口的成员 ,但是编译器不会编译到!
编译都不通过,那么自然就调用不到抽象父类中的非私有成员!
只有在运行时,才会 以 右边的运行类型来运行!所以这也就是为什么使用了多态机制之后,
在调用子类|实现类独有的成员时,要向下转型的原因之一!
只有通过编译代码才能运行! 通过编译是前提条件!
而Outer$3 这个匿名内部类又在 Outer这个 类 局部里面的,
所以也就自然能访问到 Outer 这个类的所有成员
(包括Outer类继承下来的、它自己独有的成员都可以访问到、
当然,静态方法中不能访问实例成员 这本身就是 Java的JVM类加载机制 的特点!
所有想在Outer静态方法、静态代码块中访问Outer的实例成员,必须要创建Outer的对象才可以!
而匿名内部类中又不能有静态方法、静态代码块,
所以就不用担心 不能访问外部类的成员的情况发生!)!
*/
Outer$3.class 文件反编译:
匿名内部类的匿名对象(重点!):
说明:如果是new 了一个匿名内部类 只用 父类 的 对象引用名 指向了 匿名内部类的对象内存地址的话。
- 优点是:可以重复使用 这个 对象引用名 调用 父类类型 的非私有化成员。
- 缺点是:就会出现 匿名内部类 它自己独有的实例成员、静态常量无法访问、能访问到的全都是 父类|父接口 能编译到的内容(因为访问的时候,是写代码的时候,写代码的时候 正处于编译阶段,先编译再运行!!)
匿名对象(重点! ):
test:
test:
不仅仅是匿名内部类可以匿名对象,一般的Java类都可以:
匿名内部类和匿名对象的总结(重点!):
- 可以不用 父类or父接口 对象引用 指向 匿名内部类创建对象的对象。这样就构成了 匿名对象。
- 匿名对象的优点:如果 只需要使用一次,就没有必要 用 对象引用名 指向 对象地址,简化开发。匿名对象还可以构成链式编程!非常方便!匿名内部类的匿名对象可以调用匿名内部类自己独有的实例成员、静态变量。如果用多态机制则调用不了!!
- 匿名对象的相对缺点:匿名对象只能使用一次。因为没有对象引用名 引用了,所以就不能重复使用。
- 匿名内部类的优点:简化开发、可选择性高、简洁高效、安全性高。实际开发中使用较多。(匿名内部类的匿名对象 可作为多态引用的一种表示格式,可以作为方法的多态形参 的 实参 传入进去调用方法,可以作为方法的 多态返回值类型 返回。(使用较多),可作为多态引用的一种表示格式!),(可以作为多态数组、多态集合的实际子类存储进去。(使用较少)),(类的匿名内部类至少可以访问 3 个甚至更多 类的成员! 外部类 -> 父类的非私有成员 -> 匿名内部类自己独有的成员。)(接口的匿名内部类至少可以访问4个类的成员!外部类 --> 父类(Object)--> 父接口或父接口的上级父接口 的非私有成员 --> 匿名内部类自己独有的成员。)
- 匿名内部类的相对缺点:匿名内部类的构造器不能重载,它只有一个 JVM 自动分配生成的 默认无参构造器,它是动态加载的。由于匿名内部类没有类名,所以它的构造器不能够被重载,也不能显式的写出来!只能使用一次,复用性不高,扩展性不高。匿名内部类不能够被继承。匿名内部类只能使用一次。(是匿名内部类只能使用一次,而不是 接收匿名内部类的 对象引用只能使用一次! )
- 匿名内部类是: 继承 + 封装 + 多态 + 多态动态绑定机制 这几种技术 运用到极致 的 一种语法, 一种技术, 非常优雅 !!!!!!