类、抽象类、接口之间的区别
从含义上讲区别
-
类:可以理解为一个模具,它定义了一个具体对象应该具有的属性和行为,利用类可以创建具有相同属性和行为的多个对象。
如:“老虎”就是一个类,它的皮毛是黄黑相间的(属性),它捕食食草动物(行为)。
-
抽象类:抽象类是对多个类具有的共同的属性和行为的提炼,因此抽象类中包含了子类共有的成员变量和方法。抽象类中包含的方法分为两类:
- 子类共有的方法。每个子类对这些方法的实现是相同的,因此抽象类中给出了这些方法的具体的实现,一旦定义好,所有子类均共享该方法。
- 各子类需要具体实现的抽象方法。每个子类对这些方法实现的方式各不相同,但具有相同的特征(触发条件和结果——即输入和输出),因而这些方法在抽象类中仅有声明、而没有具体实现,由各子类完成具体的实现。
如:“猫科动物”就是一个抽象类,该类定义了此类动物擅长攀爬和跳跃、喜欢独居等属性,并具有食肉、捕杀等行为;该类对多种动物的特征进行的抽象总结,需要通过“老虎”、“猫”、“猎豹”等子类来实现,且每个子类具有其特有的属性、并对对上述行为的实现方式各不相同。
-
接口:接口可以理解为一种协议(或者说规则),不同于类和抽象类关注类别、接口关注的是行为,它定义了一类行为的规则(即输入和输出)。不同的类可以对接口中定义的行为有不同的实现方式、同一个类也可以实现多个接口。
如:当一个软件被划分为多个模块由不同小组来开发时,每个小组只需要提供自己负责的模块的接口以规定其余模块与自己模块间的交互方式,每个小组也不需要关注其他模块的内部实现。
从语法上讲区别
-
类:使用
class
关键字标识public class A { private static member; // 成员变量 ... public A() {} // 构造函数 public void method() {} // 方法 ... }
-
抽象类:使用
abstract
关键字标识抽象类,使用extends
关键字继承抽象类public abstract class A { private static member; // 成员变量 ... public A() {} // 构造函数 public void method1() {} // 普通方法 ... public abtract void method2(); // 抽象方法,无函数体 ... } public class B extends A { public void method2() {} // 抽象方法的实现 }
-
接口:使用
interface
关键字标识接口,使用implements
关键字实现接口public interface A { int member; // 成员变量必须为public static final,可以省略 // 无构造函数 int method1(); // 接口中方法的声明默认访问方式为public,可以省略,无函数体 int method2(); ... } public class B implements A { public int method1() {} // 接口的实现 public int method2() {} ... }
-
** 接口和抽象类的区别 **
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的;且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类中的变量默认是 default 型,其值可以在子类中重新定义,也可以重新赋值。
- 抽象类中可以有非抽象方法,接口中则不能有非抽象方法。
- 接口可以省略 abstract 关键字,抽象类不能。
- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
从使用上讲区别
- 接口/类可以继承多个接口以组成新接口、但抽象类只能继承一个抽象类。
- Java 的单继承性决定了类只能继承一个类、但可以实现多个接口。因此接口可以使一个类除继承的主类的行为外、还能具有一些其他类具有的行为,实现了类型的多重继承。
- 抽象类里可以有普通方法,因此在抽象类中添加一个方法,其所有子类均具有了该方法;而若在接口里添加一个新方法,其所有子类需要首先先实现该方法。
如何使用
- 在下列情况下,请考虑使用抽象类:
- 希望在几个相关的类之间共享代码。
- 希望继承抽象类的子类们具有许多通用的方法或字段,或者需要除
public
之外的访问修饰符(例如protected
和private
)。- 希望声明
non-static
或non-final
的字段,以便定义可以改变具有这些属性的对象的状态的方法。- 在下列情况下,请考虑使用接口:
- 希望不相关的类实现相同的方法。 例如,Comparable和Cloneable接口由许多不相关的类实现。
- 希望指定特定数据类型的行为,但不关心谁实现其行为。
- 希望利用类型的多重继承。
经典设计模式
缺省适配模式:
graph LR
A[接口] --> B[抽象类]
B --> C[普通类]