Java面向对象(高级)-- 接口(interface)

news2024/11/17 15:51:22

文章目录

  • 一、概念
    • (1)引入
    • (2)类比
    • (3)举例
      • 1. 举例1
      • 2. 举例2
      • 3. 举例3
      • 4. 举例4
    • (4) 定义格式及重点举例
      • 1. 接口的声明格式
      • 2. 接口的成员说明
      • 3. 接口内部结构的说明
      • 4. 举例
        • 4.1 举例1--接口
        • 4.2 举例2--类实现接口
        • 4.3 举例3--接口与接口
        • 4.4 举例4--接口与多态性
          • 4.4.1 案例1--虚方法调用
          • 4.4.2 案例2--创建接口实现类的对象
          • 4.4.3 案例3--创建接口实现类的匿名对象
          • 4.4.4 案例4--创建接口匿名实现类的对象
          • 4.4.5 案例5--创建接口匿名实现类的匿名对象
      • 5 . 总结
  • 二、 接口的使用规则
    • (1)类实现接口(implements)
    • (2)接口的多实现(implements)
    • (3)接口的多继承(extends)
    • (4)接口与实现类对象构成多态引用
    • (5)使用接口的静态成员
    • (6)使用接口的非静态方法
  • 三、JDK8与JDK9中接口新特性
    • (1)说明
    • (2)举例
      • 1. 案例1--JDK8 静态方法
      • 2. 案例2--JDK8 默认方法
        • 2.1 默认方法
        • 2.2 接口冲突
        • 2.3 类优先原则
        • 2.4 在实现类中调用接口中被重写的方法
      • 3. 案例3--JDK9 定义私有方法
    • (3)整体代码
  • 四、JDK8中相关冲突问题
    • (1) 默认方法冲突问题
      • 1. 类优先原则
      • 2. 接口冲突(左右为难)
    • (2) 常量冲突问题
  • 五、接口的总结与面试题
    • (1)总结
    • (2)面试题
  • 六、接口与抽象类之间的对比
  • 七、 练习
    • (1)练习1
    • (2)练习2
    • (3)练习3

一、概念

(1)引入

从三条主线的角度来说的话,这儿属于第三条主线,叫做interface关键字的使用。

从另外一个角度来说的话,它是跟类并列的一个结构,这样讲的话,它的地位一下子就提升了。

可以看一下API

比如说就看一下这个long包下的base。这些结构里边,我们再来看一下这个long包:

image.png

可以看到Interface和Class是并列的结构:

image.png

平常新建java文件的时候,也可以看到类与接口:

image.png

那这个接口到底是一个什么样的结构?跟类并列的是吧?前面我们讲的面向对象特征说类里边都能声明什么,其实都是类的,这个体系跟接口没关系

(2)类比

生活中大家每天都在用USB接口,那么USB接口与我们今天要学习的接口有什么相同点呢?

USB,(Universal Serial Bus,通用串行总线)是Intel公司开发的总线架构,使得在计算机上添加串行设备(鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等)非常容易。

其实,不管是电脑上的USB插口,还是其他设备上的USB插口都只是遵循了USB规范的一种具体设备而已。

image.png

只要设备遵循USB规范的,那么就可以与电脑互联,并正常通信。至于这个设备、电脑是哪个厂家制造的,内部是如何实现的,我们都无需关心。

Java的软件系统会有很多模块组成,那么各个模块之间也应该采用这种面向接口低耦合,为系统提供更好的可扩展性和可维护性。


接口用interface这个关键字去修饰,就好比类用class来修饰是一样的。首先,要想理解接口,我们先跟生活中的一个例子做一个类比。

生活当中,我们通常提到这个USB,通常就会说USB接口。其实这块呢,就提到了接口这个词,那么这个USB接口跟我们现在讲的这个接口是不是一个概念呢?其实还真可以理解成就是一个概念。

生活当中,大家每天都在用USB接口,首先看USB,它叫通用串行总线。在电脑上,有一个或者有多个USB的接口,我们就可以跟很多外部设备进行连接了。

连接上以后的话,跟电脑就可以进行数据的传输了,这就是我们所谓的USB接口,你会发现全世界的USB接口的标准都是一样的。
比如说里边有几个金属点啊,这个尺寸是什么样子的,全都是一样的,所以我们都可以去做连接。

那么这其实就涉及到了一个USB的规范问题。具体的,不管是英特尔也好,或者它又交给了哪个组织也好,这个组织的话,就来负责制定USB的这个规范。

这个规范呢,咱们作为普通人,能够看到的就是一个长啊宽啊,或者里边有几个金属点(进行数据传输),这都是面儿上能看到的。
那没有看到,其实里边,就会涉及到很多功能的一些规范。

然后要求所有的外部设备,都应该具备相关的一些功能,比如说一连接上电脑以后能够进行,比如电脑对外开放了USB,连接上它以后,首先有提示“可以进行数据传输”了,包括所有的外部设备,我们最后不用的时候,这块儿有对应的弹出。

传输的时候,都会有相应的一些方法的支持。像这些的话,其实都是一些对应的规范,这个规范,在USB这个规范里面,它就定义好了。要求这些外部设备都得去实现。

其实这里边,提到这个USB接口就是我们现在要讲的这样的一种接口的概念,其实是类似的。
那我们这块儿来看一看JAVA当中这个接口是什么意思,回头让大家比对一下,你就知道,其实确实差不多。

(3)举例

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a关系。

1. 举例1

电脑都预留了可以插入USB设备的USB接口,USB接口具备基本的数据传输的开启功能和关闭功能。你能不能用USB进行连接,或是否具备USB通信功能,就看你能否遵循USB接口规范。
image.png

比如说图中这个电脑,一个相机、打印机,或者这个U盘,现在想跟电脑进行数据库的传输。

那么,你现在是有没有啊?你要是想进行数据传输了,你能不能有这样的一个规范,这样一个标准呢去遵循呢?如果你遵循了这样的标准了,那我就让你跟电脑进行数据传输;你要没有遵循这个标准,我就不让你进行数据的传输。

所以这个标准的话,就体现为JAVA层面的一个接口,就是可以定义一个接口,假设就叫USB。然后要求、几个设备都得遵循这个规范,这个遵循怎么体现呢?

注意不是继承了,不能说这个打印机的父类是一个USB是吧?那这时候就涉及到类和这个USB接口之间的关系,我们称为实现关系一旦实现了这个USB接口,它就具备了这个规范里边定义的一些功能

2. 举例2

再比如说这里,篮球运动员、足球运动员,大学生、小学生。篮球足球他们继承于叫运动员的类,学生这块儿继承学生类,这个是现有的继承关系
image.png

现在有一个叫学英语这样的一个接口,这个接口它封装了一定的功能,或者说这个接口定义了学英语的一些规范。

比如说让足球运动员,大学生,不管是谁,如果你们想学英语,想遵循这样的一个接口的规范,你就实现这个接口就可以了。这是我们用的这个关系叫实现

实现以后,足球运动员和大学生,他们就能够去学英语了。父类该是谁还是谁,不受影响。

3. 举例3

再比如,看下面的图:
image.png

这个飞机也好,子弹也好,风筝也好,热气球也好,这块儿都是不同场景的具体的类。

这呢,我们只考虑实现的关系。

现在呢,有一个接口,它表示的就是可以飞这样的功能,或者叫规范,我们定义到这个接口里了。

然后你发现飞机也好,子弹也好,风筝也好,热气球,它们都可以飞,那我就可以让它们去实现这个接口。或者换句话说,当你实现这个接口以后,体现为就是你们可以飞了

然后这还有一个接口叫攻击性,其实可以看到这里都不是名词了,以前讲父类的话,其实都是一个具体的载体,是一个名词,现在都是一种功能的封装。

这个子弹具备攻击性,那我就让子弹也实现这个接口,实现这个接口以后,它就具备了这样的一个功能。

所以从这个角度来讲呢,实现接口跟上一个例子继承的父类,它确实是不同场景。

4. 举例4

刚才提到了规范的问题,只要你实现了这个规范,你就具备这个规范里边定义的一些相关的功能

那么关于规范这个事儿,再唠叨两句,怎么体现叫规范呢?

刚才我们也说了,USB呢是定义了跟电脑通信的一种规范。谁实现了这个规范,谁就能够跟电脑进行数据传输。
这块呢又举了个例子。

这个例子的话,是在讲完这个java基础以后,大家可以学的一门技术,叫做jdbc,什么叫jdbc呢?它实际上是一波api。说白了就是一些类库,这个类库里边,主要的就是接口。

那这个接口是干什么用的呢?它就是规范了我们用JAVA程序操作不同数据库的一组规范

以前的话,比如没有JDBC,直接用JAVA程序去操作具体的数据库就可以了,但是这样写程序的话,会不具备通用性。像MySQL跟Oracle是不同的数据库,可能这个细节差别很大,你用JAVA程序连的MySQL,再拿这个代码去连Oracle肯定不好使。所以需要重新再去写一套代码去连Oracle,包括这个DB2也同样的道理。

