目录
7. 接口(interface)
7.1 类比
生活中大家每天都在用USB接口,那么USB接口与我们今天要学习的接口有什么相同点呢?
USB,(Universal Serial Bus,通用串行总线)是Intel公司开发的总线架构,使得在计算机上添加串行设备(鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等)非常容易。
其实,不管是电脑上的USB插口,还是其他设备上的USB插口都只是遵循了USB规范
的一种具体设备而已。
只要设备遵循USB规范的,那么就可以与电脑互联,并正常通信。至于这个设备、电脑是哪个厂家制造的,内部是如何实现的,我们都无需关心。
Java的软件系统会有很多模块组成,那么各个模块之间也应该采用这种面向接口
的低耦合
,为系统提供更好的可扩展性和可维护性。
7.2 概述
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a
关系。
-
例如:电脑都预留了可以插入USB设备的USB接口,USB接口具备基本的数据传输的开启功能和关闭功能。你能不能用USB进行连接,或是否具备USB通信功能,就看你能否遵循USB接口规范
- 例如:Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品能否实现Java设计的JDBC规范
接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。
7.3 定义格式
接口的定义,它与定义类方式相似,但是使用 interface
关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
引用数据类型:数组,类,枚举,接口,注解。
7.3.1 接口的声明格式
[修饰符] 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可以同步全速地进行读写操作");
}
}
7.3.2 接口的成员说明
在JDK8.0 之前,接口中只允许出现:
(1)公共的静态的常量:其中public static final
可以省略
(2)公共的抽象的方法:其中public abstract
可以省略
理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现
在JDK8.0 时,接口中允许声明默认方法
和静态方法
:
(3)公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略
(4)公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略
在JDK9.0 时,接口又增加了:
(5)私有方法
除此之外,接口中没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化。
7.4 接口的使用规则
1、类实现接口(implements)
接口不能创建对象,但是可以被类实现(implements
,类似于被继承)。
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
【修饰符】 class 实现类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}【修饰符】 class 实现类 extends 父类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
注意:
-
如果接口的实现类是非抽象类,那么必须
重写接口中所有抽象方法
。 -
默认方法可以选择保留,也可以重写。
重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了
-
接口中的静态方法不能被继承也不能被重写
2、接口的多实现(implements)
之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现
。并且,一个类能继承一个父类,同时实现多个接口。
实现格式
【修饰符】 class 实现类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
举例:
3、接口的多继承(extends)
一个接口能继承另一个或者多个接口,接口的继承也使用 extends
关键字,子接口继承父接口的方法。
所有父接口的抽象方法都有重写。
方法签名相同的抽象方法只需要实现一次。
4、接口与实现类对象构成多态引用
实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你new的实现类对象实现的方法体。
5、使用接口的静态成员
接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态常量。
6、使用接口的非静态方法
-
对于接口的静态方法,直接使用“
接口名.
”进行调用即可-
也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
-
-
对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
-
接口不能直接创建对象,只能创建实现类的对象
-
练习1:
CompareObject.java
package exer5;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个接口用来实现两个对象的比较。
* @Author zyjstart
* @Create:2024/9/12 23:28
*/
public interface CompareObject {
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}
Circle.java
package exer5;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个Circle类,声明redius属性,提供getter和setter方法
* @Author zyjstart
* @Create:2024/9/12 23:29
*/
public class Circle {
private double radius; // 半径
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
}
ComparableCircle.java
package exer5;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
* 在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
* @Author zyjstart
* @Create:2024/9/12 23:32
*/
public class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle() {
}
public ComparableCircle(double radius) {
super(radius);
}
// 根据对象的半径的大小,比较对象的大小
@Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
// 正确的写法1:
if (o instanceof CompareObject){
ComparableCircle c = (ComparableCircle) o;
// 正确的写法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());
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
}
InterfaceTest.java
package exer5;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。
* @Author zyjstart
* @Create:2024/9/12 23:47
*/
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(2.3);
ComparableCircle c2 = new ComparableCircle(5.3);
int i = c1.compareTo(c2);
if (i > 0){
System.out.println("c1对象大");
}else if (i < 0){
System.out.println("c2对象大");
}else {
System.out.println("c1和c2一样大");
}
}
}
练习2:交通工具案例
阿里的一个工程师,声明的属性和方法如下:
其中,有一个乘坐交通工具的方法takingVehicle(),在此方法中调用交通工具的run()。为了出行方便,他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如下:
其中,电动车增加动力的方式是充电,轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类的相关属性信息。
请编写相关代码,并测试。
提示:创建Vehicle[]数组,保存阿里工程师的三辆交通工具,并分别在工程师的takingVehicle()中调用。
Vechicle.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
* 车辆类
* @Author zyjstart
* @Create:2024/9/13 15:02
*/
public abstract class Vehicle {
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();
}
IPowe.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
*
* @Author zyjstart
* @Create:2024/9/13 15:19
*/
public interface IPower {
void power();
}
Vehicle.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
* 车辆类
* @Author zyjstart
* @Create:2024/9/13 15:02
*/
public abstract class Vehicle {
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();
}
ElectricVehicle.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
* 电动车类
* @Author zyjstart
* @Create:2024/9/13 15:10
*/
public class ElectricVehicle extends Vehicle implements IPower{
public ElectricVehicle() {
}
public ElectricVehicle(String brand, String color) {
super(brand, color);
}
@Override
public void run() {
System.out.println("电动车通过电机驱动行驶!");
}
@Override
public void power() {
System.out.println("电动车使用电力提供动力");
}
}
Bicycle.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
* 自行车类
* @Author zyjstart
* @Create:2024/9/13 15:06
*/
public class Bicycle extends Vehicle{
public Bicycle() {
}
public Bicycle(String brand, String color) {
super(brand, color);
}
@Override
public void run() {
System.out.println("自行车通过脚踩行驶");
}
}
VechicleTest.java
package exer6;
/**
* ClassName:IntelliJ IDEA
* Description:
* 测试类
* @Author zyjstart
* @Create:2024/9/13 15:22
*/
public class VechicleTest {
public static void main(String[] args) {
Developer developer = new Developer();
// 创建三个交通工具,保存在数组中
Vehicle[] vehicles = new Vehicle[3];
vehicles[0] = new Bicycle("宝如马","白色");
vehicles[1] = new ElectricVehicle("爱玛","黄色");
vehicles[2] = new Car("雅迪","黑色","粤C12345");
for (int i = 0; i < vehicles.length; i++) {
developer.takingVechicle(vehicles[i]);
if (vehicles[i] instanceof IPower){
((IPower) vehicles[i]).power();
}
}
}
}
运行如下: