带你深入学习java内部类_匿名类,细到不能再细~(3)

103 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

匿名类

我们是否想过一个问题,内部类既然只能在外嵌类中使用,显然当我们只需要使用该内部类一次时,常规方法创建一个内部类比较麻烦,价值不高,有其他方法解决该问题嘛? 那就是匿名类! 匿名类是不能有名字的类,它们不能被引用,只能在创建时用 new 语句来声明它们。

匿名类特点: 1.本质是一个类 2.没有名字的类 3.属于内部类 4.还是个对象

匿名类语法格式:

new 类名或接口(参数列表){
			//类体
}; 

匿名类使用演示:

//代码演示
class Outer2{ //外部类
    private int a = 1;
    void f1(IA ia){ //方法,参数实现向上转型
      ia.eat(); //调用重写方法
    }
}
interface IA{
    void eat();
}
//传统方式
class A implements IA{//我们要创建一个类实现IA接口才能创建对象
    @Override//在类中重写接口中的方法
    public void eat() {
        System.out.println("传统eat...");
    }
}
public class Test_2 {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.f1(new A()); //传统方式需要创建A对象
        outer2.f1(new IA() {//匿名类方式,在new的时候重写方法即可
            //类体
            @Override
            public void eat() {
                System.out.println("匿名eat...");
            }
        });
    }
}

在这里插入图片描述

与子类有关的匿名类

假如没有显式地声明一个类的子类,但又想用这个子类创建对象,那么该如何实现这一目的呢?

java允许用户直接使用一个类的子类的类体创建一个子类的对象,也就是说,在创建子类对象时,除了使用父类的构造方法外还有类体,此类体被认为是一个子类去掉类声明后的类体,称作匿名类。匿名类就是一个子类,由于无名可以,所以不可能用匿名类声明对象,当可以直接用匿名类创建一个对象。

语法规则:

//1.方式一
new 类名(){
    //匿名类类体
}
//2.方式二 
//使用了父类提供的(带参数的)构造方法
new 类名(参数){
    //匿名类类体
}

匿名类特点:

1.匿名类可以继承父类的方法,也可也重写父类的方法。 2.在使用匿名类时,必然存在某个类中直接用匿名类创建对象,因此匿名类一定是内部类。 3.匿名类可以访问外嵌类中的成员变量和方法。在匿名类的类体中不可以声明stataic 成员变量或方法。 4.由于匿名类是一个子类,且没有类名,所以在用匿名类创建对象时要直接使用父类的构造方法。 5.尽管匿名类创建对象没有经过类声明的步骤,但匿名对象的引用可以传递给一个匹配的参数。

//实例一
abstract class Bank{
    int money;
    public Bank(){
        money = 100;
    }
    public Bank(int money){
        this.money = money;
    }
    public abstract void output();
}
class ShowBank{
    void showMess(Bank bank){ //参数是Bank类型
        bank.output();
    }
}
public class Test_2 {
    public static void main(String[] args) {
        ShowBank showBank = new ShowBank();
        showBank.showMess(new Bank() { //向参数传递Bank的匿名子类对象
            @Override
            public void output() {
                money += 100;
                System.out.println("bug郭 银行:"+money);
            }
        });
        showBank.showMess(new Bank(500){////向参数传递Bank的匿名子类对象
            @Override
            public void output() {
                money += 100;
                System.out.println("bug郭 银行:"+money);
            }
        });
    }
}

在这里插入图片描述可以看到与子类有关的匿名类的使用,当我们只需要使用子类对象一次时,我们可以使用匿名类对象!

与接口有关的匿名类

与接口有关的匿名类和与子类有关的匿名类类似! 当某个方法的参数是接口类型时,那么可以使用接口名和类体组合创建一个匿名对象传递方法的参数,类体必须重写接口中的全部方法。

