接口的特性
- 对类的一组需求描述,对方法制定的一套规范。
- 接口没有实例对象(不能new实例对象),没有实例域(对象的值),
- 可以定义接口变量,引用实现了接口的类的对象
- 可用instanceof检查一个类是否实现了某接口:if(anObject isstanceof anInterface){…}
- 类只能extends一个超类,但却可以implements多个接口(逗号隔开)
- 接口可以extends多个接口(逗号隔开)
- 域:public static final (自动)
- 方法:public abstract (自动);default(为方法提供默认实现)
- 从Java SE 8开始,允许在接口中增加静态static方法,并给静态方法提供方法体实现,该静态方法只能通过
接口名.静态方法
来调用。实现语法只要在方法前面加static关键字即可,这理论上讲是可以的,但这有违于接口作为抽象规范的初衷。静态方法只能被具体实现类继承,不能在实现类中重写。通常将static方法放在接口的伴随类中。 - 类实现接口:implements声明,实现接口方法(显式声明public)
接口与抽象类的区别
接口interface | 抽象类abstract class |
interface Demo{ void method1(); //自动public abstract void method2(); } | public abstract class Demo{ abstract void method1(); void mehtod2(){…} } |
implements | extends |
public static final域 public abstract方法 default、static方法(Java 8新增) | 域、方法、构造器 包含抽象方法的类一定是抽象类, 不包含抽象方法的类也可以声明为abstract |
like a,子类实现接口方法后,具备相应功能 | 子类is a抽象类,是抽象类的特例化 |
子类可以implements多个接口 | 子类只能extends一个抽象类 |
接口可以extends多个接口 |
|
对行为的抽象,子类之间不必有联系 eg:鸟和飞机类都能“飞”,所以可以定义包含“飞”这个方法的接口,通过接口变量就可以调用不同类的飞行方式(多态) | 对子类的抽象,子类的公共属性、行为 eg:老师和学生类都是人,可以定义抽象类人,包含人的共同属性“姓名”、共同方法“呼吸”,和要根据子类特殊化的抽象方法“工作”,通过抽象类变量调用不同子类的工作方式(多态) |
共同点: 没有实例对象, 可以创建引用变量,引用子类对象 若子类没有实现所有抽象方法,则子类要声明为抽象类 |
接口的默认方法
- 默认方法用default修饰
- 默认方法与“接口演化”:为一个已经定义并使用很久的接口添加一个新的方法时,将这个方法定义为默认方法,就可以保证之前实现了这个接口的类仍能通过编译(重新编译后旧类中就有了接口中新添加的默认方法,即使这个旧类没有重新编译,通过jar文件加载,并用其实例调用到了新添加的默认方法,也会调用接口的默认方法,不会出错)
- 默认方法可以调用其他任何方法
默认方法冲突
- 情况1:超类优先。类扩展了超类且实现了接口,超类提供了一个具体方法,则接口同名同参的默认方法会被忽略。(但是如果接口的方法只是同名同参且不是默认方法,则会冲突报错)
- 情况2:类实现了两/多个接口,接口有同名同参方法,且至少有一个提供了默认实现,则类必须覆盖这个同名同参的方法来解决冲突(接口的共享方法都没有提供默认实现则不冲突)
- 1)超类和接口冲突。如果一个类继承了一个超类和实现了若干接口,此时不像默认方法冲突一样有超类优先原则。只能通过在实现类中覆盖该常量来解决冲突。
- 2)多接口之间冲突。如果一个类实现了多个接口,而这些接口又有重名常量,此时会发生冲突。必须用
接口名.常量
的方式来精确指明要使用的常量。
默认方法和多继承
1、多继承
- 若父接口有相同签名的默认方法,则子接口必须override,否则冲突报错 (方法签名:方法名,参数类型)
- 若父接口的默认方法的签名不同,则子接口同时继承所有默认方法
//D->A,B,C
interface A {
default void say(String name) {
System.out.println("hello " + name);
}
}
interface B {
default void say(String name) {
System.out.println("hi " + name);
}
}
interface C{
default void say(int name){
}
}
interface D extends A,B,C{
default void say(String name){ //若此处没有重写则AB的say方法会冲突
System.out.println("greet"+name);
}
//D隐式继承了C的say方法,不与AB的say冲突
}
2、接口多层继承
//C->B->A
interface A { //第一层A
default void say(int a) {
System.out.println("A");
}
default void run() {
System.out.println("A.run");
}
}
interface B extends A{ //第二层B extends A
default void say(int a) { //override接口A的同签名方法say
System.out.println("B");
}
default void play() { //B新定义的play方法
System.out.println("B.play");
}
//B隐式继承A的run方法
}
interface C extends B{ //第三层C extends B
//say(); B重写后的say()
//play(); B定义的play()
//run(); A定义的run()
}
3、多层多继承(子类只看自己最近的父类、父接口)
//A2->A1
//C->A2,B
//D->A2,A1
interface A1 {
default void say(int a) {
System.out.println("A1");
}
}
interface A2 extends A1 {
//隐式继承A1的say()
}
interface B {
default void say(int a) {
System.out.println("B");
}
}
interface C extends A2,B{
default void say(int a) { //overrideA2与B冲突的say()
B.super.say(a); //通过调用指定接口的默认方法消除二义性
}
}
interface D extends A2,A1{
//D隐式继承A2的方法
}
4、类+接口继承
- 若接口中有与超类同签名的默认方法,则子类优先继承超类的方法,忽略接口的同签名默认方法
- 若接口中的方法与超类签名不同,则由子类实现
//C->B,A
interface A {
default void say() {
System.out.println("A");
}
default void run() {
}
}
static class B {
public void say() {
System.out.println("B");
}
}
static class C extends B implements A{
//interface A的say()被忽略,优先继承超类B的同签名say()
//隐式继承A的不同签名的run()
}
public static void main(String[] args) {
C c = new C();
c.say(); //B
}
标记接口:Cloneable
- 标记接口不包含任何方法,唯一作用就是允许在类型查询中使用instanceof
- Cloneable:A class implements the
Cloneable
interface to indicate to theObject.clone()
method that it is legal for that method to make a field-for-field copy of instances of that class.(类实现Cloneable接口表示此类的对象可用Object类的clone()方法克隆) - clone():Object类的protected方法。子类只能调用protected的clone()方法来克隆它自己的对象,必须重定义clone()方法为public才能允许所有方法克隆对象,即使Object类clone()的默认实现(浅拷贝)能够满足要求,还是需要实现Cloneable接口,将clone()重新定义为public,再调用super.clone(),还可以为新定义的clone()指定返回类型
- 如果类没实现Cloneable接口,但其对象调用clone()方法,则会抛出异常CloneNotSupportedException,所以子类重新定义clone()方法时最好声明此异常
- 所有数组类型都有public的clone()方法,用于建立一个包含原数组所有元素副本的新数组