Java接口的用法(非常详细)

Java 是一门单继承的语言,一个子类只能继承一个父类。但是在编程实践中,对于某些类,只继承一个抽象类显然无法满足要求,需要实现多个抽象类的抽象方法才能解决问题,这就需要通过接口来实现。

在 Java 中,允许通过一个类实现多个接口来实现类似于多重继承的机制。

Java接口的定义

接口可以看作是从多个相似的类中抽象出来的规范,不提供任何实现,体现了规范和实现分离的思想。

例如,计算机上提供了 USB 插槽,只要一个硬件遵守 USB 规范,就能插入 USB 插槽,可以是鼠标、键盘、数据线等,计算机无须关心是和哪个硬件对象建立了联系。同样,软件系统的开发也需要采用规范和实现分离的思想,即采用面向接口的编程思想,从而降低软件系统模块之间的耦合度,提高系统的可扩展性和可维护性。

在 Java 语言中,提供了 interface 关键字,用于声明接口,其语法格式如下:

[public]interface 接口名[extends 接口1,接口2…] {

[public][static][final]数据类型 常量名 = 值;

[public][abstract] 返回值的数据类型 方法名(参数列表);

默认方法…

}

在上述语法中,当 interface 关键字前加上 public 修饰符时,接口可以被任何类的成员访问。如果省略 public,则接口只能被与它处在同一包中的成员访问。extends 语句与类继承中的 extends 语句基本相同,不同点在于接口可以继承自多个父接口,父接口之间使用逗号分隔。

接口中定义的变量和方法都包含默认的修饰符,其中变量默认声明为“public static final”,即全局静态常量,方法默认声明为“public abstract”,即抽象方法。

例如,定义一个 Charge 接口(收费接口),内有接口常量 PORT_STYLE 和成员方法 getCharge(),代码如下:

interface Charge(){

int PORT_STYLE = 1; // 接口常量

void getCharge(); // 接口方法声明

}

Java接口实现

接口与抽象类相似,也包含抽象方法,因此不能直接实例化接口,即不能使用 new 创建接口的实例。但是,可以利用接口的特性来创造一个新的类,然后再用新类来创建对象,利用接口创建新类的过程称为接口的实现。

实现接口的目的主要是在新类中重写抽象的方法,当然也可以使用抽象类来实现抽象方法的重写。

接口的实现需要使用 implements 关键字,即在声明一个类的同时用关键字 implements 来实现接口,实现接口的类一般称为接口的实现类,具体语法如下:

[修饰符]class 类名 implements 接口1,接口2, 接口3,… { // 如果实现多个接口,以逗号隔开

}

一个类实现一个接口时,需要注意如下问题:

如果实现接口的类不是抽象类,则该类必须实现接口的所有抽象方法;

在类中实现接口的抽象方法时,必须使用与接口中完全一致的方法名及参数列表,否则只是定义一个新的方法,而不是实现已有的抽象方法。

接下来,通过案例来演示接口的实现:

interface PCI { // 定义PCI接口

String serialNumber = "9CC0AC186027";

void start();

void run();

void stop();

}

public class VideoCard implements PCI { // 定义显卡类,实现PCI接口

@Override

public void start() {

System.out.println("显卡开始启动");

}

@Override

public void run() {

System.out.println("显卡序列号是:" + serialNumber);

System.out.println("显卡开始工作");

}

@Override

public void stop() {

System.out.println("显卡停止工作");

}

}

public class Demo {

public static void main(String[] args) {

VideoCard videoCard = new VideoCard();

videoCard.start();

videoCard.run();

videoCard.stop();

}

}

程序的运行结果为:

显卡开始启动

显卡序列号是:9CC0AC186027

显卡开始工作

显卡停止工作

从运行结果可以看到,程序中定义了一个 PCI 接口,该接口定义了一个全局常量 serialNumber 和 3 个抽象方法 start()、run()、stop(),显卡类 VideoCard 实现了 PCI 接口的这 3 个抽象方法,并在实现类的方法中调用接口的常量。最后,在 Demo 测试类中创建了显卡类 VideoCard 的实例,并输出运行结果。

Java接口的继承

在现实世界中,网络通信具有一定的标准,手机只有遵守相应的标准规范才可以使用相应的网络。然而,随着移动互联网技术的发展,网络通信标准从之前的 2G、3G 到目前的 4G、5G,而且 6G 也已在研发之中。

Java 程序中的接口与网络通信标准类似,定义之后,随着技术的不断发展以及应用需求的不断增加,接口往往也需要更新迭代,主要是功能扩展,增加新的方法,以适应新的需求。但是,使用直接在原接口中增加方法的途径来扩展接口可能会带来问题:所有实现原接口的实现类都将因为原来接口的改变而不能正常工作。

为了既能扩展接口,又不影响原接口的实现类,一种可行的方法是通过创建原接口的子接口来增加新的方法。

