抽象类和接口的区别(jdk8/jdk9)
抽象类和接口的区别
1、一个类可以实现多个接口,但只能继承一个抽象类。接口本身可以通过extends关键字扩展多个接口。
2、 抽象类可以有构造方法,接口中不能有构造方法。
3、抽象类中不一定都是抽象方法,也可以全是具体方法(非抽象方法)。在Java8之前,接口中的所有方法必须是抽象的。但Java8的时候,接口可以有默认方法和静态方法。
4、接口中只能有static final变量,不能有其他变量,而抽象类中不一定。
5、接口中的方法和变量默认修饰符为public(写不写都是public),而抽象类中的方法可以是public,protected和默认类型(抽象方法就是为了被重写所以不能是private修饰)
| 权限 | 类内 | 包内 | 不同包子类 | 不同包非子类 |
|---|---|---|---|---|
| private | √ | × | × | × |
| default | √ | √ | × | × |
| protected | √ | √ | √ | × |
| public | √ | √ | √ | √ |
关于抽象类:
JDK 1.8以前,抽象类的方法默认访问权限为protected
JDK 1.8时,抽象类的方法默认访问权限变为default
关于接口:
JDK 1.8以前,接口中的方法必须是public的
JDK 1.8时,接口中的方法可以是public的,也可以是default的
JDK 1.9时,接口中的方法可以是private的
6、从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
Java8接口新特性
Java8在接口中引入了默认方法和静态方法。
接口默认方法
默认方法是在接口中的方法签名前加上 default 关键字的实现方法。
/** * 简单例子 */ interface InterfaceA { default void foo() { System.out.println("InterfaceA foo"); } } class ClassA implements InterfaceA { } public class Test { public static void main(String[] args) { // 打印:“InterfaceA foo” new ClassA().foo(); } }
为什么要有默认方法?
在Java 8之前,接口与其实现类之间的耦合度过高(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法可以为接口添加新的方法,而不会破坏已有的接口的实现。这在lambda表达式作为Java 8语言的重要特性出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。
/** * Iterable接口中的forEach默认方法 */ public interface Iterable<T> { default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } }
默认方法的继承
接口默认方法的继承分三种情况(分别对应下面的InterfaceB接口、InterfaceC接口和InterfaceD接口):
1.不覆写默认方法,直接从父接口中获取方法的默认实现。
2.覆写默认方法,这跟类与类之间的覆写规则相类似。
3.覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
interface InterfaceA { default void foo() { System.out.println("InterfaceA foo"); } } interface InterfaceB extends InterfaceA { } interface InterfaceC extends InterfaceA { @Override default void foo() { System.out.println("InterfaceC foo"); } } interface InterfaceD extends InterfaceA { @Override void foo(); } public class Test { public static void main(String[] args) { // 打印:“InterfaceA foo” new InterfaceB() {}.foo(); // 打印:“InterfaceC foo” new InterfaceC() {}.foo(); new InterfaceD() { @Override public void foo() { // 打印:“InterfaceD foo” System.out.println("InterfaceD foo"); } }.foo(); // 或者使用lambda表达式 ((InterfaceD) () -> System.out.println("InterfaceD foo")).foo(); } }
注意:
如果某接口同事实现了两个接口,接口中定义了一样的默认方法,则必须重写,否则会报错。
例如:
public interface Ina { default void say(){ System.out.println("say a"); } }
public interface Inb { default void say(){ System.out.println("say b"); } }
如果classc在实现这两个接口时不重写say方法,那编译器无法确定到classc调用say方法时,选择哪一个接口的方法。因此Classc必须重写say:

于是:
public class classc implements Ina,Inb { @Override public void say(){ System.out.println("say c"); } }
根据上面提示的3条规则,也可以把say方法重新声明为抽象方法::
public interface Ind extends Ina{ public abstract void say(); }
接口静态方法
接口静态方法直接用接口名.方法名调用,实现类和实现接口是不可以调用的。
interface InterfaceA { default void foo() { printHelloWorld(); } static void printHelloWorld() { System.out.println("hello, world"); } } public class Test { public static void main(String[] args) { // 打印:“hello, world” InterfaceA.printHelloWorld(); } }
注意:
default关键字只能在接口中使用(以及用在switch语句的default分支),不能用在抽象类中;接口默认方法不能覆写Object类的equals、hashCode和toString方法;接口中的静态方法必须是public的,public修饰符可以省略,static修饰符不能省略。
Java9接口新特性
Java9在接口中引入了私有方法和私有静态方法。
由于不可被继承,因此私有方法必须定义方法体才有意义。同时也意味着接口的私有方法和私有静态方法不可被实现该接口的类或继承该接口的接口调用或重写。私有方法(private method)和私有静态方法(private static method)的提出都是对jdk8提出的default和public static method的补充。
默认方法和静态方法可以共享接口中的私有方法,因此避免了代码冗余,这也使代码更加清晰。如果私有方法是静态的,那这个方法就属于这个接口的。并且没有静态的私有方法只能被在接口中的实例调用。
