接口与抽象类

1 抽象类

  • 抽象类和抽象方法都使用 abstract 关键字进行声明。
  • 抽象方法,是一种约束,只有方法的声明,没有方法的实现。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。抽象类可以包含普通方法。
  • 抽象类,非抽象子类必须实现抽象方法。任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
  • 抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。
  • 抽象类没有构造方法。
1
2
3
4
5
6
7
8
9
10
11
public abstract class AbstractClassExample {

protected int x;
private int y;

public abstract void func1();

public void func2() {
System.out.println("func2");
}
}
1
2
3
4
5
6
public class AbstractExtendClassExample extends AbstractClassExample {
@Override
public void func1() {
System.out.println("func1");
}
}

2 接口

  • 接口规范、定义规则、本质上是契约。

  • 接口不能被实例化,接口中没有构造方法。必须重写接口中的方法。接口必须有实现类,才能进行实例化。实现了接口中的类,就必须实现接口中的方法。

  • 接口的成员(字段 + 方法)默认都是 public abstract的,并且不允许定义为 private 或者 protected。接口的字段默认都是 public static final 的。接口中一般不会定义常量。

  • 使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface InterfaceExample {

void func1();

default void func2(){
System.out.println("func2");
}

int x = 123;
// int y; // Variable 'y' might not have been initialized
public int z = 0; // Modifier 'public' is redundant for interface fields
// private int k = 0; // Modifier 'private' not allowed here
// protected int l = 0; // Modifier 'protected' not allowed here
// private void fun3(); // Modifier 'private' not allowed here
}
1
2
3
4
5
6
public class InterfaceImplementExample implements InterfaceExample {
@Override
public void func1() {
System.out.println("func1");
}
}

3 对比

抽象类和接口比较

  • 从设计层面上看,抽象类提供了一种 IS-A 关系,需要满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。
  • 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
  • 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
  • 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

抽象类和接口选择

使用接口:

  • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Comparable 接口中的 compareTo() 方法;
  • 需要使用多重继承。

使用抽象类:

  • 需要在几个相关的类中共享代码。
  • 需要能控制继承来的成员的访问权限,而不是都为 public。
  • 需要继承非静态和非常量字段。

在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。