接口的继承与类的继承相似,都是使用 extends 关键字来实现,当一个接口继承父接口时,该接口会获得父接口中定义的所有抽象方法和常量。但是,接口的继承比类的继承要灵活,一个接口可以继承多个父接口,这样可以通过继承将多个接口合并为一个接口。

接口继承的语法格式如下:

interface 接口名 extends 接口1,接口2,接口3,… {

}

接下来,通过案例来演示接口的继承:

interface I3G { // 上网

void online(); // 上网

void call(); // 打电话

void sendMsg(); // 发短信

}

interface I4G extends I3G { // 看视频

void watchVideo();

}

class Nokia implements I3G {

@Override

public void call() {

System.out.println("打电话功能");

}

@Override

public void sendMsg() {

System.out.println("发短信功能");

}

@Override

public void online() {

System.out.println("上网功能");

}

}

class Mi implements I4G {

@Override

public void call() {

System.out.println("打电话功能");

}

@Override

public void sendMsg() {

System.out.println("发短信功能");

}

@Override

public void online() {

System.out.println("上网功能");

}

@Override

public void watchVideo() {

System.out.println("看视频功能");

}

}

public class Demo {

public static void main(String[] args) {

System.out.println("Nokia手机使用第3代通信技术,具有:");

Nokia nokia = new Nokia();

nokia.call();

nokia.online();

nokia.sendMsg();

System.out.println("小米手机使用第4代通信技术,具有:");

Mi mi = new Mi();

mi.call();

mi.online();

mi.sendMsg();

mi.watchVideo();

}

}

程序的运行结果如下:

Nokia手机使用第3代通信技术,具有:

打电话功能

上网功能

发短信功能

小米手机使用第4代通信技术,具有:

打电话功能

上网功能

发短信功能

看视频功能

从运行结果可以看到,I4G 接口继承了 I3G 接口,直接继承了 I3G 接口中的 3 个抽象方法 call()、onLine()、sendMsg(),并新增了一个抽象方法 watchVide(),在 main() 方法中 Nokia 类实现了 I3G 接口,从而实现父接口的 3 个抽象方法,而 Mi 类实现了 I4G 接口,实现了子接口的 4 个抽象方法。

如果一个类同时继承类并继承某个接口,需要先 extends 父类,再 implements 接口,格式如下:

子类 extends 父类 implements [接口列表]{

}

利用接口实现多重继承

Java 语言规定一个类只能继承一个父类,这给实际开发带来了许多困扰,因为许多类需要继承多个父类的成员才能满足需求,这种问题称为多重继承问题。

然而,我们也不能将多个父类简单地合并成一个父类,因为每个父类都有自己的一套代码,合并到一起之后可能会出现同一方法的多种不同实现,由此会产生代码冲突,增加代码的不可靠性。

有了接口以后多重继承问题就迎刃而解了,由于一个类可以实现多个接口,所以在程序设计的过程中我们可以把一些“特殊类”设计成接口,进而通过接口间接地解决多重继承问题。

一个类实现多个接口时,在 implements 语句中分隔各个接口名,此时这些接口就可以被理解成特殊的类,而这种做法实际上就是使子类获得了多个父类的成员,并且由于接口成员没有实现细节,实现接口的类只能有一个具体的实现细节,从而避免了代码冲突,保证了 Java 代码的安全性和可靠性。

接下来,通过案例来演示利用接口实现多重继承:

// 定义IFly接口

interface IFly {

void takeoff(); // 起飞方法

void land(); // 落地方法

void fly(); // 飞行方法

}

// 定义ISail接口

interface ISail {

void dock(); // 停靠方法

void cruise(); // 航行方法

}

// 定义交通工具Vehicle类

class Vehicle {

private double speed; // 设置速度方法

void setSpeed(int sd) {

this.speed = sd;

System.out.println("设置速度为" + speed);

}

void speedUp(int num) { // 加速方法

this.speed += num;

System.out.println("加速" + num + ",速度变为" + speed);

}

void speedDown(int num) { // 减速方法

this.speed -= num;

System.out.println("减速" + num + ",速度变为" + speed);

}

}

// 定义水上飞机类

class SeaPlane extends Vehicle implements IFly, ISail {

public void takeoff() {

System.out.println("水上飞机开始起飞");

}

public void land() {

System.out.println("水上飞机开始落地");

}

public void fly() {

System.out.println("水上飞机可以飞行");

}

public void dock() {

System.out.println("水上飞机可以停靠");

}

public void cruise() {

System.out.println("水上飞机可以航行");

}

}

public class Demo {

public static void main(String[] args) {

SeaPlane sp = new SeaPlane();

sp.takeoff();

sp.setSpeed(1);

sp.speedUp(2);

sp.fly();

sp.speedDown(2);

sp.land();

sp.cruise();

sp.speedDown(2);

sp.dock();

}

}

程序的运行结果如下:

水上飞机开始起飞

设置速度为2

加速2,速度变为4

水上飞机可以飞行

减速2,速度变为2

水上飞机开始落地

水上飞机可以航行

减速2,速度变为0

水上飞机可以停靠

