• 22、接口与抽象类、匿名类的介绍


    一、抽象类和接口的选择

    📋 选择抽象类

    📝 在紧密相关的类之间共享代码(有关联的类之间共享代码)
    📝 需要除 public 之外的访问权限(接口中全是 public
    📝 需要定义实例变量,非 final静态变量的时候(接口中默认都是 public final

    📋 选择接口

    📝 不相关的类实现相同的方法的时候
    📝 只是定义行为(方法),不关心具体是誰实现了行为的时候
    📝 想实现类型的多重继承的时候

    二、接口中的默认方法

    ❓ 假如进行接口升级(如增加新的抽象方法),会导致大幅度的代码改动,之前实现该接口的所有类都得改动
    📋 若想在不改动之前实现类的前提下进行接口升级,从 Java8 开始有2种方案:

    (1) 默认方法

    📝 被 default 修饰的方法
    📝 默认方法只能是实例方法
    📝 默认方法有具体的实现
    📝 默认方法不强制子类(实现该接口的类)必须实现它.


    ❓ 当一个类实现的接口中存在默认方法的时候,该类可以:

    📝 啥也不干,沿用接口的该方法的默认实现
    📝 重写该默认方法的实现
    📝 把自己修改为抽象类-,重新声明抽象方法(抽象方法的方法签名和返回值类型要与父接口的默认方法一致)

    ❓ 当一个接口继承的父接口中存在默认方法的时候,该类可以:

    📝 啥也不干,沿用父接口的该方法的默认实现
    📝 重新定义默认方法,覆盖父接口默认方法
    📝 声明抽象方法(抽象方法的方法签名和返回值类型要与父接口的默认方法一致)

    默认方法细节

    ① ✏️ 若父类定义的非抽象方法接口的默认方法的方法签名一样,最终将调用父类的方法(与方法的返回类型无关)

    public interface Eatable {
        default void eat() {
            System.out.println("Eatable_eat()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class Father {
        public void eat() {
            System.out.println("Father_eat()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class Child extends Father implements Eatable {
        public static void main(String[] args) {
            Child child = new Child();
            // Father_eat()
            child.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ② ✏️ 若父类定义的抽象方法与接口的默认方法的方法签名一样,强制要求子类必须实现此抽象方法

    public interface Eatable {
        default void eat() {
            System.out.println("Eatable_eat()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public abstract class Father {
        public abstract void eat();
    }
    
    • 1
    • 2
    • 3
    public class Child extends Father implements Eatable {
        public static void main(String[] args) {
            Child child = new Child();
            
            /*
                Eatable_eat()
                Child_eat()
             */
            child.eat();
        }
    
    
        @Override
        public void eat() {
            // 调用父接口的 eat() 方法
            Eatable.super.eat();
            System.out.println("Child_eat()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ③ ✏️ 若(父)接口定义的默认方法与其他(父)接口定义的默认方法的方法签名相同的时候,要求子类型(接口或类)也必须实现该默认方法。

    📝 实现(implements)的多个接口中的默认方法有相同的方法签名的的时候,该方法的返回类型也必须一样。

    public interface Eatable {
        default String test(String text) {
            System.out.println("Eatable_test(String text)");
            return text;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public interface Testable {
        default String test(String txt) {
            System.out.println("Testable_test()");
            return txt;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    接口:

    public interface Demoable extends Eatable, Testable {
        @Override
        default String test(String text) {
            Eatable.super.test("Eatable_test");
            Testable.super.test("Testable_test");
            System.out.println("Demoable_test(String text)");
            return text;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    类:

    public class TestDemo implements Eatable, Testable {
        @Override
        public String test(String text) {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2) 静态方法

    📝 接口中定义的静态方法只能通过接口名调用
    📝 接口中的静态方法不会被继承

    public interface Runnable {
        static void move() {
            System.out.println("Runnable_move()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public interface Eatable extends Runnable {
        static void move() {
            System.out.println("Eatable_move()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public interface Walkable extends Eatable {
        static void move() {
            System.out.println("Walkable_move()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class TestDemo implements Runnable, Eatable, Walkable {
        public static void main(String[] args) {
            Runnable.move();
            Eatable.move();
            Walkable.move();
    
            /*
                output:
                    Runnable_move()
                    Eatable_move()
                    Walkable_move()
             */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    三、匿名类(实用)

    📋 当接口或抽象类的实现类在整个项目中只使用过一次的时候,可以考虑使用匿名类(Anonymous Class)

    接口:

    /**
     * 一个测试接口
     */
    public interface Testable {
        String test(String txt);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class TestDemo1 {
        public static void main(String[] args) {
            /* 匿名类实现 Testable 接口里面的方法 */
            Testable testable = new Testable() {
                @Override
                public String test(String txt) {
                    System.out.println("匿名类:test(String txt)");
                    return txt + "_Happy";
                }
            };
            
            /*
                匿名类:test(String txt)
                庆医_Happy
             */
            System.out.println(testable.test("庆医"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    抽象类:

    /**
     * 一个测试抽象类
     */
    public abstract class TestAbstract {
        public abstract void testTest();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class TestDemo2 {
        public static void main(String[] args) {
            /* 通过匿名类实现 TestAbstract 里面的抽象方法*/
            TestAbstract testAbstract = new TestAbstract() {
                public void testTest() {
                    System.out.println("TestAbstract_testTest()");
                }
            };
    
            // TestAbstract_testTest()
            testAbstract.testTest();
    
            /* 创建完匿名类对象后就调用里面的方法 */
            new TestAbstract() {
                public void testTest() {
                    System.out.println("创建完匿名类对象后就调用里面的方法");
                }
            }.testTest();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (1) 匿名类细节

    📋 匿名类内部不能定义除编译时常量之外的任何static成员

    📖 static成员通常通过类名访问,匿名类连名字都没有咋访问啊!

    📋 匿名类和局部类一样,都只能访问 final 或有效 final 的局部变量

    📋 匿名类可以直接访问外部类中的所有成员(即使被声明为 private

    📋 匿名类只有在实例相关的代码块中使用,才可直接访问外部类中的实例成员

    📋 匿名类不能自定义构造方法

    📋 匿名类可以有初始化块

    (2) 匿名类的应用(☆)

    ① 代码传递

    🍀 写一个工具类 codeTimes
    🍀 作用:可计算一段代码的执行时间
    🍀 可使用接口实现代码传递

    工具类:

    /**
     * 计算某段代码的执行时间
     */
    public class CodeTimes {
    
        /**
         * 代码块(以 Inter 结尾表示接口)
         */
        public interface BlockInter {
            void passCode(); // 传递代码
        }
    
        public static void check(String description, BlockInter codeBlock) {
        	if(codeBlock == null) return;
        	
            System.out.println("\n----------------------------");
            System.out.println("测试功能:" + description);
            long beginTime = System.currentTimeMillis();
    
            codeBlock.passCode();
    
            long endTime = System.currentTimeMillis();
            long duration = (endTime - beginTime) / 1000;
            System.out.println("代码执行花费时间:" + duration + "秒");
            System.out.println("----------------------------");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    测试类:

    public class TestDemo {
        public static void main(String[] args) {
            CodeTimes.check("测试 1", new CodeTimes.BlockInter() {
                @Override
                public void passCode() {
                    String string = "Hello Friend";
                    for (int i = 0; i < 30000; i++) {
                        string += i;
                    }
                }
            });
    
            CodeTimes.check("测试2", new CodeTimes.BlockInter() {
                @Override
                public void passCode() {
                    test();
                }
            });
    
        }
    
        /**
         * 待测试的方法
         */
        public static void test() {
            StringBuilder string = new StringBuilder("Hello Friend");
            for (int i = 0; i < 30000; i++) {
                string.append(i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ② 过滤器

    public class Files {
        public interface Filter {
            boolean accept(String filename);
        }
    
        public static String[] getAllFilenames(String dir, Filter filter) {
    		if(filter == null || dir == null || "".equals(dir)) return new String[];
            // 1.获取 dir 文件夹下的全部文件名
            String[] filenames = {};
    
            // 2.进行过滤
            for (String filename : filenames) {
                if (filter.accept(filename)) {
                    // 文件名符合要求, 保存起来
                }
            }
            // 3.返回所有符合过滤条件的文件名
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    public class TestDemo {
        public static void main(String[] args) {
            Files.getAllFilenames("D://gq", new Files.Filter() {
                @Override
                public boolean accept(String filename) {
                    return filename.contains("爱");
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ③ 回调

    public class Life {
        public interface Callback {
            void person(String money);
    
            void animal();
        }
    
        public static void check(String name, Callback callback) {
        	if(callback == null) return;
            boolean result = isGoodPerson(name);
            if (result) {
                String money = "1000万";
                callback.person("\n" + name + "_获得_" + money);
            } else {
                callback.animal();
            }
        }
    
        private static boolean isGoodPerson(String name) {
            return !name.equals("坏人");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    public class TestDemo {
        public static void main(String[] args) {
            Life.check("庆医", new Life.Callback() {
                @Override
                public void person(String money) {
                    System.out.println(money);
                }
    
                @Override
                public void animal() {
                    System.out.println("\n坏人_当动物");
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    🍀 匿名类的作用本质上是代码传递,第一次学习的时候对它的理解非常懵,只觉得它非常好用。假如你是初学者,我相信你也会很懵。慢慢来,后面有使用场景的时候你就一目了然了。

    如有错误,请不吝赐教

  • 相关阅读:
    记一次生产内存溢出分析解决
    Redis淘汰和过期策略
    【Java编程进阶】标识符和关键字
    实战案例:如何批量查询物流信息并将结果导出到表格?
    Vue学习:分析hello案例
    51.MongoDB聚合操作与索引使用详解
    Secureboot从入门到精通
    智能合约安全新范式,超越 `require`和`assert`
    RabbitMQ手把手教程(一)概念
    【附源码】计算机毕业设计java在线药物配送系统设计与实现
  • 原文地址:https://blog.csdn.net/m0_54189068/article/details/126845171