接口和抽象类

引言

由于面试笔试也时不时会问到接口和抽象类的区别,而且本人也因为接口和抽象类的相似性而对两者的适用场景感到疑惑,因此有了这篇文章。该文章大部分内容来自Java的接口和抽象类

接口和抽象类的定义

首先给出接口和抽象类的定义,由于各个资料的说法都不一样,所以我统一摘取了百度百科的定义:

接口:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)

抽象类:用abstract 关键字来修饰一个类时,这个类叫作抽象类。抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。抽象类可以看作是对类的进一步抽象。在面向对象领域,抽象类主要用来进行类型隐藏

接口和抽象类的区别

个人认为,接口用来描述方法的集合,抽象类则是对父类的进一步抽象化。

两者的共同点在于,都是对“子类”的进一步抽象化。他们都包含抽象(abstract)方法;

区别在于接口着重于功能的抽象化,而抽象类则不仅仅是对功能的抽象化,同时也包含子类通用的实现。

可以分为两个角度进行说明:

从语法的角度

  1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法(Java SE 8提供了default关键字来为接口中的抽象方法提供默认实现,这也导致了抽象类和接口越来越像了,不过它的重要作用其实是“接口演化”,从而实现代码兼容);

  2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final(常量)类型的;

  3)抽象类可以有静态代码块和静态方法,而接口中不能含有静态代码块以及静态方法(Java SE8 允许接口中增加静态方法);

  4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

可以看到,虽然两者有一定的相似性,但是抽象类更加接近于类,而接口更趋向于一种行为的规范

从设计层面的角度

​ 1)抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

​ 2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

​ 更细节的来讲,可以看以下表格,该表格来自Java的接口和抽象类

参数 抽象类 接口
默认的方法实现 它可以有默认的方法实现 接口完全是抽象的。它根本不存在方法的实现(Java SE 8之后有默认实现)
实现 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
构造器 抽象类可以有构造器 接口不能有构造器
与正常Java类的区别 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 接口是完全不同的类型
访问修饰符 抽象方法可以有publicprotecteddefault这些修饰符 接口方法默认修饰符是public。你不可以使用其它修饰符。
main方法 抽象方法可以有main方法并且我们可以运行它 接口没有main方法,因此我们不能运行它。
多继承 抽象方法可以继承一个类和实现多个接口 接口只可以继承一个或多个其它接口
速度 它比接口速度要快 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
添加新方法 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 如果你往接口中添加方法,那么你必须改变实现该接口的类(如果没有默认实现的话)

接口和抽象类的使用场景

那么什么时候使用抽象类,什么时候使用接口呢?

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

abstract关键字

由于接口和抽象类都和abstract关键字紧密相关,因此这里介绍一下abstract关键字

abstract的修饰范围

以下来自java中abstract详解

abstract关键字一般会出现在抽象类和接口中,可以修饰类、方法

  • 如果用来修饰

如果将一个类设置为abstract,则此类必须被继承使用。此类不可生成对象,必须被继承使用。 Abstract可以将子类的共性最大限度的抽取出来,放在父类中,以提高程序的简洁性。 Abstract虽然不能生成对象,但是可以声明,作为编译时类型,但不能作为运行时类型

  • 如果用来修饰方法

此时该方法为抽象方法,此时方法不需要实现(但是在接口中也可以使用default关键字实现),实现留给子类覆盖,子类覆盖该方法之后方法才能够生效

abstract关键字和哪些关键字不能共存?

以下来自java接口和抽象类的区别和作用(功能、用途、好处)

final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。
而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了