Java中接口的特性(接口的多实现,默认方法冲突问题,接口的多继承,接口与实现类对象的多态引用)

    科技2022-07-11  91

    一,定义格式

    接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

    引用数据类型:数组,类,接口。

    1、接口的声明格式

    【修饰符】 interface 接口名{ //接口的成员列表: // 静态常量 // 抽象方法 // 默认方法 // 静态方法 // 私有方法 }

    示例代码:

    interface Usb3{ //静态常量 long MAX_SPEED = 500*1024*1024;//500MB/s //抽象方法 void read(); void write(); //默认方法 public default void start(){ System.out.println("开始"); } public default void stop(){ System.out.println("结束"); } //静态方法 public static void show(){ System.out.println("USB 3.0可以同步全速地进行读写操作"); } }

    2、接口的成员说明

    接口定义的是多个类共同的公共行为规范,这些行为规范是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。

    在JDK8之前,接口中只允许出现:

    (1)公共的静态的常量:其中public static final可以省略

    (2)公共的抽象的方法:其中public abstract可以省略

    理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现

    在JDK1.8时,接口中允许声明默认方法和静态方法:

    (3)公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略

    (4)公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略

    在JDK1.9时,接口又增加了:

    (5)私有方法

    除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要初始化。

    3、面试题拷问?

    1、为什么接口中只能声明公共的静态的常量?

    因为接口是标准规范,那么在规范中需要声明一些底线边界值,当实现者在实现这些规范时,不能去随意修改和触碰这些底线,否则就有“危险”。

    例如:USB1.0规范中规定最大传输速率是1.5Mbps,最大输出电流是5V/500mA

    USB3.0规范中规定最大传输速率是5Gbps(500MB/s),最大输出电流是5V/900mA

    例如:尚硅谷学生行为规范中规定学员,早上8:25之前进班,晚上21:30之后离开等等。

    2、为什么JDK1.8之后要允许接口定义静态方法和默认方法呢?因为它违反了接口作为一个抽象标准定义的概念。

    静态方法:因为之前的标准类库设计中,有很多Collection/Colletions或者Path/Paths这样成对的接口和类,后面的类中都是静态方法,而这些静态方法都是为前面的接口服务的,那么这样设计一对API,不如把静态方法直接定义到接口中使用和维护更方便。

    默认方法:(1)我们要在已有的老版接口中提供新方法时,如果添加抽象方法,就会涉及到原来使用这些接口的类就会有问题,那么为了保持与旧版本代码的兼容性,只能允许在接口中定义默认方法实现。比如:Java8中对Collection、List、Comparator等接口提供了丰富的默认方法。(2)当我们接口的某个抽象方法,在很多实现类中的实现代码是一样的,此时将这个抽象方法设计为默认方法更为合适,那么实现类就可以选择重写,也可以选择不重写。

    3、为什么JDK1.9要允许接口定义私有方法呢?因为我们说接口是规范,规范时需要公开让大家遵守的

    私有方法:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。

     

    二,实现接口

    接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。

    类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

    1、实现接口语法格式

    【修饰符】 class 实现类 implements 接口{ // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 // 重写接口中默认方法【可选】 } 【修饰符】 class 实现类 extends 父类 implements 接口{ // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 // 重写接口中默认方法【可选】 }

    注意:

    如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法。

    默认方法可以选择保留,也可以重写。

    重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了

    不能重写静态方法

    示例代码:

    class MobileHDD implements Usb3{ //重写/实现接口的抽象方法,【必选】 public void read() { System.out.println("读数据"); } public void write(){ System.out.println("写数据"); } //重写接口的默认方法,【可选】 //重写默认方法时,default单词去掉 public void end(){ System.out.println("清理硬盘中的隐藏回收站中的东西,再结束"); } }

    2、如何调用对应的方法

    对于接口的静态方法,直接使用“接口名.”进行调用即可

    也只能使用“接口名."进行调用,不能通过实现类的对象进行调用

    对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用

    接口不能直接创建对象,只能创建实现类的对象

    public class TestInteface { public static void main(String[] args) { //创建实现类对象 MobileHDD b = new MobileHDD(); //通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法 b.start(); b.read(); b.stop(); //通过接口名调用接口的静态方法 MobileHDD.show(); } }

     

    三,接口的多实现

    之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

    实现格式:

    【修饰符】 class 实现类 implements 接口1,接口2,接口3。。。{ // 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 // 重写接口中默认方法【可选】 } 【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{ // 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写 // 重写接口中默认方法【可选】 }

    接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。

    定义多个接口:

    interface A { public abstract void showA(); public abstract void show(); } interface B { public abstract void showB(); public abstract void show(); }

    定义实现类:

    public class C implements A,B{ @Override public void showA() { System.out.println("showA"); } @Override public void showB() { System.out.println("showB"); } @Override public void show() { System.out.println("show"); } }

     

    四,默认方法冲突问题

    1、亲爹优先原则

    当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。代码如下:

    定义接口:

    interface A { public default void methodA(){ System.out.println("AAAAAAAAAAAA"); } }

    定义父类:

    class D { public void methodA(){ System.out.println("DDDDDDDDDDDD"); } }

    定义子类:

    class C extends D implements A { // 未重写methodA方法 } class B extends D implements A{ //当然也可以选择重写 public void methodA(){ System.out.println("BBBBBBBBBBBB"); } }

    定义测试类:

    public class Test { public static void main(String[] args) { C c = new C(); c.methodA(); B b = new B(); b.methodA(); } } 输出结果: DDDDDDDDDDDD BBBBBBBBBBBB

    2、必须做出选择

    当一个类同时实现了多个接口,而多个接口中包含方法签名相同的默认方法时,怎么办呢?

    声明接口:

    interface A{ public default void d(){ System.out.println("今晚7点-8点陪我吃饭看电影"); } } interface B{ public default void d(){ System.out.println("今晚7点-8点陪我逛街吃饭"); } }

    选择保留其中一个,通过“接口名.super.方法名"的方法选择保留哪个接口的默认方法。

    class C implements A,B{ @Override public void d() { A.super.d(); } }

    选择自己完全重写:

    class D implements A,B{ @Override public void d() { System.out.println("自己待着"); } }

     

    五,接口的多继承

    一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。

    定义父接口:

    interface A { void a(); public default void methodA(){ System.out.println("AAAAAAAAAAAAAAAAAAA"); } } interface B { void b(); public default void methodB(){ System.out.println("BBBBBBBBBBBBBBBBBBB"); } }

    定义子接口:

    interface C extends A,B{ @Override public default void methodB() { System.out.println("CCCCCCCCCCCCCCCCCCCC"); } }

    小贴士:

    子接口重写默认方法时,default关键字可以保留。

    子类重写默认方法时,default关键字不可以保留。

    class D implements C{ @Override public void a() { System.out.println("xxxxx"); } @Override public void b() { System.out.println("yyyyy"); } } class E implements A,B,C{//效果和上面的D是等价的 @Override public void b() { System.out.println("xxxxx"); } @Override public void a() { System.out.println("yyyyy"); } }

     

    六,接口与实现类对象的多态引用

    实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你new的实现类对象实现的方法体。

    public class TestInterface { public static void main(String[] args) { Flyable b = new Bird(); b.fly(); Flyable k = new Kite(); k.fly(); } } interface Flyable{ //抽象方法 void fly(); } class Bird implements Flyable{ @Override public void fly() { System.out.println("展翅高飞"); } } class Kite implements Flyable{ @Override public void fly() { System.out.println("别拽我,我要飞"); } }

     

    Processed: 0.009, SQL: 8