Java关键字interface(接口)

news2024/12/27 0:31:07

文章目录

  • 接口的理解
  • 接口的声明
  • 接口的成员说明
  • 接口的使用规则
      • 类实现接口(implements)
  • 接口的多实现
  • 接口的多继承(extends)
  • 接口与实现类对象构成多态引用
  • 使用接口的静态成员
  • 使用接口的非静态方法
  • JDK8中相关冲突问题
  • 总结
  • 小测试
  • 接口与抽象类之间的对比
  • 练习

接口的理解

生活中大家每天都在用USB接口,那么USB接口与接口有什么相同点呢?
其实,不管是电脑上的USB插口,还是其他设备上的USB插口都只是遵循了USB规范的一种具体设备而已。
只要设备遵循USB规范的,那么就可以与电脑互联,并正常通信。至于这个设备、电脑是哪个厂家制造的,内部是如何实现的,我们都无需关心。

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

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

  • 例如:Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品能否实现Java设计的JDBC规范
    在这里插入图片描述

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

接口的声明

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

示例代码:


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可以同步全速地进行读写操作");
    }
}

接口的成员说明

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

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

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

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

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

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

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

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

(5)私有方法

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

接口的使用规则

类实现接口(implements)

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

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

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

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

在这里插入图片描述
注意:

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

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

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

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

举例:

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()) ;

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

接口的多实现

对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

实现格式:

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

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

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

举例:
在这里插入图片描述
定义多个接口:

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

定义实现类:

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

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

测试类

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

接口的多继承(extends)

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

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

定义子接口:

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

定义子接口的实现类:

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("输出数据");
    }
}
  • 所有父接口的抽象方法都有重写。
  • 方法签名相同的抽象方法只需要实现一次。

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

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

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

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


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

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

测试类

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();
    }
}

使用接口的静态成员

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

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

使用接口的非静态方法

  • 对于接口的静态方法,直接使用“接口名.”进行调用即可
    • 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
  • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
    • 接口不能直接创建对象,只能创建实现类的对象
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中相关冲突问题

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

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

定义父类:

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

定义子类:

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

定义测试类:

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

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

  • 当一个类同时实现了多个父接口,而多个父接口中包含方法签名相同的默认方法时,怎么办呢?
  • 声明接口:
package com.atguigu.interfacetype;

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

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

public class Girl implements Friend,BoyFriend{

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

}

测试类

public class TestGirl {
    public static void main(String[] args) {
        Girl girl = new Girl();
        girl.date();
    }
}

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

2 常量冲突问题

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

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

父类和父接口:

public class SuperClass {
    int x = 1;
}
public interface SuperInterface {
    int x = 2;
    int y = 2;
}
public interface MotherInterface {
    int x = 3;
}

子类:

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);//没有重名问题,可以直接访问
    }
}

总结

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

小测试

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要允许接口定义私有方法呢?因为我们说接口是规范,规范是需要公开让大家遵守的。

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

接口与抽象类之间的对比

在这里插入图片描述

练习

interface A {
    int x = 0;
}
class B {
    int x = 1;
}
class C extends B implements A {
    public void pX() {
        System.out.println(x);
    }
    public static void main(String[] args) {
        new C().pX();
    }
}

java: 对x的引用不明确
  B 中的变量 x 和 A 中的变量 x 都匹配

方法类优先原则:

interface A {
    int x = 0;
    default void me(){
        System.out.println("interface_me");
    }
}
class B {
    int x = 1;
    public void me(){
        System.out.println("class me");
    }
}
class C extends B implements A {

    public static void main(String[] args) {
        new C().me();
    }
}
输出:class me

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

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

相关文章

不允许你不知道的 MySQL 优化实战(二)

文章目录 11、使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则。12、对查询进行优化,应考虑在where及order by涉及的列上建立索引,尽量避免全表扫描。13、如果插入数据过多,考虑批量插入。14、在适当的时候&…

[QT编程系列-9]:C++图形用户界面编程,QT框架快速入门培训 - 3- QT窗体设计 - 自动布局

目录 3. QT窗体设计 3.7 自动布局 3.7.1 自动布局 3.7.2 在主窗口中自动布局 3.7.3 在自动布局容器中自动布局 3.7.4 在widget中自动布局 3.7.5 自动布局工件 3. QT窗体设计 3.7 自动布局 3.7.1 自动布局 在QT中,自动布局是一种灵活而强大的方式来管理和排…

【ArcGIS Pro二次开发】(47):要素类追加至空库(批量)

本工具主要是针对国空数据入库而做的。 如果你手头已经整理了一部分要素类数据,但是数据格式,字段值可能并没有完全按照规范设置好,需要将这些数据按规范批量和库,就可以尝试用这个工具。 准备数据:标准空库、你已做…

Vue源码分析拓展 - Vue 模板编译渲染函数原理分析

目录 Vue 模板编译渲染函数 编译 Vue 模板编译渲染函数原理分析.html compiletoFunctions.html compileToFunctions.js vue.2.5.1.源码学习.js 一张AI生成图~ Vue 模板编译渲染函数 new Vue():初始化 $mount:挂载 compile():编译 parse:解析 optimize:静态节…

