第13讲 | 谈谈接口和抽象类有什么区别?

典型回答

接口和抽象类是 Java 面向对象设计的两个基础机制。
接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到 API 定义和实现分离的目的。接口,不能实例化;不能包含任何非常量成员,任何 field 都是隐含着 public static final的意义;同时,没有非静态方法实现,也就是说要么是抽象方法,要么是静态方法。

抽象类是不能实例化的类,用 abstract 关键字修饰 class,其目的主要是代码重用。除了不能实例化,形式上和一般的 Java类没什么区别。可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关 Java 类的共用方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。

Java 类实现 interface 使用 implements 关键词,继承 abstract class 则是使用 extends 关键词。

为接口添加任何抽象方法,相应的所有实现了这个接口的类,也必须实现新增方法,否则会出现编译错误。对于抽象类,如果我们添加非抽象方法,其子类只会享受到能力扩展,而不用担心编译错误。

有一类没有任何方法的接口,通常叫作 Marker Interface,它的目的就是为了声明实现接口的类具有某种特性。从表面看,这似乎和 Annotation 异曲同工,也确实如此。对于 Annotation,因为可以指定参数和值,在表达能力更强。

Java 8 增加了函数式编程的支持,所以又增加了一类定义,即所谓 functional interface,简单说就是只有一个抽象方法的接口,通常建议使用 @FunctionalInterface Annotation来标记,Lambda 表达式本身可以看作是一类 functionalInterface.

从 Java 8 开始,interface 增加了对 对 default method 的支持。Java 9 以后甚至可以定义 private default method。Default method 提供了一种二进制兼容的扩展已有接口的方法。

面向对象设计

基本设计原则SOLID

  • 单一职责(Single Responsibility),类或或者对象最好是只有单一职责,在程序设计中如果发现某个类承担着多种义务,可以考虑进行拆分。

  • 开关原则(Open-Close, Open for extension, close for modification),对扩展放开,对修改关闭。程序设计应保证平滑的扩展性,尽量避免因为新增同类功能而修改已有功能。这样可以少产出些回归(regression)问题。

  • 里氏替换(Liskov Substitution),这是面向面向对象的基本要素之一,进行-继承关系抽象时,凡是可以用父类或者基类的地方,都可以用子类替换。

  • 接口分离(Interface Segregation),我们在进行类和接口设计时,如果在一个接口里定义了太多方法,其子类很可能面临两难,就是只有部分方法对它是有意义的,这就破坏了程序的内聚性。 对于这种情况,可以通过拆分成功能单一的多个接口,将行为进行解耦。在未来维护中,如果某个接口设计有变,不会对使用其他接口的子类构成影响。

  • 依赖反转(Dependency Inversion),实体应该依赖于抽象而不是实现。也就是说高层次模块,不应该依赖于低层次模块,而是应该基于抽象。实践这一原则是保证产品代码之间适当耦合度的法宝。

你可能感兴趣的:(第13讲 | 谈谈接口和抽象类有什么区别?)