当前位置: 首页 > article >正文

匿名内部类(内部类) - 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、细节&注意事项

  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)内部类


http://www.kler.cn/a/150101.html

相关文章:

  • 【MySQL】 运维篇—故障排除与性能调优:案例分析与故障排除练习
  • 数字身份发展趋势前瞻:身份韧性与安全
  • 全面解析Flutter中的Stream用法及实际应用
  • Qt项目实战:红绿灯小程序
  • Optimizing Medical Image Segmentation with Advanced Decoder
  • 低代码用户中心:简化开发,提升效率的新时代
  • git-4
  • 前五年—中国十大科技进展新闻(2012年—2017年)
  • leetcode面试经典150题——30 长度最小的子数组
  • Leetcode—15.三数之和【中等】
  • Attacking Fake News Detectors via Manipulating News Social Engagement(2023 WWW)
  • 黑马程序员索引学习笔记
  • PTA:猜帽子游戏 ,C语言
  • open与openat的区别
  • Linux uname命令教程:如何打印linux操作系统名称和硬件的基本信息(附实例教程和注意事项)
  • SCI的写作前提——认识论文的本质
  • Python+requests+Jenkins接口自动化测试实例
  • linux查询某个进程使用的内存量
  • 复位电路的电阻电容的作用
  • 如何设置Linux终端提示信息
  • Qt 信号与槽简介
  • 案例:某电子产品电商平台借助监控易保障网络正常运行
  • unity shaderGraph实例-可交互瀑布
  • C++ day45 爬楼梯 零钱兑换 完全平方数
  • 大数据基础设施搭建 - Sqoop
  • AI搜索相关性在网站和APP上的应用