匿名内部类
- 1、理解
- 2、语法
- 3、使用
- (1)基于接口的内部类
- (2)基于类的内部类
- (3)基于抽象类的匿名内部类
- 4、细节&注意事项
- 5、最佳应用场景
- (1)当作实参直接传递,简洁高效。
- (2)练习
1、理解
(1)类:本质是类。
(2)内部类:定义在一个类的内部。
(3)匿名:该类没有名字。其实有名字,但是是系统起的。
(4)同时还是一个对象。
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
2、语法
new 类或者接口(参数列表) {类体}
3、使用
(1)基于接口的内部类
为什么需要《匿名内部类》?
① 需求:在Outer04 类的method方法中,想使用 IA 接口,并创建对象
② 传统方式:写一个类,实现该接口,并在method方法中 创建对象
interface IA {public void cry();
}
//外部类
class Outer04 {private int n1 = 10;public void method() {IA tiger = new Tiger();//接口的多态tiger.cry();IA dog = new Dog();//接口的多态dog.cry();}
}class Tiger implements IA{@Overridepublic void cry() {System.out.println("老虎叫唤···");}
}class Dog implements IA{@Overridepublic void cry() {System.out.println("小狗汪汪叫···");}
}
public class AnonymouslnnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}
}
③ 引出问题:这个tiger和dog对象可能只使用一次,后面不使用,浪费。
④ 可以使用匿名内部类来简化开发。
匿名内部类优化后:
class Outer04 {private int n1 = 10; public void method() {IA tiger_ = new IA() {@Overridepublic void cry() {System.out.println("老虎喊叫···");}};tiger_.cry();}
}
【分析】:
tiger 的编译类型? —— IA
tiger 的运行类型? —— 匿名内部类
底层:
XXXX = Outer04$1 底层(不是程序员)分配类名
class XXXX implements IA {@Overridepublic void cry() {System.out.println("老虎喊叫···");}
}
new
底层创建好类之后,立马new了一个。jdk 底层在创建匿名内部类 Outer04$1后,立即马上就创建了 Outer04$1实例,并且把地址返回给 tiger。
匿名内部类使用一次,就不能再使用。
匿名内部类不是tiger,tiger还是可以用的。
就是匿名内部类在方法区的模板不在了,但是通过模板创建的对象还在。
类加载在方法区;类 new 出来的对象在堆里面。虽然类用一次没了,但是new的对象还在。
(2)基于类的内部类
class Outer04_ {private int n1 = 10;public void method() {Father father = new Father("jack") {};}
}
//类
class Father {public Father(String name) {}public void test() {}
}
【分析】:
father 的编译类型? —— Father
father 的运行类型? —— 匿名内部类 Outer04_$1
(①这里为了与上一个Outer04类做区分,重新创建了一个Outer04_类,名字上多了一个下划线 ②如果还是在Outer04类内部,那么此时的运行类型是Outer04$2,是按照顺序分配的。)
【底层】创建匿名内部类:
class Outer04_$1 extends Father {
//里面什么都没有写,因为Father类的两个方法都有实现(实现=有大括号)
}
这里已经不是Father类了,是新的类Outer04_$1
匿名内部类重写方法
重写之后的底层:
class Outer04_$1 extends Father {@Overridepublic void test() {System.out.println("匿名内部类重写了Fathter中的test方法");}
}
class Outer04_ {private int n1 = 10;public void method() {Father father = new Father("jack") {@Overridepublic void test() {System.out.println("匿名内部类重写了Fathter中的test方法");}};System.out.println("father的运行类型=" + father.getClass());}
}
Father father = new Father(“jack”) {};中的new
同时也直接返回了匿名内部类Outer04_$1 的对象。
Father father = new Father(“jack”) {};中的参数列表“jack”
"jack"会传递给Father的构造器。
(3)基于抽象类的匿名内部类
上述的Father不是抽象类,如果是抽象类就需要实现。
class Outer04_ {public void method2() {//基于抽象类的匿名内部类Animal ani = new Animal() {@Overridepublic void eat() {System.out.println("吃吃吃");}};ani.eat();}
}
//抽象类
abstract class Animal {abstract public void eat();
}
4、细节&注意事项
- 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点。
因此可以调用匿名内部类方法有下面两种:AnonymousInnerDetails.java【见下文 案例a】 - 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它的地位就是一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类—>访问—>外部类成员 [访问方式:直接访问]
- 外部其他类—>不能访问—>匿名内部类(因为匿名内部类地位是一个局部变量)
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
【案例a】
- 方法一
public class AnonymousInnerDetails {public static void main(String[] args) {Outer05 outer05 = new Outer05();outer05.f1();}
}
class Outer05 {private int n1 = 99;public void f1() {Person p = new Person() {@Overridepublic void hi() {System.out.println("匿名内部类重写hi~");}};//p的编译类型 Person//p的运行类型 Outer05$1p.hi();//动态绑定}
}class Person {public void hi() {System.out.println("Person-hi()~");}
}
- 方法二 直接调用,匿名内部类本身也是返回对象
class Outer05 {public void f2() {new Person() {@Overridepublic void hi() {System.out.println("匿名内部类重写hi~哈哈哈哈");}}.hi();}
}
- 带参数调用
【main】
Outer05 outer05 = new Outer05();
outer05.f2();
class Outer05 {public void f2() {new Person() {@Overridepublic void hi() {System.out.println("匿名内部类重写hi~哈哈哈哈");}@Overridepublic void ok(String str) {System.out.println("匿名内部类重写ok~咳咳咳");super.ok(str);}}.ok("jackky");}
}class Person {public void ok(String str) {System.out.println("Person-ok()~"+str);}
}
5、最佳应用场景
(1)当作实参直接传递,简洁高效。
public class AnonymousInnerExercise {public static void main(String[] args) {f1(new IL(){@Overridepublic void show() {System.out.println("这是一幅名画");}});}public static void f1(IL il) {il.show();}
}interface IL {void show();
}
如果是传统方法:
编写一个类 —> 实现 IL —> 这个在编程领域被称为:硬编码
public class AnonymousInnerExercise {public static void main(String[] args) {f1(new Picture());}public static void f1(IL il) {il.show();}
}class Picture implements IL{@Overridepublic void show() {System.out.println("这是一幅名画");}
}interface IL {void show();
}
如果要修改show方法,硬编码方式中,只能在Picture 类修改,会影响到所有对象。
(2)练习
① 有一个铃声接口Bell,里面有个ring方法。(右图)
② 有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型(右图)
③ 测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
④ 再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class AnonymousInnerExercise02 {public static void main(String[] args) {Cellphone cellphone = new Cellphone();cellphone.alarmclock(new Bell() {@Overridepublic void ring() {System.out.println("懒猪起床了");}});cellphone.alarmclock(new Bell() {@Overridepublic void ring() {System.out.println("小伙伴上课了");}});}
}
interface Bell {void ring();
}class Cellphone {public void alarmclock(Bell bell) {bell.ring();}
}
匿名内部类涉及到(1)继承(2)多态(3)动态绑定(4)内部类