接口是自上而下的,是一种规范,完全抽象,注重解耦合。
抽象类是自下而上的,本质是在一个具有类的基础上加上了抽象方法,即便有构造器,也不能创造对象,强调代码复用。
一个类可以实现多个接口,一个类只能继承一个抽象类。
接口和抽象类有诸多区别,具体如下:
1. 定义与目的: - 接口是一种行为规范,定义了一组方法的契约,但不提供具体实现,主要目的是确保类之间的协同工作,通过规定必须实现的方法来实现多态性。例如,`java.util.List`接口定义了一组操作列表的方法,如添加元素、删除元素、获取元素等,不同的实现类(如`ArrayList`、`LinkedList`)根据自身特点实现这些方法。 - 抽象类是对一系列看上去不同,但本质上相同的具体概念的抽象,定义了一组属性和方法的通用框架,其中一些方法可能是抽象的,需要在子类中实现,目的是为子类提供一个共享的、部分实现的基类。比如,有一个抽象类`Animal`,它包含了一些动物共有的属性(如名字)和方法(如移动的方法),但具体动物(如狗、猫)的移动方式不同,需要在子类中具体实现。
2. 成员变量: - 接口中的成员变量默认都是静态常量(`static final`),不能被修改。例如,在接口中定义`public static final int MAX_VALUE = 100;`,表示一个最大值常量。 - 抽象类中的成员变量可以有不同的修饰符,它们可以是静态的,也可以是实例变量,可以有不同的访问级别,如`public`、`protected`、`private`等。例如,抽象类中有`private int age;`表示私有成员变量年龄。
3. 方法: - 接口中的方法默认是公开的(`public`),且不能有具体实现(Java 8 之后允许接口定义默认方法和静态方法,但仍以抽象方法为主)。接口中的所有方法都是抽象的,例如`java.awt.event.MouseListener`接口中的`mouseClicked(MouseEvent e)`方法,需要在实现该接口的类中具体实现。 - 抽象类中的方法可以是抽象的,也可以是非抽象的。抽象方法没有具体实现,需要在子类中重写;非抽象方法可以有具体实现。抽象类中的方法可以有不同的访问级别。例如,抽象类中有`public abstract void draw();`抽象方法,要求子类实现具体的绘制功能,也可以有`protected void init() { }`等非抽象方法,为子类提供一些初始化的默认实现。
4. 实例化: - 接口不能被实例化,它只能被类实现。例如,不能直接创建`List`接口的对象。 - 抽象类不能被直接实例化,但可以通过其子类(如果子类不是抽象的)来实例化。比如,不能直接创建`Animal`抽象类的对象,但可以创建`Dog`(继承自`Animal`)类的对象。
5. 继承与实现: - 一个类可以实现多个接口,从而获取多个接口中的方法,这是接口实现多态性的一个重要方式。例如,一个类可以同时实现`Serializable`和`Cloneable`接口,使该类具备序列化和克隆的功能。 - 一个类只能继承一个抽象类(在大多数面向对象的编程语言中,如 Java),这种单继承的特性限制了抽象类的使用范围。例如,一个类只能继承一个特定的抽象类来获得其部分实现和通用属性。
6. 扩展性: - 接口具有很好的扩展性,可以很容易地添加新的方法,而不需要修改现有的实现类。例如,给`List`接口添加一个新的方法`removeIf(Predicate<? super E> filter)`,所有实现了`List`接口的类无需修改代码就能自动获得这个新方法。 - 抽象类的扩展性相对较差,如果添加新的抽象方法,所有子类都需要实现这个方法,这可能会影响到现有的代码。比如在抽象类`Animal`中添加一个新的抽象方法`sleep()`,那么所有继承自`Animal`的子类都必须实现这个方法,否则会编译错误。
7. 设计层面: - 抽象类是对类的抽象,是一种模板式设计。它对整个类进行抽象,包括属性和行为,子类必定是抽象类的一种类型,继承是“是不是”的关系。例如,`Bird`类继承自`Animal`抽象类,说明`Bird`是一种`Animal`。 - 接口是对行为的抽象,是一种辐射式设计。它是对类局部(行为)的抽象,接口实现是“有没有”的关系,比如一个类是否能实现某个接口,取决于它是否具备该接口所定义的行为。例如,一个类实现了`Flyable`接口,就表示它具备飞行的行为能力。