显然就感觉一致性很差,怎么办呢?现在Sun公司他出面定一套标准,这套标准呢,就是有好多的接口,那么这套标准合在一起就叫做JDBC,所以对于JAVA程序员来讲,你不要去面向具体的数据库去编程了,你只面向我这套接口编程就行。需要具备什么样功能,我这都定义好了,你直接去实现我这个接口,实现完以后的话,再去提供下边这些实现类。

因为是面向接口编程的,它封装了不同数据库厂商的细节。

比如现在操作的是Oracle,后期想改成MySQL,其实这块儿呢,非常简单,因为不同的数据库厂商都针对这套接口,就是这个标准进行了相关的一些改造了,都满足这套标准了。然后在连接MySQL的时候,这个代码其实变动很小,直接就可以来操作MySQL。

这就使得代码具备很好的移植性。

这就体现了接口它的作用,它就是一套规范
image.png

Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品能否实现Java设计的JDBC规范。

接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。

比如制定了JDBC这样一套规范,里边主要是接口大,大家都遵循这套接口和规范。那我们再去编程的时候,这个代码就具备一致性、规范性。

USB也同样道理,全世界范围内USB的标准全是一样的,大家都去遵守。你去欧洲买USB的一个设备插到你电脑上照样也能用,因为这个标准都是一样的。

(4) 定义格式及重点举例

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

引用数据类型:数组,类,枚举,接口,注解。

1. 接口的声明格式

[修饰符] interface 接口名{
    //接口的成员列表:
    // 公共的静态常量
    // 公共的抽象方法
    
    // 公共的默认方法(JDK1.8以上)
    // 公共的静态方法(JDK1.8以上)
    // 私有方法(JDK1.9以上)
}

示例代码:

package com.atguigu.interfacetype;

public interface USB3{
    //静态常量
    long MAX_SPEED = 500*1024*1024;//500MB/s

    //抽象方法
    void in();
    void out();

    //默认方法
    default void start(){
        System.out.println("开始");
    }
    default void stop(){
        System.out.println("结束");
    }

    //静态方法
    static void show(){
        System.out.println("USB 3.0可以同步全速地进行读写操作");
    }
}

2. 接口的成员说明

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

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

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

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

在JDK8.0 时,接口中允许声明默认方法静态方法

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

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

在JDK9.0 时,接口又增加了:

(5)私有方法

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

3. 接口内部结构的说明

<1> 接口的理解:接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。

<2> 定义接口的关键字interface

<3> 接口内部结构的说明

  • 可以声明:

    • 属性:必须使用public static final修饰(全局常量–不能改),不能声明变量。

    • 方法

      • jdk8之前:声明抽象方法,修饰为public abstract
      • jdk8:声明静态方法、默认方法(自己用的少,一般源码会用)
      • jdk9:声明私有方法(自己用的少,一般源码会用)
  • 不可以声明:构造器、代码块等(接口跟抽象类一样也不能造对象,接口压根没有构造器)

4. 举例

image.png

4.1 举例1–接口

属性

public class InterfaceTest {
    public static void main(String[] args) {
        System.out.println(Flyable.MIN_SPEED);  //可以直接拿结构调用

        System.out.println(Flyable.MAX_SPEED);  //就算MAX_SPEED前面没有写static,也可以调用

        //Flyable.MAX_SPEED=7000;   //报错,MAX_SPEED是常量
    }
}

interface Flyable{  //接口
    //1.全局常量
    public static final int MIN_SPEED=0;      //能飞行的最低速度

    //public static final可以不写
    int MAX_SPEED=7900;   //能飞行的最高速度

}

final体现也可以证明,比如修改它的值,会报错,如下:

image.png

由于属性一般有 public static final 声明,所以可以省略。

方法

interface Flyable{  //接口

    //2.方法(JDK8之前,只能写抽象方法)
    //可以省略 public abstract 声明
    public abstract void fly();

}

由于抽象方法一般都有 public abstract声明,所以可以省略。

4.2 举例2–类实现接口

再比如“攻击性”。

interface Attackable{   //接口
    public abstract void attack();
}

现在来提供具体接口的实现类

类实现接口之后,接口里面的属性、方法也都拿过来了。

class Plane implements Flyable{

}

目前会报错,如下:

image.png

报错的原因是因为接口的抽象方法,两方面处理,要么写成一个抽象类,要么就将抽象方法实现以下。

比如:

//方法一、抽象类
abstract class Plane implements Flyable{

}

//方法二、重写抽象方法
class Bullet implements Flyable{

    @Override
    public void fly() {

    }
}

若将抽象方法实现了,相对应就可以实例化了

我们也可以实现两个接口,同样会拥有它们所有的抽象方法,都需要重写,比如:

//重写抽象方法
class Bullet implements Flyable,Attackable{

    @Override
    public void fly() {

    }

    @Override
    public void attack() {

    }
}

那么后续操作就和以前一样了,比如可以通过类造对象并调方法。比如:

package yuyi01;

/**
 * ClassName: InterfaceTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/26 0026 9:22
 */
public class InterfaceTest {
    public static void main(String[] args) {
        System.out.println(Flyable.MIN_SPEED);  //可以直接拿结构调用

        System.out.println(Flyable.MAX_SPEED);  //就算MAX_SPEED前面没有写static,也可以调用

        //Flyable.MAX_SPEED=7000;   //报错,MAX_SPEED是常量

        Bullet b1=new Bullet();
        b1.fly();
        b1.attack();
    }
}

interface Flyable{  //接口
    //1.全局常量
    public static final int MIN_SPEED=0;      //能飞行的最低速度

    //public static final可以不写
    int MAX_SPEED=7900;   //能飞行的最高速度


    //2.方法(JDK8之前,只能写抽象方法)
    //可以省略 public abstract 声明
    public abstract void fly();

}

interface Attackable{   //接口
    public abstract void attack();
}

//抽象类
abstract class Plane implements Flyable{

}

//重写抽象方法
class Bullet implements Flyable,Attackable{

    @Override
    public void fly() {
        System.out.println("让子弹飞一下");
    }

    @Override
    public void attack() {
        System.out.println("子弹可以击穿石头");
    }
}

输出结果:

image.png


🗳️总结

<1> 接口与类的关系 :实现关系

<2> 格式class A extends SuperA implements B,C{}

A相较于SuperA来讲,叫做子类

A相较于B,C来讲,叫做实现类

<3> 满足此关系之后的说明

  • 类可以实现多个接口。(实现功能的扩充–多实现)

  • 类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性。

  • 必须将实现的接口中的所有的抽象方法都重写(或实现),方可实例化。否则,此实现类必须声明为抽象类。

4.3 举例3–接口与接口

将D实现CC,那么类 D里面就相当于有两个方法,要想实例化,就需要都重写一下。

//测试接口的继承关系
interface AA{
    void method1();
}

interface BB{
    void method2();
}

interface CC extends AA,BB{    //接口多继承  相较于AA,BB来说,CC是子接口

}
class D implements CC{
    @Override
    public void method1() {
        
    }

    @Override
    public void method2() {

    }  //将D实现CC,那么类 D里面就相当于有两个方法,要想实例化,就需要都重写一下

}

接口与接口的关系:继承关系,且可以多继承。

4.4 举例4–接口与多态性

接口的多态性: 接口名 变量名 = new 实现类对象; (new的不是什么子接口,接口本身没有构造器,不能造对象,这里要造对象,所以肯定是“实现类对象”)

4.4.1 案例1–虚方法调用

image.png

其实就是虚方法调用,跟讲类的多态性时候一样。

编译时定位到接口Flyable里面,但是运行的时候是实现类Bullet()里面的。

//接口的多态性
Flyable f1=new Bullet();
f1.fly();   //编译时定位到接口Flyable里面,但是运行的时候是实现类Bullet()里面的

调式:
问题5.gif

运行结果:

image.png


4.4.2 案例2–创建接口实现类的对象

用接口体现的是“规范”,实际new的时候都是“实现类的对象”。

比如:
image.png

🌱代码(场景一)

package yuyi01;

/**
 * ClassName: USBTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/26 0026 15:59
 */
public class USBTest {
    public static void main(String[] args) {
    	//场景一: 创建接口实现类(Printer)的对象(printer)
        
        //造一个电脑
        Computer computer=new Computer();   //computer类调用抽象接口的抽象方法,具体类实现抽象接口(重写方法)

        //造一个打印机
        Printer printer=new Printer();

        //在电脑上连打印机
        computer.transferData(printer); //transferData()方法里面声明的是USB类型,实际赋值的是Printer类型。即:USB usb=new Printer();

    }
}

//电脑
class Computer{
    public void transferData(USB usb){
        System.out.println("设备连接成功...");
        usb.start();

        System.out.println("数据传输细节操作...");

        usb.stop();
    }
}

//打印机
class Printer implements USB{   //打印机实现USB接口

