一篇文章带你学会匿名内部类,由浅入深,一定手把手教会你

目录

1. 匿名内部类的语法格式

2. 常规类举例

3. 匿名内部类的推演过程

4. 匿名内部深层分析

5. 匿名内部类有什么用?

5.1 问题引入

5.2 解决思路

5.3 巧用匿名内部类解决代码冗余

5.4 牢记匿名内部类创建的也是一个对象哦!


1. 匿名内部类的语法格式

匿名内部类的定义:匿名内部类我们从字面上的意思理解就可以了, "匿名" 就是没有名字,"内部类" 就是在其它类内部的一个类,合在一起的意思就是 "在其它类中没有名字的内部类"。

匿名内部类的语法格式也比较简单,如下图所示;

对于匿名内部类而言,它里面整体包含了三个部分,继承/实现,方法重写,创建对象。各位童鞋先把匿名内部类的格式混个眼熟,下面我们一点一点来推演匿名内部类为什么是这样的格式

2. 常规类举例

因为直接讲解可能会有很多同学懵,不知道所以然,所以我们先从 Java 中最为常规的类举例说起,一点点带你解析匿名内部类的由来。

如下所示,我先创建一个 Swim 游泳接口

// 创建一个 Swim 游泳接口
public interface Swim {
    // 定义一个抽象方法,用于让子类重写实现具体行为
    public abstract void swim();
}

现在我来提问,如果我现在要调用 swim 方法,该怎么做?

答案也呼之欲出了吧!使用我们最为常规的方法,可以定义一个类然后实现 Swim 游泳接口,然后重写接口中的方法,再然后创建定义出来的那个类对象,利用类对象调用重写的 swim 游泳方法就可以了。

好的,既然这么说了,下面我们就来试着做一下,代码已经写好了如下所示

// 定义一个学生类实现 Swim 游泳接口
public class Student implements Swim{
    // 重写 Swim 接口中的游泳方法
    @Override
    public void swim() {
        // 为了演示简单打印一句话,不做逻辑复杂的语句
        System.out.println("重写的游泳方法");
    }
    // 定义 main 方法
    public static void main(String[] args) {
        // 创建 Student 类的对象 s1
        Student s1 = new Student();
        // 让对象 s1 调用实现的方法
        s1.swim();
    }
}

经过上面的代码书写,我们已经完成了所有的步骤,现在来运行上述方法,我们就可以达到我们想要的结果了,运行结果如下图所示

3. 匿名内部类的推演过程

上面的常规类代码相比对于各位同学来说应该是小意思,那么现在我们来对上面的代码做一个分析。

Student 就是类的名字,实现了 Swim 接口,大括号体内部的所有东西就是当前类的所有内容。

(1)推理第一步:现在我这样做,我把大括号前面的内容全部删掉,只留大括号内部的内容,如下所示,仔细分析可以看出,此时这个类是一个没有名字的类,而且重写了 swim 方法,内部类三要素之一的方法重写已经出现了。

(2)推理第二步:这个没有名字的类重写的方法正是 Swim 接口中的方法,所以说这个没有名字的类实现了 Swim 接口,匿名内部类三要素之一的继承/实现也出现了。

既然这个没有名字的类实现了 Swim 接口,我们姑且把 Swim 接口放在这个没有名字的类的前面,如下图所示

 (3)推理第三步:创建对象,我们先来回顾普通创建对象时的创建方法,基本都是使用 new Object() 的方式,而匿名内部类也是一样的道理,既然我们要创建对象,当然也是采用 new ... 的方式,接着刚才第二部的推理,我们将 new 关键字放在 Swim 的前面,将 "()" 放在 Swim 的后面,就得到了如下的样子,匿名内部类三要素之一的创建对象也完成了

此时我们来对比刚才匿名内部类的语法格式,可以发现已经完全你全匹配上了,我们也可以简单的这样理解匿名的推演过程

4. 匿名内部深层分析

经过了上面的推演过程,想必各位同学对于匿名内部类的格式已经有了更好的理解。

