十、java 接口

文章目录

  • 接口
    • 1.1 接口的概念
    • 1.2 接口定义
    • 1.3 实现接口
    • 1.4 使用接口
    • 1.5 接口的细节
    • 1.6 Java 8 和Java 9 对接口的增强

接口

本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记

很多时候,我们将对象看作属于某种数据类型,并按该类型进行操作,在一些情况下,并不能反映对象以及对对象操作的本质。为什么这么说呢?很多时候,我们实际上关心的,并不是对象的类型,而是对象的能力,只要能提供这个能力,类型并不重要。我们来看一些生活中的例子。比如要拍照,很多时候,只要能拍出符合需求的照片就行,至于是用手机拍,还是用Pad拍,或者是用单反相机拍,并不重要,即关心的是对象是否有拍出照片的能力,而并不关心对象到底是什么类型,手机、Pad或单反相机都可以。又如要计算一组数字,只要能计算出正确结果即可,至于是由人心算,用算盘算,用计算器算,用计算机软件算,并不重要,即关心的是对象是否有计算的能力,而并不关心对象到底是算盘还是计算器。再如要将冷水加热,只要能得到热水即可,至于是用电磁炉加热,用燃气灶加热,还是用电热水壶加热,并不重要,即重要的是对象是否有加热水的能力,而并不关心对象到底是什么类型。在这些情况中,类型并不重要,重要的是能力。那如何表示能力呢?接口。下面就来详细介绍接口,包括其概念、用法、一些细节。

1.1 接口的概念

接口这个概念在生活中并不陌生,比如USB接口。计算机往往有多个USB接口,可以插各种USB设备,如键盘、鼠标、U盘、摄像头、手机等。接口声明了一组能力,但它自己并没有实现这个能力,它只是一个约定。接口涉及交互两方对象,一方需要实现这个接口,另一方使用这个接口,但双方对象并不直接互相依赖,它们只是通过接口间接交互,如下图所示。

对象甲
接口
对象乙,实现接口

拿上面的USB接口来说,USB协议约定了USB设备需要实现的能力,每个USB设备都需要实现这些能力,计算机使用USB协议与USB设备交互,计算机和USB设备互不依赖,但可以通过USB接口相互交互。java的接口就类似USB协议,接口实现类类似于鼠标等USB设备,而接口使用类类似于电脑。

接口扩展了继承,降低了耦合性。继承中类和子类之间是is-a的关系,接口中类和接口是like-ahas-a的关系。

1.2 接口定义

我们定义一个用于比较的接口,很多对象都可以比较,对于求最大值、求最小值、排序的程序而言,它们其实并不关心对象的类型是什么,只要对象可以比较就可以了,或者说,它们关心的是对象有没有可比较的能力。Java API中提供了Comparable接口,以表示可比较的能力,但它使用了泛型,本节我们定义一个MyComparable接口,用于实现和Comparable接口一样的功能。首先来定义这个接口,代码如下:

package com.ieening.learnInterface;public interface MyComaprable {int compareTo(Object other);
}

定义接口的代码解释如下:

  1. Java使用interface这个关键字来声明接口,修饰符一般都是public
  2. interface后面就是接口的名字MyComparable
  3. 接口定义里面,声明了一个方法compareTo,但没有定义方法体,Java 8之前,接口内不能实现方法。接口方法不需要加修饰符,加与不加相当于都是public abstract

再来解释compareTo方法:

  1. 方法的参数是一个Object类型的变量other,表示另一个参与比较的对象。
  2. 第一个参与比较的对象是自己。
  3. 返回结果是int类型,-1表示自己小于参数对象,0表示相同,1表示大于参数对象。

接口与类不同,它的方法没有实现代码。定义一个接口本身并没有做什么,也没有太大的用处,它还需要至少两个参与者:一个需要实现接口,另一个使用接口。我们先来实现接口。

1.3 实现接口

类可以实现接口,表示类的对象具有接口所表示的能力。以自定义Point类举例。我们让Point具备可以比较的能力,Point之间怎么比较呢?我们假设按照与原点的距离进行比较,Point类代码如下所示。

