Java 中抽象类与接口的区别
作者:Pankaj
Java 中抽象类与接口的区别是最流行的面试问题之一。抽象类和接口都是Java 编程语言的核心部分。每个架构师都会面临选择抽象类还是接口这个设计决策。本文将会介绍抽象类和接口之间的区别,分辨什么情况下应当选择接口,而什么情况下又应当选择抽象类。
抽象类和接口的区别
abstract
关键字用于创建抽象类,同时还可以用于创建方法;而interface
关键字只能用于接口,不能用来创建方法。- 抽象类的子类需使用
extends
关键字来扩展父类,而且还需实现父类中所有声明了的方法,除非子类本身也是抽象类。而接口的子类使用implements
关键字实现接口,且必须实现所有接口中声明的方法。 - 抽象类中可以存在已实现的方法,而接口中的方法必须为抽象方法,不能有任何方法已被实现。值得注意的是,从 Java 8 开始,即使在接口中也可以实现默认方法和静态方法。
- 抽象类可以有构造方法,但接口不可以。
- 抽象类具有普通 java 类的所有特性,只是不能被实例化。我们可以通过
abstract
关键字使一个类抽象化。而接口是一个完全不同的类型,接口中只能有公共静态(public static)的 final 常量和方法声明。 - 抽象类的方法可以拥有public(公共),private(私有),protected(保护)和static(静态)等访问权限修饰符,而对于接口中的方法,虽然没有直接体现,但它们都是 public (公共)且 abstract (抽象)的,不能对这些方法使用其它的访问权限修饰符。
- 一个子类只能继承一个抽象类,但可以实现多个接口。
- 抽象类可以同时继承其它类并实现接口,而接口只能扩展自其它接口。
- 如果抽象类有
main()
方法,就可以运行。但接口不能实现 main 方法,所以无法运行。 - 接口用于定义子类的合约,虽然抽象类也可以定义合约,但它还可以为子类提供其它方法实现。
这就是接口和抽象类之间的全部区别。现在我们可以继续了解何时该使用接口,以及何时该使用抽象类。
接口还是抽象类
当涉及选择接口还是抽象类为子类提供合约时,很多因素都会影响这个设计决策。让我们来看看什么情况下接口是最优解,而什么情况下又该选择抽象类。
- Java 不支持多类继承,因此每个类只能继承一个父类。但一个类可以实现多个接口。所以大多数情况下,当需要为类的层级和合约提供基类时,接口会是一个好选择。同样,根据接口编程也是 java 编程的最佳实践之一。
- 如果合约中有很多方法,抽象类则更加有用,因为抽象类可以为一些所有子类通用的方法提供默认实现。此外,如果子类不需要实现抽象父类的某一方法,它就可以不实现。但如果子类扩展的是接口,就必须实现所有方法,即使这些实现只是空块,没有实际用处。
- 如果基类契约会不断变化,那么使用接口就会出现问题。因为如果不更改所有的实现类,就无法在接口中声明其它方法。而使用抽象类就可以提供默认实现,只修改需要实际运行新方法的子类。
同时使用抽象类及接口
同时使用抽象类与接口是设计系统的最佳方法。例如,在 JDK 中,java.util.List
这个接口中包含很多方法,所以对应着有一个 java.util.AbstractList
抽象类,这个抽象类实现了 List 接口中所有方法的基本结构,所以任何继承了这个抽象类的子类只需再实现要用到的方法即可。我们应始终以接口为基类,并定义所有子类需要实现的方法。如果一些方法只有某个子类需要实现,我们可以扩展基类接口,用这些方法创建一个新接口。子类可以按需选择实现基类接口还是子接口。如果方法的数量还会大幅增长,也可以创建一个实现子接口的骨架抽象类,使子类可以灵活选择接口或抽象类。
Java 8 中接口的改变
从 Java 8 开始,我们可以在接口中创建并实现默认方法和静态方法。这一点弥补了抽象类与接口之间的差距。现在,接口成为了大势所趋,因为我们可以为新方法提供默认实现,从而进一步扩展接口。