精炼成一句话来说就是,"匿名内部类是指大括号内部的那部分代码才是真正的没有名字的类,这个没有名字的类继承或者实现了方法体之前的那个类或接口,最前面的 new 关键字是创建对象用的,new 出来的对象其实就是后面大括号内部没有名字的那个类的对象"。

5. 匿名内部类有什么用?

5.1 问题引入

OK,上面我们大篇幅地讲解了匿名内部类的推演和底层逻辑,估计现在很多同学都会很疑惑,这玩意有啥用啊,我看不出来它有什么好处和优点?

我给大家就一个场景提问一个问题,你就知道它有什么用了。

如下代码,我定义了一个 Animal 接口,

public interface Animal {
    void eat();
}

在另一个类中,我定义了一个静态方法,并且方法的参数需要传递一个 Animal 类型的对象,

public class Test6 {
    public static void method(Animal a){
        a.eat();
    }
}

现在,我要在测试类中调用 method 方法,该怎么办?

5.2 解决思路

各位同学要知道,现在 Animal 是个接口,我们不能通过接口去创建对象,所以按照正常的逻辑来说,我们必须创建一个类去实现 Animal 接口,并重写 Animal 接口中的方法,然后再创建 Animal 接口的实现类对象,将对象传递给 method 方法,然后才能调用 Animal 接口中的方法。

如下代码展示

// 创建 Dog 类 实现 Animal 接口
public class Dog implements Animal{
// 重写Animal 中的方法
    @Override
    public void eat() {
        System.out.println("狗吃东西");
    }
}
public class Test6 {
    public static void method(Animal a){
        a.eat();
    }
    public static void main(String[] args) {
        // 创建一个狗对象
        Dog dog = new Dog();
        // 将 dog 对象作为参数传递给 method
        method(dog);
    }
}

此时运行 main 方法,就可以得到我们的结果,在控制台得到 "狗吃东西" 的运行结果

5.3 巧用匿名内部类解决代码冗余

通过刚才举的例子和我们的代码解决方案,不难看出,传统的解决思路虽然可以达到目的,却是极为麻烦,带来了大量的代码冗余。如果这个实现类我只使用一次,会不会很浪费。而且这才只是一个接口,如果还有别的静态方法和这个一样,其实会对开发造成一定的影响。

但如果现在我们使用匿名内部类的话,就可以极大地简化开发,提高效率。

代码如下所示,我们完全不需要创建一个类去实现 Animal 接口,直接使用匿名内部类的方式创建 Animal 接口的方法,并在匿名内部类方法内部直接重写 Animal 接口的 eat 方法。这样非常非常简化将代码,极大的提高了开发效率,也避免了代码的冗余

public class Test6 {
    public static void method(Animal a){
        a.eat();
    }

    public static void main(String[] args) {
        method(new Animal() {
            @Override
            public void eat() {
                System.out.println("狗吃东西");
            }
        });
    }
}

运行结果如下所示,同样可以达到相同的效果

5.4 牢记匿名内部类创建的也是一个对象哦!

各位同学不要忘记了,匿名内部类语法本身创建出来的就是一个对象,既然是一个对象,那么对象 Object 类中的方法它都有,它也可以作为对象调用它自己的方法,下面我就来给同学们展示一些匿名内部类花里胡哨的玩法。

如下代码,我们都知道接口不能创建对象,但现在我利用匿名内部类的方式就能创建 Animal 接口的对象,还能重写 eat 方法,

public class Test6 {
    public static void main(String[] args) {
        // 采用多态+匿名内部类的方式创建 Animal 接口的对象
        Animal animal = new Animal() {
            @Override
            public void eat() {
                System.out.println("猫吃小鱼");
            }
        };
        // 调用 animal 对象中的方法
        animal.eat();
    }
}

不仅如此,创建出来的 animal 对象还可以调用它自己重写的 eat 方法,是不是很巧妙呀!

运行如下,完全没有问题

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值