package com.ieening.learnInterface;public class Point implements MyComaprable { // 注释1private int x;public int getX() {return x;}public void setX(int x) {this.x = x;}private int y;public int getY() {return y;}public void setY(int y) {this.y = y;}public Point(int x, int y) {this.x = x;this.y = y;}public double distance() {return Math.sqrt(x * x + y * y);}@Overridepublic int compareTo(Object other) { // 注释2if (other instanceof Point) { // 注释3Point otherPoint = (Point) other; // 注释4double delta = distance() - otherPoint.distance();if (delta < 0) {return -1;} else if (delta > 0) {return 1;} else {return 0;}} else {throw new IllegalArgumentException(other.toString() + " is not instanceof " + this.getClass().getName());}}@Overridepublic String toString() {return "Point [x=" + x + ", y=" + y + "]";}
}

代码解释如下:

  1. 注释1:Java使用implements这个关键字表示实现接口,前面是类名,后面是接口名。
  2. 注释2:实现接口必须要实现接口中声明的方法,Point实现了compareTo方法。

再来解释PointcompareTo实现。

  1. 注释3:Point不能与其他类型的对象进行比较,它首先检查要比较的对象是否是Point类型,如果不是,使用throw抛出一个异常。
  2. 注释4:如果是Point类型,则使用强制类型转换将Object类型的参数other转换为Point类型的参数otherPoint

一个类可以实现多个接口,表明类的对象具备多种能力,各个接口之间以逗号分隔,语法如下所示:

public class Test implements Interface1, Interface2 {// 主体代码
}

1.4 使用接口

与类不同,接口不能新建new,不能直接创建一个接口对象,对象只能通过类来创建。但可以声明接口类型的变量,引用实现了接口的类对象。比如,可以这样:

MyComparable p1 = new Point(2,3);
MyComparable p2 = new Point(1,2);
System.out.println(p1.compareTo(p2));

p1p2MyComparable类型的变量,但引用了Point类型的对象,之所以能赋值是因为Point实现了MyComparable接口。如果一个类型实现了多个接口,那么这种类型的对象就可以被赋值给任一接口类型的变量。p1p2可以调用MyComparable接口的方法,也只能调用MyComparable接口的方法,实际执行时,执行的是具体实现类的代码。为什么Point类型的对象非要赋值给MyComparable类型的变量呢?在以上代码中,确实没必要。但在一些程序中,代码并不知道具体的类型,这才是接口发挥威力的地方。我们来看下面使用MyComparable接口的例子,代码如下所示:

package com.ieening.learnInterface;import java.util.Objects;public class CompareUtil {public static Object max(MyComaprable[] objs) {if (Objects.isNull(objs) || objs.length == 0) {return null;}MyComaprable max = objs[0];for (int i = 1; i < objs.length; i++) {if (max.compareTo(objs[i]) < 0) {max = objs[i];}}return max;}public static void sort(MyComaprable[] objs) {if (!(Objects.isNull(objs) || objs.length == 0)) {for (int i = 0; i < objs.length; i++) {int min = i;for (int j = i + 1; j < objs.length; j++) {if (objs[j].compareTo(objs[min]) < 0) {min = j;}}if (min != i) {MyComaprable temp = objs[i];objs[i] = objs[min];objs[min] = temp;}}}}
}

CompUtil提供了两个方法,max获取传入数组中的最大值,sort对数组升序排序,参数都是MyComparable类型的数组,可以看出,这个类是针对MyComparable接口编程,它并不知道具体的类型是什么,也并不关心,但却可以对任意实现了MyComparable接口的类型进行操作。我们来看如何对Point类型进行操作,代码如下:

package com.ieening;import static org.junit.Assert.assertTrue;import org.junit.Test;import com.ieening.learnInterface.CompareUtil;
import com.ieening.learnInterface.MyComaprable;
import com.ieening.learnInterface.Point;public class TestCompareUtil {@Testpublic void testMax() {MyComaprable[] points = new MyComaprable[] { (MyComaprable) new Point(1, 2), (MyComaprable) new Point(2, 1),(MyComaprable) new Point(1, 1), (MyComaprable) new Point(-1, -3) };Point point = (Point) CompareUtil.max(points);assertTrue(point.compareTo(new Point(-1, -3)) == 0);}@Testpublic void testSort() {MyComaprable[] points = new MyComaprable[] { (MyComaprable) new Point(1, 2), (MyComaprable) new Point(2, 1),(MyComaprable) new Point(1, 1), (MyComaprable) new Point(-1, -3) };CompareUtil.sort(points);for (MyComaprable point : points) {System.out.println((Point) point);}}
}