    @Override
    public void start() {
        System.out.println("打印机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

//USB接口
interface USB{
    //声明常量,比如USB的长、宽、高...

    //方法
    public abstract void start();

    void stop();
}

🍺输出结果:

image.png

编译的时候看似调用的是USB的方法,实际执行的时候是具体打印机里面的方法。

多态性的体现:

//在电脑上连打印机
computer.transferData(printer); //transferData()方法里面声明的是USB类型,实际赋值的是Printer类型。即:USB usb=new Printer();

接口要是没有多态性就废了,接口本身也没有构造器,无法造对象,也不让实现类的对象直接赋,那么这块就没法赋值了。

class Computer{
    public void transferData(USB usb){
        //...
    }
}

🚗题外话

现在都是win 10 、win 11了,以前用这个XP啊,或者包括WIN 7的时候,你会发现经常一连外部设备,

它这块儿会联网操作,然后会提示一个说安装相关的驱动,这个驱动是什么?

驱动其实就是这个USB接口,它这一套标准已经定义好了,但是你连接这个外部设备,不知道你是什么,你要是连接我这个电脑,想进行数据传输的话,你得提供这一套实现类。我这只有一个接口,实际上的话它有好多接口,你得提供这样的一套实现类,那么这一套实现类的集合就是驱动

比如说扫描仪想在电脑上去传输,扫描仪连上电脑之后,到底怎么传呀?这个USB是有一套标准,但是这都是抽象方法,这块儿你得把你的这套驱动先加载进来,或者要是联网的话,去下载一个也行,那就相当于提供了一套,就是实现了接口的这样一套实现类。然后电脑跟你去传的时候,就按照这套实现类里边实现的这种方法去做了。


4.4.3 案例3–创建接口实现类的匿名对象

再写一个照相机:

//照相机
class Camera implements USB{

    @Override
    public void start() {
        System.out.println("照相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("照相机结束工作");
    }
}

🌱整体代码(场景二)

package yuyi01;

/**
 * ClassName: USBTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/26 0026 15:59
 */
public class USBTest {
    public static void main(String[] args) {
        //造一个电脑
        Computer computer=new Computer();   //computer类调用抽象接口的抽象方法,具体类实现抽象接口(重写方法)

        //场景一:创建接口实现类(Printer)的对象(printer)
        //造一个打印机
        Printer printer=new Printer();

        //在电脑上连打印机
        computer.transferData(printer); //transferData()方法里面声明的是USB类型,实际赋值的是Printer类型。即:USB usb=new Printer();

        System.out.println();

        //场景二:创建接口实现类的匿名对象
        computer.transferData(new Camera());

    }
}

//电脑
class Computer{
    public void transferData(USB usb){
        System.out.println("设备连接成功...");
        usb.start();

        System.out.println("数据传输细节操作...");

        usb.stop();
    }
}

//打印机
class Printer implements USB{   //打印机实现USB接口

    @Override
    public void start() {
        System.out.println("打印机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

//照相机
class Camera implements USB{

    @Override
    public void start() {
        System.out.println("照相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("照相机结束工作");
    }
}


//USB接口
interface USB{
    //声明常量,比如USB的长、宽、高...

    //方法
    public abstract void start();

    void stop();
}

🍺输出结果

image.png


4.4.4 案例4–创建接口匿名实现类的对象

之前写了两个场景了,现在再来看一个。

public class USBTest {
    public static void main(String[] args) {
        //场景一: 创建接口实现类(Printer)的对象(printer)
        Computer computer=new Computer(); //造一个电脑
        Printer printer=new Printer();	//造一个打印机
        computer.transferData(printer); //在电脑上连打印机   USB usb=new Printer();


        //场景二: 创建接口实现类的匿名对象
        computer.transferData(new Camera());


        //场景三: 创建接口匿名实现类的对象
        computer.transferData();
    }
}

computer.transferData();

里面要传一个USB的实现类的对象,场景一是Printer,场景二是Camera,实现类都有名

现在若实现类没有名了,要想声明一个变量怎么办呢?只能先拿USB顶替一下,对象有名,比如叫usb1:USB usb1=

等号右边咋写?new一个啥呢?new一个构造器?构造器要与类同名,比如场景二是new Camera(),那么类就是Camera()

此时对象有个名叫usb1,但是类没有。

那我们就new一个USB(),然后将usb1当作实参放入computer.transferData();中。如下:

USB usb1=new USB();
computer.transferData(usb1);

此时是报错状态,如下:

image.png

此时报错很正常,因为USB本身没有构造器。

现在这个实现类没有名,但是形式上又得这样写,此时拿USB充当了一下。但是实现类又要保证不是抽象类,这样才能够造对象,USB是充当了,但是它里面有抽象方法啊(抽象方法不能够造对象了)。如下:

//USB接口
interface USB{
    //声明常量,比如USB的长、宽、高...

    //方法
    public abstract void start();

    void stop();
}

🍸那咋办呢?

我们可以临时将USB里面的抽象方法给重写一下。

直接在后面整一对大括号,如下:

USB usb1=new USB(){

};

然后在里面重写抽象方法即可,如下:

//场景三: 创建接口匿名实现类的对象
USB usb1=new USB(){
    public void start(){
        System.out.println("U盘开始工作");
    }

    public void stop(){
        System.out.println("U盘结束工作");
    }
};
computer.transferData(usb1);

🌱整体代码(场景三)

package yuyi01;

/**
 * ClassName: USBTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/26 0026 15:59
 */
public class USBTest {
    public static void main(String[] args) {
        //造一个电脑
        Computer computer=new Computer();   //computer类调用抽象接口的抽象方法,具体类实现抽象接口(重写方法)

        //场景一:创建接口实现类(Printer)的对象(printer)
        //造一个打印机
        Printer printer=new Printer();
        //在电脑上连打印机
        computer.transferData(printer); //transferData()方法里面声明的是USB类型,实际赋值的是Printer类型。即:USB usb=new Printer();

        System.out.println();   //换行

        //场景二:创建接口实现类的匿名对象
        computer.transferData(new Camera());

        System.out.println();   //换行

        //场景三: 创建接口匿名实现类的对象
        USB usb1=new USB(){
            public void start(){
                System.out.println("U盘开始工作");
            }

            public void stop(){
                System.out.println("U盘结束工作");
            }
        };
        computer.transferData(usb1);
    }
}

//电脑
class Computer{
    public void transferData(USB usb){
        System.out.println("设备连接成功...");
        usb.start();

        System.out.println("数据传输细节操作...");

        usb.stop();
    }
}

//打印机
class Printer implements USB{   //打印机实现USB接口

    @Override
    public void start() {
        System.out.println("打印机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

//照相机
class Camera implements USB{

    @Override
    public void start() {
        System.out.println("照相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("照相机结束工作");
    }
}


//USB接口
interface USB{
    //声明常量,比如USB的长、宽、高...

    //方法
    public abstract void start();

    void stop();
}

🍺输出结果

image.png

🗃️絮叨

USB usb1=new USB(){
    //...
};

接口的匿名实现类,new后面要放实现类的类名,但现在实现类匿名,所以只能暂时用USB充当一下。

而类要是想造对象,必须要把抽象方法给实现一下,那么就在后面大括号临时实现一下即可。

现在对象是有名的。


4.4.5 案例5–创建接口匿名实现类的匿名对象

再来看一个场景。

此时类匿名,对象也匿名,很显然,在上一个场景的基础上,直接将new的部分当参数即可。

如下:

//场景四: 创建接口匿名实现类的匿名对象
computer.transferData(new USB(){//...});

写一下:

//场景四: 创建接口匿名实现类的匿名对象
computer.transferData(new USB(){
    public void start(){
        System.out.println("扫描仪开始工作");
    }

    public void stop(){
        System.out.println("扫描仪结束工作");
    }
});

这个也是多态性,可千万别说是new了一个接口USB的对象,是new了一个接口实现类的对象(只不过这个实现类没有名)。

🌱整体代码(场景四)

package yuyi01;

/**
 * ClassName: USBTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/26 0026 15:59
 */
public class USBTest {
    public static void main(String[] args) {
        //造一个电脑
        Computer computer=new Computer();   //computer类调用抽象接口的抽象方法,具体类实现抽象接口(重写方法)

        //场景一:创建接口实现类(Printer)的对象(printer)
        //造一个打印机
        Printer printer=new Printer();
        //在电脑上连打印机
        computer.transferData(printer); //transferData()方法里面声明的是USB类型,实际赋值的是Printer类型。即:USB usb=new Printer();

        System.out.println();   //换行

        //场景二:创建接口实现类的匿名对象
        computer.transferData(new Camera());

        System.out.println();   //换行

        //场景三: 创建接口匿名实现类的对象
        USB usb1=new USB(){
            public void start(){
                System.out.println("U盘开始工作");
            }

            public void stop(){
                System.out.println("U盘结束工作");
            }
        };
        computer.transferData(usb1);

        System.out.println();   //换行

        //场景四: 创建接口匿名实现类的匿名对象
        computer.transferData(new USB(){
            public void start(){
                System.out.println("扫描仪开始工作");
            }

            public void stop(){
                System.out.println("扫描仪结束工作");
            }
        });


    }
}

//电脑
class Computer{
    public void transferData(USB usb){
        System.out.println("设备连接成功...");
        usb.start();

        System.out.println("数据传输细节操作...");

        usb.stop();
    }
}

//打印机
class Printer implements USB{   //打印机实现USB接口

    @Override
    public void start() {
        System.out.println("打印机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

//照相机
class Camera implements USB{

    @Override
    public void start() {
        System.out.println("照相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("照相机结束工作");
    }
}


//USB接口
interface USB{
    //声明常量,比如USB的长、宽、高...

    //方法
    public abstract void start();

    void stop();
}

🍺输出结果

image.png

5 . 总结

🗳️总结

<1> 接口与类的关系 :实现关系

<2> 格式class A extends SuperA implements B,C{}

A相较于SuperA来讲,叫做子类

A相较于B,C来讲,叫做实现类

<3> 满足此关系之后的说明

  • 类可以实现多个接口。(实现功能的扩充–多实现)
  • 类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性。
  • 必须将实现的接口中的所有的抽象方法都重写(或实现),方可实例化。否则,此实现类必须声明为抽象类。

<4> 接口与接口的关系:继承关系,且可以多继承

<5> 多态性

  • 接口的多态性: 接口名 变量名 = new 实现类对象; (new的不是什么子接口,接口本身没有构造器,不能造对象,这里要造对象,所以肯定是“实现类对象”)
  • 的多态性:父类 变量名 = new 子类对象 ;

<6> 面试题:区分抽象类和接口

  • 共性

    • 都可以声明抽象方法。(抽象类可能没有抽象方法)
    • 都不能实例化。(都不能创建对象)
  • 不同

    • 抽象类一定有构造器。接口没有构造器。
    • 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系。
    • 抽象类中的属性可以随意定义,但是接口中只能是常量。

<7> 重点掌握四种场景

public class USBTest {
    public static void main(String[] args) {
        //场景一: 创建接口实现类(Printer)的对象(printer)
        Computer computer=new Computer(); //造一个电脑
        Printer printer=new Printer();	//造一个打印机
        computer.transferData(printer); //在电脑上连打印机   USB usb=new Printer();


        //场景二: 创建接口实现类的匿名对象
        computer.transferData(new Camera());


        //场景三: 创建接口匿名实现类的对象
        USB usb1=new USB(){
            public void start(){
                System.out.println("U盘开始工作");
            }

            public void stop(){
                System.out.println("U盘结束工作");
            }
        };
        computer.transferData(usb1);


        //场景四: 创建接口匿名实现类的匿名对象
        computer.transferData(new USB(){
            public void start(){
                System.out.println("扫描仪开始工作");
            }

            public void stop(){
                System.out.println("扫描仪结束工作");
            }
        });
    }
}

第一种最规范,但通常开发中会使用第三种和第四种,所以需要掌握。

<8> 接口存在多态性

以后看到一个方法的形参是一个接口的时候,必然要使用多态。因为接口本身没有构造器,不能造对象,一定要放实现类的对象,即多态性。

//电脑
class Computer{
    public void transferData(USB usb){  //多态:USB usb=new Printer();
        System.out.println("设备连接成功...");
        usb.start();

        System.out.println("数据传输细节操作...");

        usb.stop();
    }
}

二、 接口的使用规则

(1)类实现接口(implements)

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

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

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

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

image.png

注意:

  1. 如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法
  2. 默认方法可以选择保留,也可以重写。

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

  1. 接口中的静态方法不能被继承也不能被重写

举例:

interface USB{		// 
	public void start() ;
	public void stop() ;	
}
class Computer{
	public static void show(USB usb){	
		usb.start() ;
		System.out.println("=========== USB 设备工作 ========") ;
		usb.stop() ;
	}
};
class Flash implements USB{
	public void start(){	// 重写方法
		System.out.println("U盘开始工作。") ;
	}
	public void stop(){		// 重写方法
		System.out.println("U盘停止工作。") ;
	}
};
class Print implements USB{
	public void start(){	// 重写方法
		System.out.println("打印机开始工作。") ;
	}
	public void stop(){		// 重写方法
		System.out.println("打印机停止工作。") ;
	}
};
public class InterfaceDemo{
	public static void main(String args[]){
		Computer.show(new Flash()) ;
		Computer.show(new Print()) ;

		c.show(new USB(){
			public void start(){
				System.out.println("移动硬盘开始运行");
			}
			public void stop(){
				System.out.println("移动硬盘停止运行");
			}
		});
	}
};

(2)接口的多实现(implements)

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

实现格式:

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

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

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

举例:
image.png

image.png

image.png

定义多个接口:

package com.atguigu.interfacetype;

public interface A {
    void showA();
}
package com.atguigu.interfacetype;

public interface B {
    void showB();
}

定义实现类:

package com.atguigu.interfacetype;

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

    @Override
    public void showB() {
        System.out.println("showB");
    }
}

测试类

package com.atguigu.interfacetype;

public class TestC {
    public static void main(String[] args) {
        C c = new C();
        c.showA();
        c.showB();
    }
}

(3)接口的多继承(extends)

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

定义父接口:

package com.atguigu.interfacetype;

public interface Chargeable {
    void charge();
    void in();
    void out();
}

定义子接口:

package com.atguigu.interfacetype;

public interface UsbC extends Chargeable,USB3 {
    void reverse();
}

定义子接口的实现类:

package com.atguigu.interfacetype;

public class TypeCConverter implements UsbC {
    @Override
    public void reverse() {
        System.out.println("正反面都支持");
    }

    @Override
    public void charge() {
        System.out.println("可充电");
    }

    @Override
    public void in() {
        System.out.println("接收数据");
    }

    @Override
    public void out() {
        System.out.println("输出数据");
    }
}

所有父接口的抽象方法都有重写。

方法签名相同的抽象方法只需要实现一次。

(4)接口与实现类对象构成多态引用

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

接口的不同实现类:

package com.atguigu.interfacetype;

public class Mouse implements USB3 {
    @Override
    public void out() {
        System.out.println("发送脉冲信号");
    }

    @Override
    public void in() {
        System.out.println("不接收信号");
    }
}
package com.atguigu.interfacetype;

public class KeyBoard implements USB3{
    @Override
    public void in() {
        System.out.println("不接收信号");
    }

    @Override
    public void out() {
        System.out.println("发送按键信号");
    }
}

测试类

package com.atguigu.interfacetype;

public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        USB3 usb = new Mouse();
        computer.setUsb(usb);
        usb.start();
        usb.out();
        usb.in();
        usb.stop();
        System.out.println("--------------------------");

        usb = new KeyBoard();
        computer.setUsb(usb);
        usb.start();
        usb.out();
        usb.in();
        usb.stop();
        System.out.println("--------------------------");

        usb = new MobileHDD();
        computer.setUsb(usb);
        usb.start();
        usb.out();
        usb.in();
        usb.stop();
    }
}

(5)使用接口的静态成员

接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态常量。

package com.atguigu.interfacetype;

public class TestUSB3 {
    public static void main(String[] args) {
        //通过“接口名.”调用接口的静态方法 (JDK8.0才能开始使用)
        USB3.show();
        //通过“接口名.”直接使用接口的静态常量
        System.out.println(USB3.MAX_SPEED);
    }
}

(6)使用接口的非静态方法

  • 对于接口的静态方法,直接使用“接口名.”进行调用即可
    • 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
  • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
    • 接口不能直接创建对象,只能创建实现类的对象
package com.atguigu.interfacetype;

public class TestMobileHDD {
    public static void main(String[] args) {
        //创建实现类对象
        MobileHDD b = new MobileHDD();

        //通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法
        b.start();
        b.in();
        b.stop();

        //通过接口名调用接口的静态方法
//        MobileHDD.show();
//        b.show();
        Usb3.show();
    }
}

三、JDK8与JDK9中接口新特性

(1)说明

  1. 接口内部结构的说明:
  • 可以声明:

  • 属性:必须使用public static final修饰

  • 方法:jdk8之前:声明抽象方法,修饰为public abstract
    jdk8:声明静态方法、默认方法
    jdk9:声明私有方法

  • 不可以声明:构造器、代码块等

  1. 传统中,就按照这样来写:

image.png
但是从JDK8开始,它可以有方法体的方法了。其实自己写的比较少,更多的是源码层面,它会扩充一些有方法体的方法。可以直接使用。

  1. 接口与类:class A extends SuperA implements B,C{}

继承是单继承,现在可以实现多个接口。JDK8中,接口里面可以有方法体的方法了,可以直接拿给A用,一定程度上缓解了单继承的局限性

(2)举例

1. 案例1–JDK8 静态方法

先来看JDK8中静态方法

【CompareA.java】

package yuyi05;

/**
 * ClassName: CompareA
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 9:59
 */
public interface CompareA {
    //属性:声明为 public static final
    //方法:JDK8之前,只能声明抽象方法 public abstract

    //方法:JDK8--静态方法
    public static void method1(){
        System.out.println("CompareA:北京");
    }
}

可以看到,method1()就是静态方法,有方法体。和平时在类里面写静态方法一致,没什么区别。

那怎么用呢?

先写一个SubClass类,实现CompareA:

package yuyi05;

/**
 * ClassName: SubClass
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 10:03
 */
public class SubClass implements CompareA{

}

由于接口里面没有抽象方法,所以此时也没有报错,如下:

image.png

再写一个测试类SubClassTest

public class SubClassTest {
    public static void main(String[] args) {

    }
}

现在接口CompareA里面有一个静态方法method1(),静态方法拿声明的主体去调应该就可以,比如:CompareA.method1();

运行发现可以:

image.png

如果用接口的实现类SubClass来调用这个方法,可以吗?

其实不可以,如下:

image.png

所以,实现类不可以调用!

知识点1:接口中声明的静态方法只能被接口调用,不能使用其实现类进行调用。

2. 案例2–JDK8 默认方法

2.1 默认方法

接下来看JDK8中默认方法

这里要使用一个关键字default

【CompareA.java】

public interface CompareA {
	//...
    //方法:JDK8--默认方法
    public default void method2(){
        System.out.println("CompareA:上海");
    }

}

就是在普通方法里面加了一个default。若是不加default,它就默认你省略了abstract,会认为此方法是一个抽象方法。如下:

image.png

既然SubClass实现了接口CompareA,那么这个默认方法method2()应该也可以拿到。

下面来做测试。

public class SubClassTest {
    public static void main(String[] args) {
        //...
        SubClass s1=new SubClass(); //因为是默认方法,不是静态,所以要造对象
        s1.method2();
    }
}

运行结果:

image.png

在SubClass类里面,也可以对接口CompareA里的方法做重写。比如:

public class SubClass implements CompareA{
    @Override
    public void method2() {
        System.out.println("SubClass:上海");
    }
}

再次测试,就会调用重写之后的方法了(与类似),如下:

image.png

知识点2:接口中声明的默认方法可以被实现类继承,实现类在没有重写此方法的情况下,默认调用接口中声明的默认方法;如果实现类重写了此方法,则调用的是自己重写的方法。

2.2 接口冲突

在接口CompareA中再写一个默认方法method3(),如下:

public interface CompareA {
    //属性:声明为 public static final
    //方法:JDK8之前,只能声明抽象方法 public abstract

    //方法:JDK8--静态方法
    public static void method1(){
        System.out.println("CompareA:北京");
    }

    //方法:JDK8--默认方法
    public default void method2(){
        System.out.println("CompareA:上海");
    }

    public default void method3(){
        System.out.println("CompareA:广州");
    }

}

然后再写一个接口CompareB,方法method3()的声明与接口CompareA中一致,如下:

public interface CompareB {
    //默认方法
    public default void method3(){
        System.out.println("CompareB:广州");
    }
}

现在SubClass实现了接口CompareA,那么SubClass也拿到了CompareA中的方法method3()。

此时让SubClass也实现接口CompareB,可以发现出错了:

image.png

前面说过“抽象方法”,若接口CompareA里面有一个抽象方法,接口CompareB里面也有一个抽象方法,CompareA和CompareB里面的抽象方法声明一致,那么此时SubClass会有上面的问题吗?

其实不会

因为SubClass类要想造对象的话,需要把这个抽象方法重写一下,这个重写既可以看作是对CompareA里面抽象方法的重写,也可以看作是对CompareB里面抽象方法的重写。

若SubClass类没有重写抽象方法,而是一个抽象类,那也没有问题,因为它们的方法长得一样,没有方法体。


但是现在的情况是,接口CompareA和CompareB它们的默认方法有方法体,而且声明一样,那么在实现类SubClass没有重写的情况下就会冲突

此时若在测试类里面调用method3(),编译器就不知道调用谁,因为接口的地位对于类来说完全一致,s1不知道该调用谁,就会报错。

image.png

我们将这种问题叫做“接口冲突”。

此时实现类必须要重写接口中定义的同名同参数的方法

public class SubClass implements CompareA,CompareB{
    //...
    public  void method3(){ //类没有default之说,不用写它
        System.out.println("SubClass:广州");
    }
}

这个重写既可以看作是对CompareA里面method3()方法的重写,也可以看作是对CompareB里面method3()方法的重写。

然后在测试类里面调用method3(),就会是自己的方法了,如下:

image.png

知识点3:类实现了两个接口,而两个接口中定义了同名同参数的默认方法。则实现类在没有重写此两个接口默认方法的情况下会报错–>接口冲突

要求:此时实现类必须要重写接口中定义的同名同参数的方法。

2.3 类优先原则

现在在接口CompareA里面再加一个方法method4()

public interface CompareA {
    //...
    public default void method4(){
        System.out.println("CompareA:深圳");
    }

}

然后再写一个类SuperClass,并将CompareA中的method4()粘过来(名字、参数都一样),如下:

public class SuperClass {
    public  void method4(){
        System.out.println("SuperClass:深圳");
    }
}

然后让SubClass继承于SuperClass:

image.png

可以发现,此时没有报错

SuperClass里面有method4(),CompareA里面也有method4(),它们名字一样、参数一样,此时为啥不会报错呢?

这是因为类与接口的低位不平等

若此时拿s1去调用method4(),结果如下:

image.png

可以发现,调用的是SuperClass类中的method4()方法。

将这种情况叫做“类优先原则”。

知识点4:子类(实现类)继承了父类并实现了接口,父类和接口中声明了同名同参数的方法。(对于接口来说,这是默认方法)

默认情况下,子类(实现类)在没有重写此方法的情况下,调用的是父类中的方法。—> 类优先原则

若子类重写了,那么调用的时候肯定就是重写之后的方法了,

2.4 在实现类中调用接口中被重写的方法

在SubClass类里面重写一下method4()方法:

public class SubClass extends SuperClass implements CompareA,CompareB{
    //...  
    public void method4(){
        System.out.println("SubClass:深圳");
    }
}

在测试类里面调用method4()就是调用自己重写的了:

image.png

此时在SubClass中,再写一个普通的方法,想调用自己方法中的method4(),直接method4()即可。

如果想调用父类中的method4(),加一个super.即可:super.method4()

public class SubClass extends SuperClass implements CompareA,CompareB{
  	//...
    public void method(){
        method4();  //调用自己类中的方法
        
        super.method4();    //调用父类中的方法
        
    }
}

现在我想调用CompareA中的method3(),接口CompareA和接口CompareB中都有method3(),那怎么调用呢?

image.png

用CompareA调用吗?这样:CompareA..method3()。此时method3()不是静态的啊,所以不能这样调用。

那加一个super:CompareA.super.method3();可以啦。

同理。调用CompareB里面的method3():CompareB.super.method3();

这里的super也不是体现“父类的”了,就是继承了父类或者实现了接口,都算是super的场景(基于实例来说的)。

public class SubClass extends SuperClass implements CompareA,CompareB{
    //...
    public void method(){
        method4();  //调用自己类中的方法

        super.method4();    //调用父类中的方法

        CompareA.super.method3();   //调用接口CompareA中的默认方法
        CompareB.super.method3();   //调用接口CompareB中的默认方法
    }
}

知识点5:如何在子类(或实现类)中调用父类或接口中被重写的方法

比如,调用CompareB里面的method3():CompareB.super.method3();

3. 案例3–JDK9 定义私有方法

“文件”–>“项目结构”:

image.png

这个地方用的17,如果是8就不行哦:

image.png

这种情况下就可以演示JDK9新特性了。比如:

public interface CompareA {
    //...
    //方法:JDK9新特性--定义私有方法
    private void method5(){
        System.out.println("我是接口CompareA中定义的私有方法");
    }

}

私有方法实现类也不能继承,这个方法干啥用呢?就是自己用

刚才写的默认方法都有方法体,而且method5()也不是静态方法,不是给静态方法用的,就是给默认方法用的啦。

若定义了好多默认方法,彼此之间有一些共同的代码,可以将这些共同代码专门抽取出来,也不对外暴露了,就让它私有化。
就是这样使用。

(3)整体代码

这里将刚才举例的所有代码展现出来,供大家学习使用。

🌱代码

【CompareA.java】

package yuyi05;

/**
 * ClassName: CompareA
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 9:59
 */
public interface CompareA {
    //属性:声明为 public static final
    //方法:JDK8之前,只能声明抽象方法 public abstract

    //方法:JDK8--静态方法
    public static void method1(){
        System.out.println("CompareA:北京");
    }

    //方法:JDK8--默认方法
    public default void method2(){
        System.out.println("CompareA:上海");
    }

    public default void method3(){
        System.out.println("CompareA:广州");
    }

    public default void method4(){
        System.out.println("CompareA:深圳");
    }

    //方法:JDK9新特性--定义私有方法
    private void method5(){
        System.out.println("我是接口CompareA中定义的私有方法");
    }

}

【CompareB.java】

package yuyi05;

/**
 * ClassName: CompareB
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 10:35
 */
public interface CompareB {
    //默认方法
    public default void method3(){
        System.out.println("CompareB:广州");
    }
}

【SubClass.java】

package yuyi05;

/**
 * ClassName: SubClass
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 10:03
 */
public class SubClass extends SuperClass implements CompareA,CompareB{
    @Override
    public void method2() {
        System.out.println("SubClass:上海");
    }

    public  void method3(){ //类没有default之说,不用写它
        System.out.println("SubClass:广州");
    }

    public void method4(){
        System.out.println("SubClass:深圳");
    }

    public void method(){
        method4();  //调用自己类中的方法

        super.method4();    //调用父类中的方法

        CompareA.super.method3();   //调用接口CompareA中的默认方法
        CompareB.super.method3();   //调用接口CompareB中的默认方法
    }
}

【SuperClass.java】

package yuyi05;

/**
 * ClassName: SuperClass
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 11:01
 */
public class SuperClass {
    public  void method4(){
        System.out.println("SuperClass:深圳");
    }
}

【SubClassTest.java】

package yuyi05;

/**
 * ClassName: SubClassTest
 * Package: yuyi05
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/29 0029 10:05
 */
public class SubClassTest {
    public static void main(String[] args) {
        //知识点1:接口中声明的静态方法只能被接口调用,不能使用其实现类进行调用。
        CompareA.method1();
        //SubClass.method1();

        //知识点2:接口中声明的默认方法可以被实现类继承,实现类在没有重写此方法的情况下,默认调用接口中声明的默认方法;
        // 如果实现类重写了此方法,则调用的是自己重写的方法。
        SubClass s1=new SubClass(); //因为是默认方法,不是静态,所以要造对象
        s1.method2();

        //知识点3:类实现了两个接口,而两个接口中定义了同名同参数的默认方法。则实现类在没有重写此两个接口默认方法的情况下会报错-->接口冲突
        //要求:此时实现类必须要重写接口中定义的同名同参数的方法。
        s1.method3();

        //知识点4:子类(实现类)继承了父类并实现了接口,父类和接口中声明了同名同参数的方法。(对于接口来说,这是默认方法)
        //默认情况下,子类(实现类)在没有重写此方法的情况下,调用的是父类中的方法。--->类优先原则
        s1.method4();

        s1.method();
    }
}

🍺输出结果

image.png

四、JDK8中相关冲突问题

(1) 默认方法冲突问题

1. 类优先原则

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

定义接口:

package com.atguigu.interfacetype;

public interface Friend {
    default void date(){//约会
        System.out.println("吃喝玩乐");
    }
}

定义父类:

package com.atguigu.interfacetype;

public class Father {
    public void date(){//约会
        System.out.println("爸爸约吃饭");
    }
}

定义子类:

package com.atguigu.interfacetype;

public class Son extends Father implements Friend {
    @Override
    public void date() {
        //(1)不重写默认保留父类的
        //(2)调用父类被重写的
//        super.date();
        //(3)保留父接口的
//        Friend.super.date();
        //(4)完全重写
        System.out.println("跟康师傅学Java");
    }
}

定义测试类:

package com.atguigu.interfacetype;

public class TestSon {
    public static void main(String[] args) {
        Son s = new Son();
        s.date();
    }
}

2. 接口冲突(左右为难)

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

image.png

无论你多难抉择,最终都是要做出选择的。

声明接口:

package com.atguigu.interfacetype;

public interface BoyFriend {
    default void date(){//约会
        System.out.println("神秘约会");
    }
}

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

package com.atguigu.interfacetype;

public class Girl implements Friend,BoyFriend{

    @Override
    public void date() {
        //(1)保留其中一个父接口的
//        Friend.super.date();
//        BoyFriend.super.date();
        //(2)完全重写
        System.out.println("跟康师傅学Java");
    }

}

测试类

package com.atguigu.interfacetype;

public class TestGirl {
    public static void main(String[] args) {
        Girl girl = new Girl();
        girl.date();
    }
}
  • 当一个子接口同时继承了多个接口,而多个父接口中包含方法签名相同的默认方法时,怎么办呢?

另一个父接口:

package com.atguigu.interfacetype;

public interface USB2 {
    //静态常量
    long MAX_SPEED = 60*1024*1024;//60MB/s

    //抽象方法
    void in();
    void out();

    //默认方法
    public default void start(){
        System.out.println("开始");
    }
    public default void stop(){
        System.out.println("结束");
    }

    //静态方法
    public static void show(){
        System.out.println("USB 2.0可以高速地进行读写操作");
    }
}

子接口:

package com.atguigu.interfacetype;

public interface USB extends USB2,USB3 {
    @Override
    default void start() {
        System.out.println("Usb.start");
    }

    @Override
    default void stop() {
        System.out.println("Usb.stop");
    }
}

小贴士:

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

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

(2) 常量冲突问题

  • 当子类继承父类又实现父接口,而父类中存在与父接口常量同名的成员变量,并且该成员变量名在子类中仍然可见。
  • 当子类同时实现多个接口,而多个接口存在相同同名常量。

此时在子类中想要引用父类或父接口的同名的常量或成员变量时,就会有冲突问题。

父类和父接口:

package com.atguigu.interfacetype;

public class SuperClass {
    int x = 1;
}
package com.atguigu.interfacetype;

public interface SuperInterface {
    int x = 2;
    int y = 2;
}
package com.atguigu.interfacetype;

public interface MotherInterface {
    int x = 3;
}

子类:

package com.atguigu.interfacetype;

public class SubClass extends SuperClass implements SuperInterface,MotherInterface {
    public void method(){
//        System.out.println("x = " + x);//模糊不清
        System.out.println("super.x = " + super.x);
        System.out.println("SuperInterface.x = " + SuperInterface.x);
        System.out.println("MotherInterface.x = " + MotherInterface.x);
        System.out.println("y = " + y);//没有重名问题,可以直接访问
    }
}

五、接口的总结与面试题

(1)总结

  • 接口本身不能创建对象,只能创建接口的实现类对象,接口类型的变量可以与实现类对象构成多态引用。
  • 声明接口用interface,接口的成员声明有限制:
    • (1)公共的静态常量
    • (2)公共的抽象方法
    • (3)公共的默认方法(JDK8.0 及以上)
    • (4)公共的静态方法(JDK8.0 及以上)
    • (5)私有方法(JDK9.0 及以上)
  • 类可以实现接口,关键字是implements,而且支持多实现。如果实现类不是抽象类,就必须实现接口中所有的抽象方法。如果实现类既要继承父类又要实现父接口,那么继承(extends)在前,实现(implements)在后。
  • 接口可以继承接口,关键字是extends,而且支持多继承。
  • 接口的默认方法可以选择重写或不重写。如果有冲突问题,另行处理。子类重写父接口的默认方法,要去掉default,子接口重写父接口的默认方法,不要去掉default。
  • 接口的静态方法不能被继承,也不能被重写。接口的静态方法只能通过“接口名.静态方法名”进行调用。

(2)面试题

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

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

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

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

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

静态方法

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

默认方法

(1)我们要在已有的老版接口中提供新方法时,如果添加抽象方法,就会涉及到原来使用这些接口的类就会有问题,那么为了保持与旧版本代码的兼容性,只能允许在接口中定义默认方法实现。比如:Java8中对Collection、List、Comparator等接口提供了丰富的默认方法。

(2)当我们接口的某个抽象方法,在很多实现类中的实现代码是一样的,此时将这个抽象方法设计为默认方法更为合适,那么实现类就可以选择重写,也可以选择不重写。

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

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

六、接口与抽象类之间的对比

【区分抽象类和接口】

  • 共性

    • 都可以声明抽象方法。(抽象类可能没有抽象方法)
    • 都不能实例化。(都不能创建对象)
  • 不同

    • 抽象类一定有构造器。接口没有构造器。
    • 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系。
    • 抽象类中的属性可以随意定义,但是接口中只能是常量。

☕总结如下图:

image.png

在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。

七、 练习

(1)练习1

🌋题目描述

1、声明接口Eatable,包含抽象方法public abstract void eat();

2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭。

3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭。

4、声明实现类印度人Indian,重写抽象方法,打印用手抓饭。

5、声明测试类EatableTest,创建Eatable数组,存储各国人对象,并遍历数组,调用eat()方法。

🌱代码

【Eatable.java】

package yuyi02;

/**
 * ClassName: Eatable
 * Package: yuyi02
 * Description:
 *      1、声明接口Eatable,包含抽象方法public abstract void eat();
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 9:37
 */
public abstract interface Eatable {  //接口
    public abstract void eat();
}

【Chinese.java】

package yuyi02;

/**
 * ClassName: Chinese
 * Package: yuyi02
 * Description:
 *      2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 9:39
 */
public class Chinese implements Eatable{
    @Override
    public void eat() {
        System.out.println("中国人使用筷子吃饭");
    }
}

【American.java】

package yuyi02;

/**
 * ClassName: American
 * Package: yuyi02
 * Description:
 *      3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 9:46
 */
public class American implements Eatable{
    @Override
    public void eat() {
        System.out.println("美国人使用刀叉吃饭");
    }
}

【Indian.java】

package yuyi02;

/**
 * ClassName: Indian
 * Package: yuyi02
 * Description:
 *      4、声明实现类印度人Indian,重写抽象方法,打印用手抓饭
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 9:49
 */
public class Indian implements Eatable{
    @Override
    public void eat() {
        System.out.println("印度人使用手抓吃饭");
    }
}

【EatableTest.java】

package yuyi02;

/**
 * ClassName: EatableTest
 * Package: yuyi02
 * Description:
 *      5、声明测试类EatableTest,创建Eatable数组,存储各国人对象,并遍历数组,调用eat()方法
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 9:50
 */
public class EatableTest {
    public static void main(String[] args) {
        //new的是一个数组,数组这里指明的是元素的类型,类型是Eatable,new的并不是构造器,只是指明了元素类型
        Eatable[] eatables=new Eatable[3];  //接口类型3个元素

        //创建实现类对象,并赋值给数组元素
        eatables[0]=new Chinese(); //针对数组中每个元素进行赋值,这后边new的是构造器
        eatables[1]=new American();
        eatables[2]=new Indian();

        //遍历数组
        for (int i = 0; i < eatables.length; i++) {
            eatables[i].eat();
        }
    }
}

🍺输出结果

image.png

🍹注意

//new的是一个数组,数组这里指明的是元素的类型,类型是Eatable,new的并不是构造器,只是指明了元素类型
Eatable[] eatables=new Eatable[3];  //接口类型3个元素

//创建实现类对象,并赋值给数组元素
eatables[0]=new Chinese(); //针对数组中每个元素进行赋值,这后边new的是构造器
eatables[1]=new American();
eatables[2]=new Indian();

只要声明接口了,给它实例化的时候,一定是实现类的对象,必然要体现多态性。

只要有接口的地方就有多态性!

(2)练习2

🌋题目描述

1、定义一个接口用来实现两个对象的比较。

interface CompareObject{
   //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
   public int compareTo(Object o);
}

2、定义一个Circle类,声明radius属性,提供getter和setter方法。

3、定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。

在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。

4、定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。

拓展:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。

🌱代码

【Circle.java】

package yuyi03;

/**
 * ClassName: Circle
 * Package: yuyi03
 * Description:
 *      定义一个Circle类,声明radius属性,提供getter和setter方法
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 14:52
 */
public class Circle {   //两个Circle对象不能够比较大小
    public double radius;   //半径

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public Circle() {

    }

    public Circle(double radius) {
        this.radius = radius;
    }

    //toString方法
    @Override
    public String toString() {
        return "Circle{" +
        "radius=" + radius +
        '}';
    }
}

【CompareObject.java】

package yuyi03;

/**
 * ClassName: CompareObject
 * Package: yuyi03
 * Description:
 *      定义一个接口用来实现两个对象的比较。
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 10:09
 */
public interface CompareObject {    //自定义一个接口来比较对象大小
    //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
    public int compareTo(Object o); //这个是抽象方法(省略了abstract),虽然它没有方法体,但是这个方法是做什么的,形参是什么意思,返回值类型是什么都完全确定了,只是细节没有确定
}

【ComparableCircle.java】

package yuyi03;

/**
 * ClassName: ComparableCircle
 * Package: yuyi03
 * Description:
 *      定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
 *      在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 14:56
 */
public class ComparableCircle extends Circle implements CompareObject{
    //根据对象半径的大小,比较对象的大小(和之前说的equals很像)
    @Override
    public int compareTo(Object o) {
        if(this==o){    //判断当前对象与o是不是指向同一个
            return 0;   //若地址一样,则半径肯定一致,直接返回0
        }
        if(o instanceof ComparableCircle){  //判断是否是当前类的对象
            ComparableCircle c=(ComparableCircle) o;    //若是当前类对象,先强转一下 (从父类对象强转成子类才能调用子类特有的结构)

            //错误的(逻辑上错误)
            //return (int) (this.getRadius()-c.getRadius());    //当两者整数部分都一致的时候,不靠谱

            //正确的写法1
            /*if(this.getRadius()>c.getRadius()){
                return 1;
            } else if (this.getRadius()<c.getRadius()) {
                return -1;
            }else{
                return 0;
            }*/

            //正确写法2
            return Double.compare(this.getRadius(),c.getRadius()); //API里面有一个类就叫Double,里面有一个方法叫compare(),里面传入两个double类型的值,就会自动比较它们的大小,返回的就是一个int类型的值,直接return即可

        }else{  //当这个对象不是当前实例
            throw new RuntimeException("输入类型不匹配");
        }

    }

    public ComparableCircle() {

    }

    public ComparableCircle(double radius) {
        super(radius);
    }
}

【InterfaceTest.java】

package yuyi03;

/**
 * ClassName: InterfaceTest
 * Package: yuyi03
 * Description:
 *      定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 15:43
 */
public class InterfaceTest {
    public static void main(String[] args) {
        ComparableCircle c1=new ComparableCircle(2.3);
        ComparableCircle c2=new ComparableCircle(5.3);

        int compareValue=c1.compareTo(c2);
        if(compareValue>0){
            System.out.println("c1对象大");
        } else if (compareValue<0) {
            System.out.println("c2对象大");
        }else {
            System.out.println("c1与c2一样大");
        }
    }
}

🍺输出结果

image.png

🍹注意

//根据对象半径的大小,比较对象的大小(和之前说的equals很像)
@Override
public int compareTo(Object o) {
    if(this==o){    //判断当前对象与o是不是指向同一个
        return 0;   //若地址一样,则半径肯定一致,直接返回0
    }
    if(o instanceof ComparableCircle){  //判断是否是当前类的对象
        ComparableCircle c=(ComparableCircle) o;    //若是当前类对象,先强转一下
        return (int) (this.getRadius()-c.getRadius());
    }
	//...
}

注意return (int) (this.getRadius()-c.getRadius());这里强转是不好使的(因为半径radius是double类型的)。

this.getRadius()是2.5,c.getRadius()是2.1,那么this.getRadius()-c.getRadius()则是2.5-2.1=0.4,经过强转就是0,0表示两个对象相等,不靠谱。

API里面有一个类就叫Double,里面有一个方法叫compare(),里面传入两个double类型的值,就会自动比较它们的大小,返回的就是一个int类型的值,直接return即可。

//正确写法2
return Double.compare(this.getRadius(),c.getRadius()); 

当这个对象不是当前实例的时候,相当于这两个对象就没有办法比较,此时还必须要一个返回值,但是 return 任何一个值都不太合适。

image.png

若是返回大于0的数,则当前对象大;若为0,则一样大;若是小于0,则当前小。

以后会说“异常”,这个时候就可以拋一个异常,在异常里面写“输入的类型不匹配”即可。(这里需要的类型是ComparableCircle,结果传过来的不是这个类型,没法比较)

else{  //当这个对象不是当前实例
    throw new RuntimeException("输入类型不匹配");
}

只要你实现CompareObject接口(这个接口用于定义比较大小的事儿),就需要重写compareTo这个方法,只要重写了这个方法,就知道谁大谁小。

(3)练习3

🌋题目描述

阿里的一个工程师Developer,结构见图。

image.png

其中,有一个乘坐交通工具的方法takingVehicle(),在此方法中调用交通工具的run()。

为了出行方便,他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如图。
image.png

其中,电动车增加动力的方式是充电,轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类的相关属性信息。
请编写相关代码,并测试。

提示:创建Vehicle[]数组,保存阿里工程师的三辆交通工具,并分别在工程师的takingVehicle()中调用。

🌱代码

【Developer.java】

package yuyi04;

/**
 * ClassName: Developer
 * Package: yuyi04
 * Description:
 *      阿里的一个工程师Developer,结构见图。
 *      其中,有一个乘坐交通工具的方法takingVehicle(),在此方法中调用交通工具的run()。
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:15
 */
public class Developer {
    private String name;
    private int age;

    public Developer() {

    }

    public Developer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void takingVehicle(Vehicle vehicle){
        vehicle.run();
    }
}

【IPower.java】

package yuyi04;

/**
 * ClassName: IPower
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:40
 */
public interface IPower {
    void power();
}

【Vehicle.java】

package yuyi04;

/**
 * ClassName: Vehicle
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:21
 */
public abstract class Vehicle { //abstract抽象类,不能实例化(创建对象)
    private String brand;   //品牌
    private String color;   //颜色

    public Vehicle() {

    }

    public Vehicle(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public abstract void run(); //接口里面可以省略public abstract,抽象类里面不可以
}

【Bicycle.java】

package yuyi04;

/**
 * ClassName: Bicycle
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:30
 */
public class Bicycle extends Vehicle{
    @Override
    public void run() {
        System.out.println("自行车通过人力行驶");
    }

    public Bicycle() {

    }

    public Bicycle(String brand, String color) {
        super(brand, color);
    }
}

【ElectricVehicle.java】

package yuyi04;

/**
 * ClassName: ElectricVehicle
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:34
 */
public class ElectricVehicle extends Vehicle implements IPower{
    @Override
    public void run() {
        System.out.println("电动车通过电机驱动行驶");
    }

    public ElectricVehicle() {

    }

    public ElectricVehicle(String brand, String color) {
        super(brand, color);
    }

    @Override
    public void power() {
        System.out.println("电动车使用电力提供动力");
    }
}

【Car.java】

package yuyi04;

/**
 * ClassName: Car
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:36
 */
public class Car extends Vehicle implements IPower{
    private String carNumber;

    @Override
    public void run() {
        System.out.println("汽车通过内燃机驱动行驶");
    }

    public Car() {

    }

    public Car(String brand, String color, String carNumber) {
        super(brand, color);
        this.carNumber = carNumber;
    }

    public String getCarNumber() {
        return carNumber;
    }

    public void setCarNumber(String carNumber) {
        this.carNumber = carNumber;
    }

    @Override
    public void power() {
        System.out.println("汽车通过汽油提供动力");
    }
}

【VehicleTest.java】

package yuyi04;

/**
 * ClassName: VehicleTest
 * Package: yuyi04
 * Description:
 *      为了出行方便,他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如图。
 *      其中,电动车增加动力的方式是充电,轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类的相关属性信息。
 *
 *      提示:创建Vehicle[]数组,保存阿里工程师的三辆交通工具,并分别在工程师的takingVehicle()中调用。
 * @Author 雨翼轻尘
 * @Create 2023/11/28 0028 17:45
 */
public class VehicleTest {
    public static void main(String[] args) {
        //创建一个工程师
        Developer developer=new Developer();

        //创建三个交通工具,保存在数组中(数组的类型是三个交通工具的共同类型)
        Vehicle[] vehicles=new Vehicle[3];  //声明了3个元素的数组结构,每个元素是Vehicle,并不是new了一个构造器
        vehicles[0]=new Bicycle("捷安特","红色");
        vehicles[1]=new ElectricVehicle("爱玛","蓝色");
        vehicles[2]=new Car("奔驰","黑色","皖P666");

        for (int i = 0; i < vehicles.length; i++) {
            developer.takingVehicle(vehicles[i]);

            if(vehicles[i] instanceof IPower){  //接口这儿仍然可以这样来写
                ((IPower) vehicles[i]).power();
            }
        }

    }
}

🍺输出结果

image.png

🍹注意

Ctrl+I可以调出接口要重写的方法。

比如:

image.png

则可以自动生成:

@Override
public void power() {

}

image.png

编译看左边,运行看右边。这是多态的体现,虽然编译的时候,点进去是在vehicle类中,但真正执行run方法的是实现父类抽象方法的子类。

这个是自己加的新功能,题目中并没有要求。

接口也可以这样来写:

if(vehicles[i] instanceof IPower){  //接口这儿仍然可以这样来写

}

当然,此时用vehicles[i]调用power()方法是不可以的,如下:
image.png

vehicles[i]本身没有power()方法,所以此时需要强转,如下:

if(vehicles[i] instanceof IPower){  //接口这儿仍然可以这样来写
    ((IPower) vehicles[i]).power();
}

Vechicles类型是父类,没有重写接口的power方法,我们需要将其类型转化为Ipower类型的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1270516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang数据类型(数组)

数组重要概念 数组&#xff08;Array&#xff09;是一种非常常见的数据类型&#xff0c;几乎所有的计算机编程语言中都会用到它 在Go语言中&#xff0c;数组有如下的特点&#xff1a; 数组里的元素必须全部为同一类型&#xff0c;要嘛全部是字符串&#xff0c;要嘛全部是整数…

linux 讨论题合集(个人复习)

常规文件的权限是什么&#xff1f;如何分配或修改这些权限&#xff1f;文件夹&#xff08;目录&#xff09;的权限是什么&#xff1f;显示常规文件和文件夹的区别 讨论&#xff1a;①常规的文件权限有四种&#xff0c;r可读、w可写、x可执行、-没有权限&#xff1b;②可以使用c…

【智能家居】一、工厂模式实现继电器灯控制

一、用户手册对应的I/O 二、工厂模式实现继电器灯控制 三、代码段 controlDevice.h&#xff08;设备类&#xff09;main.c&#xff08;主函数&#xff09;bathroomLight.c&#xff08;浴室灯&#xff09;bedroomLight.c&#xff08;卧室灯&#xff09;bedroomLight.c&#xff…

滴滴就系统故障再次致歉

滴滴出行官博发文就11月27日夜间发生的系统故障再次致歉&#xff0c;同时表示&#xff0c;初步确定&#xff0c;这起事故的起因是底层系统软件发生故障&#xff0c;并非网传的“遭受攻击”&#xff0c;后续将深入开展技术风险隐患排查和升级工作&#xff0c;全面保障服务稳定性…

Linux系统centos7防火墙firewall开放IP及端口命令

CentOS7使用的是firewall防火墙&#xff0c;不再是原来的iptables 防火墙基础命令 1&#xff1a;查看firewall防火墙状态 firewall-cmd --state //或 systemctl status firewalld2&#xff1a;打开防火墙 systemctl start firewalld3&#xff1a;关闭防火墙 systemctl sto…

MxL3706-AQ-R 2.0通道绑定同轴网络集成电路特性

MxL3706-AQ-R是Max线性公司的第三代MoCA2.0同轴网络控Z器SoC&#xff0c;可用于在现有的家庭同轴电缆上创建具有千兆位吞吐量性能的家庭网络。 该MxL3706-AQ-R工作在400MHz至1675MHz之间的无线电频率&#xff0c;并与satellite共存&#xff0c;电X和有线电视运营商的频率计划。…

java学习part23异常try catch

124-异常处理-异常的概述与常见异常的举例_哔哩哔哩_bilibili 1.异常 2.try catch 3.finally 类似golang的defer 一定执行的语句

用Java制作简易版的王者荣耀

第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import j…

HarmonyOS4.0开发应用(二)【快速学习】

快速学习 创建项目 1.开始创建 2.选择模板 刚开始选择空白的模板即可 3.填写项目信息 这样一个基本项目就创建好了 代码结构 实现Demo(文字动态切换) Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fo…

文件fd【Linux系统编程】

本文是基础IO的第一个部分&#xff0c;基础IO部分将主要讲解以下内容&#xff1a;文件fd 文件系统 软硬链接 操作系统的内存管理 以及 动静态库。本节重点讲解文件fd&#xff0c;其余内容将在后面的博客更新。 一、共识 文件 内容 属性 文件分为打开了的文件和没打开的文件。…

YOLOv7独家原创改进:自研独家创新FT_Conv,卷积高效结合傅里叶变换

💡💡💡本文自研创新改进:卷积如何有效地和频域结合,引入分数阶傅里叶变换和分数阶Gabor变换,最终创新到YOLOv7。 推荐指数:五星 在道路缺陷检测任务中,原始map为0.8,FT_Conv为0.82 收录YOLOv7原创自研 https://blog.csdn.net/m0_63774211/category_12511937.htm…

Mysql之子查询(知识点+例题)

Mysql之子查询<知识点例题> 什么是子查询案例分析案例分析子查询的分类单行子查询子查询中的空值问题题目练习题目一题目二题目三题目四题目五补充&#xff1a;聚合函数与GROUP BY的使用关系 CASE表达式&#xff08;子查询中的运用&#xff09;简单CASE表达式搜索CASE表达…

第72讲:MySQL数据库锁机制剖析:行级锁、间隙锁与临键锁详解及应用指南

文章目录 1.行级锁的概念2.行锁的概念以及基本使用2.1.行锁的概念2.2.常见的SQL语句所对应的行锁类别2.3.行锁的基本使用 3.间隙锁和临键锁的概念以及基本使用3.1.间隙锁和临键锁的概念3.2.间隙锁和临键锁的基本使用 1.行级锁的概念 行级锁指的是&#xff0c;每次操作锁住的是…

PyCharm免费安装和新手使用教程

PyCharm是一款由JetBrains公司开发的Python集成开发环境&#xff08;IDE&#xff09;。它提供了一系列强大的功能&#xff0c;包括自动代码完成、语法高亮、自动缩进、代码重构、调试器、测试工具、版本控制工具等&#xff0c;使开发者可以更加高效地开发Python应用程序。 新手…

C++中的类型转换和异常

C类型转换 类型转换(cast) 是将一种数据类型转换成另一种数据类型。例如&#xff0c;如果将一个整型 值赋给一个浮点类型的变量&#xff0c;编译器会暗地里将其转换成浮点类型。 转换是非常有用的&#xff0c;但是它也会带来一些问题&#xff0c;比如在转换指针时&#xff0c…

a-table:表格组件常用功能记录——基础积累2

antdvue是我目前项目的主流&#xff0c;在工作过程中&#xff0c;经常用到table组件。下面就记录一下工作中经常用到的部分知识点。 a-table&#xff1a;表格组件常用功能记录——基础积累2 效果图1.table 点击行触发点击事件1.1 实现单选 点击事件1.2 实现多选 点击事件1.3 实…

高效办公:如何使用视频剪辑工具批量转码,mp4视频到TS视频

在视频处理过程中&#xff0c;转码是一项常见的任务。将MP4视频转换为TS视频可以提供许多优势&#xff0c;包括更好的兼容性、更广泛的设备和平台支持以及更高的视频质量。然而&#xff0c;手动转码大量视频文件可能会非常耗时且效率低下。为了实现高效办公&#xff0c;可以使用…

模板引擎详解

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f324;️动态页面的渲染方式 …

2023-简单点-树莓派-camera module 3介绍

Raspberry Pi Camera Module 3 介绍开头先说&#xff0c;注意事项libcamera感光芯片和驱动的兼容图额外&#xff1a;不同module的对应的参数图用bullseye系统测试摄像头预览窗口拍摄静态图片bayer raw图像获取超长曝光视频流raw视频流 ref 介绍 树莓派镜像在Bullseye版本之后&…

【每日一题】1657. 确定两个字符串是否接近-2023.11.30

题目&#xff1a; 1657. 确定两个字符串是否接近 如果可以使用以下操作从一个字符串得到另一个字符串&#xff0c;则认为两个字符串 接近 &#xff1a; 操作 1&#xff1a;交换任意两个 现有 字符。 例如&#xff0c;abcde -> aecdb操作 2&#xff1a;将一个 现有 字符的…