接口和抽象类是Java面向对象设计的两个基础机制。

接口是对行为的抽象,是抽象方法的集合,利用接口可以达到API定义和实现分离的目的。接口,不能实例化;不能包含任何非常量成员,任何field都是隐含着public static final的意义;方法默认是public abstract修饰。Java8引入了一种新特性,为了使接口具有更大的灵活性,接口提供了静态方法和默认方法,静态方法是指在返回值前加static关键字,默认方法是指在返回值前加default关键字。示例如下:

  1. public interface MethodTest {
  2. /**
  3. * 默认方法
  4. */
  5. default void name(){
  6. System.out.println("java");
  7. }
  8. /**
  9. * 静态方法
  10. */
  11. static void modify(){
  12. System.out.println("java");
  13. }
  14. }

抽象类是不能实例化的类,用abstract关键字修饰class,其目的主要是代码重用。除了不能实例化,形式上和一般的Java类没有太大的区别,可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关Java类的共用方法实现或者共同成员变量,然后通过继承的方式达到代码复用的目的。Java标准库中,比如collection框架,很多通用部分就被抽取成为抽象类,例如java.util.AbstractList。

Java类实现interface使用implements关键字,继承abstract class则是使用extends关键词。可以参考ArrayList:

  1. public class ArrayLis<E> extends AbsractLis<E>
  2. implements Lis<E>, RandomAccess, Cloneable, java.io.Serializable
  3. {
  4. //...
  5. }

知识扩展

  • Java不支持多继承,可以实现多个接口。
  • 有一类没有任何方法的接口,通常叫作Marker Interface,它的目的就是为了声明某些东西,比如Cloneable、Serializable等。
  • Java8增加了函数式编程的支持,所以又增加了一类定义,即所谓的functional interface,简单说就是只有一个抽象方法的接口,通常建议使用@FunctionInterface Annotation来标记。Lambda表达式本身可以看作是一类functional interface,某种程度上这和面向对象算是两码事。我们熟知的Runnable、Callable之类,都是functional interface。
  • 封装的目的是隐藏事务内部的实现细节,以便提高安全性和简化编程。
  • 继承是代码复用的机制。
  • 多态,重写。重写是父子类中相同名字和参数的方法,不同的实现。重载则是相同名字的方法,但是不同的参数。
  • 面向对象编程,基本的设计原则:
    • 单一职责:类或者对象最好是只有单一职责,如果某个类中承担着多种义务,可以考虑拆分。
    • 开关原则:对扩展开放,对修改关闭。尽量保证平滑的扩展。
    • 里式替换:进行继承关系抽象时,凡是可以用父类或者基类的地方,都可以用子类替换。
    • 接口分离:我们在进行类和接口设计时,如果在一个接口里定义了太多方法,其子类很可能面临两难,就是只有部分方法对它是有意义的,这就破坏了程序的内聚性。对于这种情况要拆分成多个接口。