interface SpeakHello{
    void speak();
}
class HelloMachine{
    public void turnOn(SpeakHello hello){
        hello.speak();
    }
}
public class Test_3 {
    public static void main(String[] args) {
        HelloMachine machine = new HelloMachine();
        machine.turnOn(new SpeakHello() { //和接口有关的匿名类
            @Override
            public void speak() {
                System.out.println("hello,you are welcom!");
            }
        });
        machine.turnOn(new SpeakHello() {//和接口有关的匿名类
            @Override
            public void speak() {
                System.out.println("你好,欢迎光临!");
            }
        });
    }
}

在这里插入图片描述

匿名类小测试

题目: 1有一个铃声接口 Bell,里面有个 ring 方法。 2.有一个手机类 Cellphone,具有闹钟功能 alarmClock,参数是Bell类型 3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了 4.再传入另一个匿名内部类(对象),打印:小伙伴上课了 答案:

public class Test_3{
    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 接口类型
        System.out.println(bell.getClass());
        bell.ring();//动态绑定
    }
}

在这里插入图片描述

匿名类深入了解

匿名类真的就没有名字嘛?

错,只是我们不知道名字而已,就像匿名信一样,写信者肯定有名字的!其实在java底层jdk中我们java匿名类系统会给他定义一个名字的!

在这里插入图片描述我们可以看jdk系统自动给匿名类取名为:调用匿名类的类类名$1

总结:

我们什么时候可以用到匿名类呢? 当我们只需要使用一次该类的子类对象或者使用一次该接口对象时我们就可以使用匿名类! 匿名类在创建对象时,构建! 采用 new 类名(接口名){匿名类体};实现!

匿名类通常当做实参直接传递,简洁高效!

Lambda表达式代替匿名类

我们先来了解一下什么是Lambda表达式~

函数接口和Lambda表达式

函数接口 如果一个接口中有且只有一个abstract方法,称这样的接口是单接口。从JDK 8 开始,java使用Lambda表达式,并将单接口称为函数接口。

Lambda表达式 下面add()是一个通常的方法(函数):

int add(int a,int b){
  retunrn a + b;
}

Lambda表达式就是一个用匿名方法(函数),用Lambda表达式表达同样功能的匿名方法:

//省略函数名
(int a,int b)->{
return a+b;
}

(a,b)->{
return a+b;
}

Lambda表达式就是只写参数列表和方法体的匿名方法(参数列表和方法体之间用符号->连接)

//语法规则
(参数列表)->{
  //方法体
}

Lambda表达式的值 由于Lambda表达式过于简化,所以必须有特殊上下文,编译器才能推断出Lambda表达式到底是哪个方法,才能计算值,Lambda表达式的值就是方法的入口地址。因此,java中的Lambda表达式主要用在单接口,即函数接口!

使用举例:


interface ShowMessage{ //函数接口
    void show(String str);
}
public class Test_4 {
    public static void main(String[] args) {
        //接口变量中存放Lambda表达式的值
       ShowMessage sm = (s)->{ //Lambda表达式(匿名方法)
           System.out.println("java yyds!");
           System.out.println(s);
           System.out.println("hond on!");
       };
       sm.show("bug郭"); //接口回调Lambda表达式实现接口方法
    }
}

在这里插入图片描述因为是匿名方法,当我们要使用该接口创建一个对象时,就可以通过Lambda表达式实现接口方法,创建对象,接口回调该方法!


大概了解了Lambda表达式,我们就可以用Lambda表达式代替匿名类了 就是在使用匿名类时,当我们要实现接口函数(单接口中的方法)时,用Lambda表达式非常便利!

interface ShowMessage{ //函数接口
    void show();
}
class Test{
    public void test(ShowMessage message){
        message.show();
    }
}
public class Test_4 {
    public static void main(String[] args) {
        Test test = new Test();
        test.test(()->{ //向形参message传递Lambda表达式的值
            System.out.println("java yyds!");
        });
    }
}

在这里插入图片描述