一、抽象类和接口的选择
📋 选择抽象类
📝 在紧密相关的类之间共享代码(有关联的类之间共享代码)
📝 需要除 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坏人_当动物"); } }); } }
🍀 匿名类的作用本质上是代码传递,第一次学习的时候对它的理解非常懵,只觉得它非常好用。假如你是初学者,我相信你也会很懵。慢慢来,后面有使用场景的时候你就一目了然了。
如有错误,请不吝赐教