一、抽象类:把公共骨架抽出来
-
关键字 abstract
• 不能实例化;
• 可包含抽象方法(无方法体)和普通成员;
• 子类必须补全所有抽象方法,否则也得声明为抽象。
-
强制让子类实现某方法
方案一:运行时抛
UnsupportedOperationException
(不推荐,编译期不报错)。方案二:直接声明抽象方法,编译器帮你兜底。
二、接口:只描述能力,不提供实现
-
基本语法
javapublic interface 会飞 { void 飞(); // 默认 public abstract }
实现类:
javapublic class 鸟 extends 动物 implements 会飞, 会呼吸 { @Override public void 飞() { System.out.println("鸟儿飞"); } }
• 支持多继承,解决菱形问题;
• 成员变量默认
public static final
。 -
Java 8 后的默认方法
javainterface List<E> { default void sort(Comparator<? super E> c) { ... } }
带来便利的同时也可能出现"二义性",类优先原则或显式
X.super.f()
解决。 -
向后兼容
接口一旦发布很难改,默认方法可在不破坏旧实现的前提下扩展功能。
三、抽象类 vs 接口
维度 | 抽象类 | 接口 |
---|---|---|
继承/实现 | 单继承 | 多实现 |
构造器 | 有 | 无 |
成员变量 | 任意 | 仅 public static final |
方法 | 可有实现 | 默认无实现(8 后支持 default) |
设计语义 | is-a,模板 | has-a,能力 |
口诀:
"模板用抽象,能力用接口;多继承场景一定用接口。"
四、实战 1:文件过滤器
需求:递归查找指定后缀的文件。
• 第一版:独立 FileFilterVisitor
继承 SimpleFileVisitor<Path>
。
• 第二版:匿名内部类,把逻辑收拢,减少参数传递。
java
Files.walkFileTree(root, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(ext)) names.add(file.getFileName());
return CONTINUE;
}
});
五、实战 2:Comparable 与策略模式
• Comparable<T>
就是策略接口,sort 方法内部使用 compareTo
决定顺序。
• TreeSet
依赖 compareTo
判断相等,若比较器写错会导致元素"丢失"。
原则:不同对象绝不能让 compareTo
返回 0。
六、Predicate ------ JDK 自带"判断"策略
java
Predicate<String> p = s -> s.endsWith(".csv");
可组合 and/or/negate
,配合 Stream 一行搞定过滤。
七、内部类与匿名类
-
种类
• 成员内部类(非 static):隐式持有外部类实例。
• 静态内部类:不持有外部引用,可独立存在。
• 局部/匿名内部类:常见于回调、一次性实现。
-
面试问答
Q:
private class
与private static class
区别?A:前者需要外部实例,后者不需要,也不会造成隐式内存泄漏。
-
最佳实践
永远优先使用
static
内部类,除非必须访问外部实例字段。匿名类编译后生成$1
、$2
字节码文件,调试时注意。
八、小结与 checklist
• 需要"模板"→抽象类;需要"能力"→接口。
• 多实现、多继承场景→接口。
• 默认方法谨慎使用,避免二义性。
• 内部类默认加 static,防止内存泄漏。
• 匿名类适合一次性策略实现,代码紧凑。