抽象类和接口

1. 抽象类和接口的概念及区别

抽象类和接口的概念:

  • 抽象类是Java中用于实现抽象概念的一种特殊类,使用abstract关键字修饰,不能被直接实例化,通常作为对应子类的基类,提供共同的属性和部分行为的实现。
  • 接口是一种完全抽象的类型,使用interface关键字定义,同样也不能被实例化,接口用于描述类应该具有什么功能,但没有具体实现,当某个类要使用接口时再去具体实现其中的方法,接口是对外提供的一组规则、标准。
  • 抽象类相当于提供了一个模板,而接口更像是一个契约,规定了实现类必须遵守的行为规范。

抽象类和接口的区别:

维度 抽象类 接口
成员变量 可以有各种访问修饰符的实例变量、静态变量和常量 默认且必须是public static final,且必须在定义时就初始化
成员方法 可以有抽象方法和具体方法,抽象方法不能是private,static,final Java8之前只能有public abstract,Java8及以后可能有默认方法、静态方法,Java9及之后可以有私有方法
构造方法 可以有构造方法,用于子类初始化父类成员,但不能直接实例化 不能有构造方法
创建对象 不能直接实例化,只能通过子类(非抽象类)实例化 不能直接实例化,只能通过实现类(非抽象类)实例化,并通常通过多态引用
继承关系 使用extends关键字继承,只支持单继承 使用implements关键字实现,支持多实现(一个类可实现多个接口),接口之间可多继承
适用场景 表示"is-a"关系,作为类族共同的基类,提供模板方法模式 表示"can-do"或"has-a"关系,定义行为规则/标准,实现多态,弥补单继承机制的不足

2. Java8之后接口可以有默认方法和静态方法,这使得接口和抽象类看起来更相似了。那么,在什么情况下我仍然应该选择抽象类而不是接口?

  • 抽象类更适合“模板方法模式”,它可以定义算法的骨架,约束更多的“默认行为结构”,父类定义算法骨架,子类实现细节,这样可以更好地控制子类的行为。而接口默认方法只能提供独立的小功能,不能用来组织复杂流程。
  • 接口适合 “能力标签”:飞行、跳跃、可序列化、可比较、可克隆……行为可以随意组合。
  • 抽象类适合 “家族结构”:有共同属性、有共享构造、有统一流程、有基类逻辑。

3. 既然接口可以多继承,抽象类可以单继承,那么在设计时,什么时候优先考虑使用接口,什么时候优先考虑使用抽象类?

  • 当需要定义一组规则或标准,而与实现这组规则的具体实现类的类型无关时,优先考虑使用接口。例如,定义一个Serializable接口,表示类可以被序列化。当需要实现多重继承效果时,优先考虑使用接口,因为Java不支持类的多继承,但允许一个类实现多个接口。当项目对于耦合性比较严格时,优先考虑使用接口,因为接口可以降低类之间的耦合度,提高代码的灵活性和可维护性。
  • 当需要提供一个共同的基类,包含共享的属性和方法实现时,优先考虑使用抽象类。例如,定义一个Animal抽象类,包含动物的基本属性和行为。当需要在类层次结构中共享代码时,优先考虑使用抽象类,因为抽象类可以包含具体的方法实现,减少代码重复。
  • 如果侧重于“行为”,而不考虑具体如何实现,优先考虑使用接口;如果侧重于“类型”,且需要共享部分实现和属性,优先考虑使用抽象类。