第22篇:接口与抽象类、匿名类的介绍

简介: 📋 匿名类和局部类一样,都只能访问 final 或有效 final 的局部变量📋 匿名类可以直接访问外部类中的所有成员(即使被声明为 private)📋 匿名类只有在实例相关的代码块中使用,才可直接访问外部类中的实例成员📋 匿名类不能自定义构造方法📋 匿名类可以有初始化块

一、抽象类和接口的选择

📋 选择抽象类

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

📋 选择接口

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

二、接口中的默认方法

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

(1) 默认方法

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


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

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

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

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

默认方法细节

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

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

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

public interface Eatable {
    default void eat() {
        System.out.println("Eatable_eat()");
    }
}
public abstract class Father {
    public abstract void eat();
}
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()");
    }
}

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

📝 实现( implements)的多个接口中的默认方法有相同的方法签名的的时候,该方法的返回类型也必须一样。
public interface Eatable {
    default String test(String text) {
        System.out.println("Eatable_test(String text)");
        return text;
    }
}
public interface Testable {
    default String test(String txt) {
        System.out.println("Testable_test()");
        return txt;
    }
}

接口:

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;
    }
}

类:

public class TestDemo implements Eatable, Testable {
    @Override
    public String test(String text) {
        return null;
    }
}

(2) 静态方法

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

public interface Runnable {
    static void move() {
        System.out.println("Runnable_move()");
    }
}
public interface Eatable extends Runnable {
    static void move() {
        System.out.println("Eatable_move()");
    }
}
public interface Walkable extends Eatable {
    static void move() {
        System.out.println("Walkable_move()");
    }
}
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()
         */
    }
}

三、匿名类(实用)

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

接口:

/**
 * 一个测试接口
 */
public interface Testable {
    String test(String txt);
}
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("庆医"));
    }
}

抽象类:

/**
 * 一个测试抽象类
 */
public abstract class TestAbstract {
    public abstract void testTest();
}
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) 匿名类细节

📋 匿名类内部不能定义除编译时常量之外的任何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("----------------------------");
    }
}

测试类:

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);
        }
    }
}

② 过滤器

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;
    }
}
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("爱");
            }
        });
    }
}

③ 回调

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("坏人");
    }
}
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坏人_当动物");
            }
        });
    }
}
🍀 匿名类的作用本质上是 代码传递,第一次学习的时候对它的理解非常懵,只觉得它非常好用。假如你是初学者,我相信你也会很懵。慢慢来,后面有使用场景的时候你就一目了然了。

如有错误,请不吝赐教

相关文章
|
Java
Java接口和抽象类
Java接口和抽象类
87 0
|
15天前
抽象类和接口在实现多态时的区别
【10月更文挑战第19天】抽象类和接口在实现多态时都有其独特的作用。抽象类更适合用于定义一些具有共同特征和部分实现的类,而接口则更强调定义一组行为规范,以便不同的类可以实现并组合这些行为。
49 10
|
30天前
|
Java 编译器
关于抽象类和接口(详解)
关于抽象类和接口(详解)
30 6
|
2月前
|
编译器
方法重载与抽象类3-13
方法重载与抽象类3-13
|
1月前
|
程序员 C++
抽象类与接口
抽象类与接口
27 0
|
6月前
|
Java 编译器 程序员
重写、抽象类、接口
Java编程涉及重载、重写、final、super、抽象类、static和接口。方法重载允许同名方法,但参数列表需不同;方法重写在子类中重新定义父类方法,保持相同签名。final修饰的类不可继承,final方法不可重写。抽象类包含抽象方法,不能实例化,子类必须重写抽象方法。static修饰静态属性和方法,与对象无关。接口是抽象类的极致形式,包含常量和抽象方法,实现接口需重写方法。Java中,接口可多继承,而类单继承。
39 0
|
3月前
|
Java
抽象类和接口类的区别是什么?
【8月更文挑战第6天】抽象类和接口类的区别是什么?
254 0
|
6月前
|
设计模式 Java
Java接口与抽象类
Java接口与抽象类
42 0
|
6月前
|
Java
22、接口与抽象类、匿名类的介绍
22、接口与抽象类、匿名类的介绍
41 0
|
存储 Cloud Native Linux
C++的纯虚函数使用与接口实现
C++的纯虚函数使用与接口实现