以上代码创建了一个Point类型的数组points,然后使用CompareUtilmax方法获取最大值,使用sort排序,并输出结果。这里演示的是对Point数组操作,实际上可以针对任何实现了MyComparable接口的类型数组进行操作。这就是接口的魅力,可以说,针对接口而非具体类型进行编程,是计算机程序的一种重要思维方式。接口很多时候反映了对象以及对对象操作的本质。它的优点有很多,首先是代码复用,同一套代码可以处理多种不同类型的对象,只要这些对象都有相同的能力,如CompareUtil。接口更重要的是降低了耦合,提高了灵活性。使用接口的代码依赖的是接口本身,而非实现接口的具体类型,程序可以根据情况替换接口的实现,而不影响接口使用者。解决复杂问题的关键是分而治之,将复杂的大问题分解为小问题,但小问题之间不可能一点关系没有,分解的核心就是要降低耦合,提高灵活性,接口为恰当分解提供了有力的工具。

1.5 接口的细节

前面介绍了接口的基本内容,接口还有一些细节,包括:

  1. 接口中的变量。
  2. 接口的继承。
  3. 类的继承与接口。
  4. instanceof。

1、接口中的变量

接口中也可以定义变量,语法如下所示:

public interface Interface1 {public static final int a = 0;
}

