匿名内部类(内部类) - Java

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、细节&注意事项

  1. 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点。
    因此可以调用匿名内部类方法有下面两种:AnonymousInnerDetails.java【见下文 案例a】
  2. 可以直接访问外部类的所有成员,包含私有的。
  3. 不能添加访问修饰符,因为它的地位就是一个局部变量。
  4. 作用域:仅仅在定义它的方法或代码块中。
  5. 匿名内部类—>访问—>外部类成员 [访问方式:直接访问]
  6. 外部其他类—>不能访问—>匿名内部类(因为匿名内部类地位是一个局部变量)
  7. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.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)内部类

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值