接口和抽象类的区别

Posted by 汤键|兔子队列 on November 17, 2021 禁止转载
本文总共 3263 字 · 阅读全文大约需要 10 分钟

序章

  • 抽象是隐藏实现并且只向用户提供基本细节的过程
  • Java 中的抽象是通过抽象类和接口实现的
  • 抽象类和接口有一些共同点,但它们之间有很大的不同

注意JDK版本

  • 接口和抽象类,最明显的区别就是接口只是定义了一些方法而已,在不考虑Java8中default方法情况下,接口中是没有实现的代码的
  • 在 JDK 8 之后 static 和 default 方法必须有方法实现
  • 抽象类可以有构造器,而接口不能有构造器
  • 抽象类中的抽象方法可以有public、protected和default这些修饰符
  • 而接口中默认修饰符是public,不可以使用其它修饰符
  • JDK9的接口被允许定义私有方法
  • 总结一下JDK7-JDK9 Java中接口概念的变化:
    • 在JDK7或更早的版本中,接口里面只能有常量变量和抽象方法;这些接口方法必须由选择接口的类实现
    • JDK8的时候接口可以有默认方法和静态方法功能
    • JDK9在接口中引入了私有方法和私有静态方法

关键字不同

  • 接口使用关键字 interface 来定义
  • 抽象类使用关键字 abstract 来定义
  • 类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类
  • 而接口只是一个行为的规范或规定
  • 接口使用 implements 关键字定义其具体实现
  • 抽象类使用 extends 关键字实现继承
  • 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法

扩展不同

  • 在 Java 语言中,一个类只能继承一个父类(单继承),但可以实现多个接口
  • 接口的实现类可以有多个,而抽象类的子类,只能继承一个抽象类,继承多个抽象类就会报错
  • 添加新方法
    • 如果你往抽象类中添加新的方法,你可以给它提供默认的实现,因此你不需要改变你现在的代码
    • 如果你往接口中添加方法,那么你必须改变实现该接口的类

职责不同

  • 接口和抽象类的职责不一样
  • 接口主要用于制定规范,因为我们提倡也经常使用的都是面向接口编程
  • 而抽象类主要目的是为了复用,比较典型的就是模板方法模式
  • 所以当想要定义标准、规范的时候,就使用接口
  • 当想要复用代码的时候,就使用抽象类
  • 一般在实际开发中,会先把接口暴露给外部,然后在业务代码中实现接口
  • 如果多个实现类中有相同可复用的代码,则在接口和实现类中间加一层抽象类,将公用部分代码抽出到抽象类中
  • 可以参考下模板方法模式,这是一个很好的理解接口、抽象类和实现类之间关系的设计模式
  • 抽象类表示的是,这个对象是什么
  • 接口表示的是,这个对象能做什么

设计层面不同

  • 抽象类作为很多子类的父类,它是一种模板式设计
  • 而接口是一种行为规范,它是一种辐射式设计
  • 什么是模板式设计?
  • 简单例子,大家都用过 ppt 里面的模板,如果用模板 A 设计了 ppt B 和 ppt C , ppt B 和 ppt C 公共的部分就是模板 A 了,如果它们的公共部分需要改动,则只需要改动模板 A 就可以了,不需要重新对 ppt B 和 ppt C 进行改动
  • 而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新
  • 也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更
  • 而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动
  • 抽象类 和 接口 都是用来抽象具体对象的,但是接口的抽象级别更高

接口Interface

  • 硬件接口:是指同一计算机不同功能层之间的通信规则称为接口
  • 软件接口:是指对协定进行定义的引用类型;其他类型实现接口,以保证它们支持某些操作
  • 在Java中接口需要用interface关键字定义,他是对一种行为的抽象,是一种约定的协议,是一种行为的规范,只定义行为不会实现具体的行为
  • 在接口中的行为一般必须都是公共的,如果定义成员变量也必须是静态不可变的(static final)
  • 接口中定义的行为都是abstract的,也可以理解为特殊的抽象
  • 接口是抽象方法的集合
  • 如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法,这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法
  • 接口只是一种形式,接口自身不能做任何事情
  • 标志接口:仅仅作为一个定义,就是一个标志
  • 常量接口:用来封装多个常量信息

抽象类abstract class

  • 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
  • 抽象类需要用abstract class关键字来定义,表示一个类为抽象类
  • 只要满足被 abstract 类修饰的类就是一个抽象类
  • 也就是说,抽象类可以没有抽象方法,可以没有任何一个抽象方法
  • 一个类要是有抽象方法那么这个类就必须是一个抽象类,即被 abstract 修饰
  • 也就是说抽象类只是比普通类多了一个abstract 关键字,即不能创建对象,还有写不写抽象方法的选择 , 成员变量,构造方法等等它都可以有
  • 但是大多数情况下抽象类是都会有抽象方法的,比如 JDK 中的 AbstractMap,抽象类设计主要是为了把共性的东西让子类去继承(成员变量和成员方法),把包含子类特性的方法定义成抽象的,让子类自己去发挥
  • 抽象类不能创建实例,它只能作为父类被继承
  • 抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象
  • 从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类的随意性
  • 抽象类是对一类事物共性的一种抽象,是对类的抽象,是一种模板设计,实现公共的行为,并且构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式
  • 抽象类是用来捕捉子类的通用特性的,它不能被实例化,只能被用作子类的超类,抽象类是被用来创建继承层级里子类的模板

什么时候使用抽象类和接口

  • 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧
  • 如果你想实现多重继承,那么你必须使用接口;由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口;因此你就可以使用接口来解决它
  • 如果基本功能在不断改变,那么就需要使用抽象类;如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类

  • Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现
  • Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现
  • OOP面向对象的编程,如果要提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些太有用的抽象类型做为java结构层次上的顶层
  • Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以,这大概就是Java抽象类少数的优点吧,但这个优点非常有用
  • 如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点
  • 一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣
  • 在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型
  • 抽象类的功能要远超过接口,但是,定义抽象类的代价高

  • 当你关注一个事物的本质的时候,用抽象类
  • 当你关注一个操作的时候,用接口

抽象性和具体性

  • 简单说,概念的内涵越小,则其抽象程度就越高,其外延也越大,反之亦然
  • 比如“人”比“男人”抽象一点,而“生物”又比“人”更抽象一点,“物质”则比“生物”更抽象
  • 抽象的概念是由具体概念依其“共性”而产生的,把具体概念的诸多个性排出,集中描述其共性,就会产生一个抽象性的概念
  • 抽象思维,是人类思维达到阶段产生的一种能力
  • 例如,当小孩子思维尚未成熟时,他们只能掌握具体概念,他们在学习代词“你、我、他”时往往遇到困难,因为代词具有较高的抽象性
  • 总之,抽象概念的外延大,内涵小,具体概念的外延小,内涵大