身份证读卡器安卓SDK在安卓12版本targetSdkVersion=32报错解决办法

之前的东信智能的EST-100身份证读卡器安卓SDK版本V1.0.40在安卓12版本,targetSdkVersion32的时候会出现以下错误: Targeting S (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingInten…

Nginx 解决漏洞扫描 弱CORS策略

主要在nginx配置允许通过的地址 如: if ($http_host !~* 192.168.0.1|127.0.0.1|localhost) { # 允许的ipreturn 403 ; }add_header Access-Control-Allow-Origin $http_origin; #跨域请求

高压放大器到底有什么作用

高压放大器是一种重要的电子元器件,其作用是将信号放大到更高的电压水平,以便供给需要高电压的负载使用。高压放大器被广泛应用于通讯设备、医疗仪器、仿真模拟、气体激光、光学器件等领域。下面安泰电子将详细介绍高压放大器的作用以及其在各领域中的应…

Win11 设置FTP服务详细教程

起因: 因测试需要,本机建立FTP服务测试使用,此文章用于记录使用! 操作步骤: 1、配置FTP功能 ①、"winR" > 在运行窗口输入"control" 回车; ②、打开"控制面板" > 点击…

图文讲解Redis延时双删原因及必要性

目录 一、前言 二、常见更新策略 2.1 先删缓存,再更新数据库 2.2 先更新数据库,再删除缓存 2.3 普通双删 2.4 延迟双删 三、建议 一、前言 我们在实际项目中经常会使用到Redis缓存用来缓解数据库压力,但是当更新数据库时,…

今天实习第三天,vue(cli部分)

01.创建第一个vue-cli。这里用的是node.js。早上的时候,就需要把node.js安装上去 02.node.js安装 第一步.去官网下载node.js https://nodejs.org/en 第二步.运行官网下载的node.js的msi文件(记住所有的node.js文件的安装包都是msi文件的形式&#xff0…

算法笔记\python 笔记: 相似性度量

1 欧氏距离 1.1 python实现: from scipy.spatial import distance distance.euclidean([1,2],[2,1]) #1.4142135623730951 1.2 标准化欧氏距离 先将数据标准化 (减去的均值两两抵消) 2 曼哈顿距离 又称为城市街区距离 2.1 python 实现 f…

C++图形开发(16):绘制一个圆环和一根针

文章目录 绘制一个圆环和一根针1.1 绘制1.2 line()函数1.3 circle()函数1.4 setlinestyle()函数1.5 setlinecolor()函数 接下来,我会继续制作一些小游戏,但因为整个难度的上升(毕竟我也是初学者),可能文章不会再像之前…

吐血整理,性能测试-Jmeter分布式压测实战(超细详解)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 Jmeter的集群模式…

InsCode Stable Diffusion使用教程【InsCode Stable Diffusion美图活动一期】

记录一下如何使用 InsCode Stable Diffusion 进行 AI 绘图以及使用感受。 一、背景介绍 目前市面上比较权威,并能用于工作中的 AI 绘画软件其实就两款。一个叫 Midjourney(简称 MJ),另一个叫 Stable Diffusion(简称 …

Unity游戏源码分享-Unity经营类美食小摊小游戏

Unity经营类美食小摊小游戏 挺有意思的小游戏 关卡页面 游戏主页面 有顾客上门 需要给顾客搭配他们想要的美食 会不断地有顾客过来,这个时候就考验手速的时候了,真实模拟经营 服务到位立马有钱 项目地址: https://download.csdn.net/downl…

VisualStudio2022将printf信息打印到控制台

点击“解决方案管理器”,选中项目名称,点击鼠标右键---->属性---->生成事件---->生成后事件,在命令行的右侧输入框里填写如下内容: editbin /SUBSYSTEM:CONSOLE $(OUTDIR)\$(ProjectName).exe接下来在编译运行时&#x…

数据库--->MySQL(2)【事务、SQL优化】

文章目录 事务什么是事务?隔离性中的不同隔离级别事务实现的原理隔离级别的实现原理(MVCC)MySQL中的锁机制 SQL优化 事务 什么是事务? 事务就是逻辑上的一组操作,在同一个事务中,如果有多条sql语句执行&am…

一文详解新一代高效前端构建工具VITE-达观数据

Vite 是一个快速、简单且高效的前端构建工具,它的出现为前端开发者带来了新的构建体验。在本文中,我们将探讨 Vite 的技术原理、优点和使用方法。 Vite 的技术原理 Vite 的核心技术是基于 ES Modules 和浏览器原生模块系统的构建工具。Vite 的构建过程是…

多态的基本使用

这部分的内容主要是记住使用方法,原理在之后会讲。 多态的概念 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 比如说买票,普通人买票就是正常买&…

WTM框架页面被其他网站引用免登录

用ASP.NET CORE开发通常都会有这样一个需求,自己框架开发的页面,要被其他网站嵌套引用,但其他网站通过链接到自己的开发页面的时候,通常会有一个登录页面,有的时候网站无缝集成的时候,这就会要求跳过这个WT…