这里定义了一个变量int a,修饰符是public static final,但这个修饰符是可选的,即使不写,也是public static final。这个变量可以通过接口名.变量名的方式使用,如``Interface1.a`。

2、接口的继承

接口也可以继承,一个接口可以继承其他接口,继承的基本概念与类一样,但与类不同的是,接口可以有多个父接口,代码如下所示:

public interface IBase1 {void method1();
}
public interface IBase2 {void method2();
}
public interface IChild extends IBase1, IBase2 {
}

IChildIBase1IBase2两个父类,接口的继承同样使用extends关键字,多个父接口之间以逗号分隔。

3、类的继承与接口

类的继承与接口可以共存,换句话说,类可以在继承基类的情况下,同时实现一个或多个接口,语法如下所示:

public class Child extends Base implements IChild {//主体代码
}

关键字extends要放在implements之前。

4、instanceof

instanceof与类一样,接口也可以使用instanceof关键字,用来判断一个对象是否实现了某接口,例如:

Point p = new Point(2,3);
if(p instanceof MyComparable){System.out.println("comparable");
}

1.6 Java 8 和Java 9 对接口的增强

需要说明的是,前面介绍的都是Java 8之前的接口概念,Java 8Java 9对接口做了一些增强。在Java 8之前,接口中的方法都是抽象方法,都没有实现体,Java 8允许在接口中定义两类新方法:静态方法和默认方法,它们有实现体,比如:

public interface IDemo {void hello();public static void test() {System.out.println("hello");}default void hi() {System.out.println("hi");}
}

test()就是一个静态方法,可以通过IDemo.test()调用。在接口不能定义静态方法之前,相关的静态方法往往定义在单独的类中,比如,Java API中,Collection接口有一个对应的单独的类Collections,在Java 8中,就可以直接写在接口中了,比如Comparator接口就定义了多个静态方法。

hi()是一个默认方法,用关键字default表示。默认方法与抽象方法都是接口的方法,不同在于,默认方法有默认的实现,实现类可以改变它的实现,也可以不改变。引入默认方法主要是函数式数据处理的需求,是为了便于给接口增加功能。在没有默认方法之前,Java是很难给接口增加功能的,比如List接口,因为有太多非Java JDK控制的代码实现了该接口,如果给接口增加一个方法,则那些接口的实现就无法在新版Java上运行,必须改写代码,实现新的方法,这显然是无法接受的。函数式数据处理需要给一些接口增加一些新的方法,所以就有了默认方法的概念,接口增加了新方法,而接口现有的实现类也不需要必须实现。看一些例子,List接口增加了sort方法,其定义为:

default void sort(Comparator<? super E> c) {Object[] a = this.toArray();Arrays.sort(a, (Comparator) c);ListIterator<E> i = this.listIterator();for(Object e : a) {i.next();i.set((E) e);}
}

Collection接口增加了stream方法,其定义为:

default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);
}

因为类可以继承以及实现接口,那么,当父类的方法和接口中默认方法有相同的函数签名时,那么实例对象调用该方法时,最终执行的是父类的,还是接口的?

答案:执行的是父类的,遵循父类优先原则。

B类实现了多个接口。如果多个接口中出现了方法签名相同的默认方法,那么访问通过B类的实例对象调用该方法,最终执行的是哪个接口?

答案:要求类B必须重写该方法,否则产生编译错误。方法重写后调用方法时执行的一定是重写后的方法体。

Java 8中,静态方法和默认方法都必须是public的,Java 9去除了这个限制,它们都可以是private的,引入private方法主要是为了方便多个静态或默认方法复用代码,比如:这里,actionAactionB两个默认方法共享了相同的common()方法的代码。

public interface IDemoPrivate {private void common() {System.out.println("common");}default void actionA() {common();}default void actionB() {common();}}

  1. 马俊昌.Java编程的逻辑[M].北京:机械工业出版社,2018. ↩︎

  2. 尚硅谷教育.剑指Java:核心原理与应用实践[M].北京:电子工业出版社,2023. ↩︎

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

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

相关文章

区块链互操作协议

1. 引言 Alexei Zamyatin等人2019年论文 SoK: Communication Across Distributed Ledgers。 参考资料 [1] 2019年论文 SoK: Communication Across Distributed Ledgers [2] A list of blockchain-related SoK papers [3] 2021年视频 FC21: SoK: Communica…

【机器学习】数据清洗之处理异常点

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步…

数据结构——lesson2线性表和顺序表

目录 前言 一、顺序表是什么&#xff1f; 1. 静态顺序表&#xff1a;使用定长数组存储元素 2. 动态顺序表&#xff1a;使用动态开辟的数组存储。 二、接口实现 1.动态顺序表存储 2.基本增删查改接口 (1)初始化顺序表 (2)顺序表摧毁 (3)检查空间 (4)顺序表打印 (5)顺…

嵌入式学习之Linux入门篇——Linux文件系统的层次结构讲解

在 Linux 操作系统中,所有的文件和目录都被组织成以一个根节点“/”开始的树状结构。其中,目录就相当于 Windows 中的文件夹,目录中存放的既可以是文件,也可以是其他的子目录,而文件中存储的是数据。 文件系统基础知识 文件系统的最顶层是由根目录开始的,系统使用“/”来…

第四篇【传奇开心果微博系列】Python微项目技术点案例示例:美女颜值判官

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展思路四、添加不同类型的美女示例代码五、增加难度等级示例代码六、添加特殊道具示例代码七、设计关卡系统示例代码八、添加音效和背景音乐示例代码九、多人游戏…

C++ 音视频原理

本篇文章我们来描述一下音视频原理 音视频录制原理: 下面是对这张思维导图的介绍 摄像头部分: 麦克风采集声音 摄像头采集画面 摄像头采集回来的数据可以用RGB也可以用YUV来表示 图像帧帧率 一秒能处理多少张图像 图像处理 &#xff1a;调亮度 图像帧队列 :意思是将数据取…

简单介绍源程序执行方式

源程序执行方式 编译和解释 程序设计语言能够把算法翻译成机器能够理解的可执行程序。这里将计算机不能直接执行的非机器语言源程序翻译成能直接执行的机器语言的语言翻译程序称为语言处理程序 源程序&#xff1a;用各种程序设计语言编写的程序称为源程序&#xff0c;计算机不…

C++ //练习 6.6 说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。

C Primer&#xff08;第5版&#xff09; 练习 6.6 练习 6.6 说明形参、局部变量以及局部静态变量的区别。编写一个函数&#xff0c;同时用到这三种形式。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /********************…

分享87个CSS3特效,总有一款适合您

分享87个CSS3特效&#xff0c;总有一款适合您 87个CSS3特效下载链接&#xff1a;https://pan.baidu.com/s/1CAxe8nPBzXvH7Nr6B_U72Q?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不…

更新至2022年,迪博上市公司内部控制指数、分项指数、评级等数据

更新至2022年&#xff0c;上市公司迪博内部控制指数、分项指数、评级等数据 1、时间&#xff1a;更新至2022年&#xff0c;四份数据&#xff0c;具体时间见下文 2、范围&#xff1a;上市公司 3、具体数据&#xff1a; 2000-2022年上市公司内部控制指数评级及评分数据 2007…

第三代互联网web3.0

Web3.0&#xff0c;通常被称为第三代互联网&#xff0c;代表了互联网技术的下一个演进阶段。它主要基于区块链、去中心化和用户赋权的理念构建&#xff0c;旨在创造一个更加智能、开放且安全的网络环境。以下是Web3.0的一些关键特点&#xff1a; 1. **去中心化**&#xff1a;We…

2月8号作业

Sqlite3系统命令 .quit 退出数据库 .exit 退出数据库 .help 显示帮助信息&#xff0c;获取所有系统命令 .table 查看当前数据库下的所有表格 .schema 查看表的结构 Sqlite3语句 创建表格&#xff1a; create table 表名 (字段名 数据类型, 字段名 数据类型); create table if…

【JVM篇】什么是jvm

文章目录 &#x1f354;什么是Java虚拟机&#x1f6f8;Java虚拟机有什么用&#x1f339;Java虚拟机的功能&#x1f388;Java虚拟机的组成 &#x1f354;什么是Java虚拟机 JVM指的是Java虚拟机&#xff0c;本质上是一个运行在计算机上的程序&#xff0c;可以运行 Java字节码文件…

NAT——网络地址转换、NAPT

网络地址转换 NAT (Network Address Translation) 1994 年提出。 需要在专用网连接到互联网的路由器上安装 NAT 软件。 装有 NAT 软件的路由器叫做 NAT路由器&#xff0c;它至少有一个有效的外部全球 IP 地址。 所有使用本地地址的主机在和外界通信时&#xff0c;都要在 NA…

【教程】MySQL数据库学习笔记(三)——数据定义语言DDL(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 文章目录 【MyS…

docker 1:介绍

docker 1&#xff1a;介绍 docker解决哪些问题&#xff1a; 传统APP在安装到不同电脑的时候可能会遇到依赖问题&#xff0c;比如缺少VS 20xx&#xff0c;软件无法运行”的情况。docker使用容器技术将软件 依赖​打包为image包发布&#xff0c;解决了依赖问题。docker有一个官…

ES实战-聚集

根据某字段值聚集返回统计数据 #curl 写法 curl localhost:9200/get-together/_search?pretty -H Content-Type:application/json -d { "aggs":{"top_tags":{"terms":{"field":"tags.verbatim"}} }} # kibana主体写法 GET…

MATLAB|【免费】高比例可再生能源电力系统的调峰成本量化与分摊模型

目录 主要内容 部分代码 结果一览 下载链接 主要内容 程序复现文献《高比例可再生能源电力系统的调峰成本量化与分摊模型》&#xff0c;从净负荷波动的角度出发&#xff0c;建立了调峰成本的量化与分摊模型&#xff0c;构造了无调峰需求的替代场景&#xff0c;将…

在Ubuntu22.04上部署FoooCUS2.1

Fooocus 是一款基于 Gradio的图像生成软件&#xff0c;Fooocus 是对 Stable Diffusion 和 Midjourney 设计的重新思考&#xff1a; 1、从 Stable Diffusion 学习&#xff0c;该软件是离线的、开源的和免费的。 2、从 Midjourney 中学到&#xff0c;不需要手动调整&#xff0c;…

优先级队列(堆)_PriorityQueue

前言 想要看如何使用可以通过目录跳转到 PriorityQueue的使用 优先级队列 概念 队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队 列时&#xff0c;可能需要优先级高的元素先出队列&#xff0c;该中场…