匿名内部类(内部类) - Java
匿名内部类
- 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{
@Override
public void cry() {
System.out.println("老虎叫唤···");
}
}
class Dog implements IA{
@Override
public 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() {
@Override
public void cry() {
System.out.println("老虎喊叫···");
}
};
tiger_.cry();
}
}
【分析】:
tiger 的编译类型? —— IA
tiger 的运行类型? —— 匿名内部类
底层:
XXXX = Outer04$1 底层(不是程序员)分配类名
class XXXX implements IA {
@Override
public 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 {
@Override
public void test() {
System.out.println("匿名内部类重写了Fathter中的test方法");
}
}
class Outer04_ {
private int n1 = 10;
public void method() {
Father father = new Father("jack") {
@Override
public 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() {
@Override
public 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() {
@Override
public void hi() {
System.out.println("匿名内部类重写hi~");
}
};
//p的编译类型 Person
//p的运行类型 Outer05$1
p.hi();//动态绑定
}
}
class Person {
public void hi() {
System.out.println("Person-hi()~");
}
}
- 方法二 直接调用,匿名内部类本身也是返回对象
class Outer05 {
public void f2() {
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写hi~哈哈哈哈");
}
}.hi();
}
}
- 带参数调用
【main】
Outer05 outer05 = new Outer05();
outer05.f2();
class Outer05 {
public void f2() {
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写hi~哈哈哈哈");
}
@Override
public 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(){
@Override
public 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{
@Override
public 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() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell {
void ring();
}
class Cellphone {
public void alarmclock(Bell bell) {
bell.ring();
}
}
匿名内部类涉及到(1)继承(2)多态(3)动态绑定(4)内部类