程序中,水上飞机类 SeaPlane 继承了交通工具类 Vehicle,并且实现了 IFly 接口和 ISail 接口。从程序运行结果中可以看到,它不仅具有了交通工具的功能,还增加了飞行功能和航行功能。

Java接口默认方法

在程序开发中,如果之前创建了一个接口,并且已经被大量的类实现,当需要再添加新的方法以扩充这个接口的功能的时候,就会导致所有已经实现的子类都要重写这个方法。

但是,在接口中使用默认方法就不会有这个问题,所以从 JDK 8 开始新加了接口默认方法,便于接口的扩展。

接口默认方法是一个默认实现的方法,并且不强制实现类重写此方法,使用default关键字来修饰。接口新增的默认方法在实现类中可以直接使用。

接下来,通过案例来演示接口默认方法的使用:

public interface ICat { // 定义ICat接口

void play(); // 抽象方法

default void run() { // 默认方法

System.out.println("猫咪在跑,猫步");

}

}

class BlackCat implements ICat { // 黑猫类实现了ICat接口

@Override

public void play() { // 重写ICat接口的抽象方法

System.out.println("黑猫在玩耍");

}

}

public class Demo {

public static void main(String[] args) {

BlackCat cat = new BlackCat();

cat.play();

cat.run();

}

}

程序的运行结果如下:

黑猫在玩耍

猫咪在跑,猫步

程序中,ICat 接口定义了抽象方法 play() 和默认方法 run(),BlackCat 类实现了 ICat 接口,并重写了抽象方法 play(),通过测试类 Demo 中的 main() 方法创建了 BlackCat 类的实例,调用 play() 和 run() 方法后发现,ICat 接口的默认方法 run() 可以被它的实现类的对象直接调用。

注意,接口允许定义多个默认方法,其子类可以实现多个接口,因此接口默认方法可能会出现同名情况,此时子类在实现或者调用默认方法时通常遵循以下原则:

子类中的同名方法优先级最高。

如果第一条无法进行判断,那么子接口的优先级更高;方法名相同时,优先选择拥有最具体实现的默认方法的接口,即如果接口 B 继承了接口 A,那么接口 B 就比接口 A 更加具体。

Java接口实现多态

前面我们讲解了使用继承机制来实现多态,事实上使用接口也同样可以实现多态。

接下来,通过某汽车销售店案例演示如何通过接口实现多态:

// 定义ICar接口

interface ICar {

String showName(); // 显示汽车名称

double getPrice(); // 获取汽车价格

}

// 定义Haval汽车类

class Haval implements ICar {

@Override

public String showName() {

return "哈佛SUV";

}

@Override

public double getPrice() {

return 150000;

}

}

// 定义GreatWall汽车类

class GreatWall implements ICar {

@Override

public String showName() {

return "长城汽车";

}

@Override

public double getPrice() {

return 68000;

}

}

// 定义汽车销售店CarShop类

class CarShop {

private double money = 0;

public void sellCar(ICar car) {

System.out.println("车型:" + car.showName() + ",价格:" + car.getPrice());

money += car.getPrice();

}

public double getMoney() {

return money;

}

}

// 测试类

public class Demo {

public static void main(String[] args) {

CarShop shop = new CarShop();

Haval haval = new Haval();

shop.sellCar(haval);

GreatWall greatWall = new GreatWall();

shop.sellCar(greatWall);

System.out.println("销售总收入:" + shop.getMoney());

}

}

程序的运行结果如下:

车型:哈佛SUV,价格:150000.0

车型:长城汽车,价格:68000.0

销售总收入:218000.0

程序中 ICar 接口定义了抽象方法 showName() 和 getPrice(),Haval 类和 GreatWall 类实现了 ICar 接口,汽车销售店类 CarShop 针对实现 ICar 接口的实现类进行销售并汇总金额。

在测试类 Demo 的 main() 方法中创建 CarShop 类的实例 shop、Haval 类的实例 haval、GreatWall 类的实例 greatWall,通过 shop 对象的 sellCar() 方法对汽车对象 havel 和 greatWall 销售,并调用 getMoney() 方法统计销售总收入。这样,我们便通过 ICar 接口实现了多态,

抽象类和接口的比较

抽象类与接口是 Java 中对于抽象类定义进行支持的两种机制,它们都用于为对象定义共同的行为,二者比较如下:

抽象类和接口都包含抽象方法;

抽象类可以有非抽象方法,接口中如果要定义非抽象方法,需要标注为接口默认方法;

接口中只能有常量,不能有变量;抽象类中既可以有常量,也可以有变量;

一个类可以实现多个接口,但只能继承一个抽象类。

在程序设计时,应该根据具体业务需求来确定是使用抽象类还是接口。如果子类需要从父类继承一些变量或继承一些抽象方法、非抽象方法,可以考虑使用抽象类;如果一个类不需要继承,只需要实现某些重要的抽象方法,可以考虑使用接口。

Copyright © 2022 历届世界杯_世界杯篮球 - cnfznx.com All Rights Reserved.