什么是接口,Java接口详解

什么是接口,Java接口详解

接口是 Java 程序开发中很重要的一种思想,准确地讲不仅仅是 Java 编程,对于其他高级编程语言来说接口也是非常重要的,在实际开发中使用非常广泛。

接口是由抽象类衍生出来的一个概念,并由此产生了一种编程方式:面向接口编程。我们已经掌握了面向对象编程思想,那什么是面向接口编程呢?

面向接口编程不是一种思想,更准确地讲它应该是一种编程方式。

面向接口编程就是将程序的业务逻辑进行分离,以接口的形式去对接不同的业务模块。接口只串联不实现,真正的业务逻辑实现交给接口的实现类来完成。当用户需求变更的时候,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响。

上面这段解释不是很容易理解,我们通过一个现实生活中的例子来类比面向接口编程。

我们都知道计算机可以通过 USB 插口来连接外部设备,例如鼠标、U盘、散热架等。假如没有 USB 插口这种配置,那么外部设备就是固定在计算机上的,不能更换。例如一个鼠标固定在计算机上,但我现在希望换成 U 盘,怎么办呢?我们可能就需要把计算机拆了,重新组装计算机和 U 盘的内部连接。同理,每一次希望更换外置设备的时候,都需要把计算机拆了,移除之前的结构并重新组装,这种方式很显然是不可取的。维护成本太高,效率太低,用专业的语言来描述,叫作耦合性太高,模块和模块结合得太紧密,不灵活,要更换就必须重新构建内部组成来替换原有的结构。

但是有了 USB 插口之后,上述问题就迎刃而解了。通过 USB 插口连接到电脑上的外部设备是非常灵活的,即插即用,需要更换就拔掉旧的,把新的设备插入即可,这就是接口的思想。在设计制作计算机的时候,不需要考虑到底是跟鼠标、U 盘还是散热架连接,只需要把这个对接的部分提取出来,设计成一个接口。计算机内部只需要跟这个接口进行连接即可,你插入鼠标,计算机就识别鼠标,插入 U 盘就识别 U 盘。USB 插口就相当于接口,鼠标、U 盘、散热架就相当于接口的实现类,这样就很好理解了。

使用 USB 插口的方式可以很好地实现计算机与外部设备之间的各种切换,这是因为它们的连接是松散的、不紧密的,用专业的语言来讲就叫作低耦合,模块和模块之间连接松散,自然很容易切换。这里我们又引出来耦合性这个概念,很显然在实际开发中应该尽量降低程序的耦合性,以提高程序的扩展性,便于维护。

面向接口编程就具备以下优点:

能够最大限度地解耦,降低程序的耦合性;

使程序易于扩展;

有利于程序的后期维护。

了解完接口的概念,以及我们为什么要使用接口,接下来就是如何使用接口了。

如何使用接口

接口在 Java 中是独立存在的一种结构,和类相似,我们需要创建一个接口文件,Java 中用 class 关键字来标识类,用 interface 来标识接口,基本语法如下:

public interface 接口名{

public 返回值 方法名(参数列表);

}

看到定义接口的基本语法,你有没有一种很熟悉的感觉?没错,接口与抽象类非常相似,同样是定义了没有实现的方法,只是一个抽象的概念,没有具体实现。

接口其实就是一个极度抽象的抽象类。为什么这么说呢?抽象类的概念我们知道,一个类中一旦存在没有具体实现的抽象方法,那么该类就必须定义为抽象类,同时抽象类中是允许存在非抽象方法的。但是接口完全不同,接口中不能存在非抽象方法,必须全部是抽象方法,所以我们说接口是极度抽象的抽象类。

因为接口中全部是抽象方法,所以修饰抽象方法的 abstract 可以省略,不需要添加在方法定义处。当然,在定义方法时添加 abstract,程序也不会报错。

我们知道了接口中的方法必须全部是抽象的,那么接口中可以存在成员变量吗?答案是肯定的,接口中可以定义成员变量,但是有如下要求:

不能定义 private 和 protected 修饰的成员变量,只能定义 public 和默认访问权限修饰的成员变量。

接口中的成员变量在定义时必须被初始化。

接口中的成员变量都是静态常量,即可以直接通过接口访问,同时值不能被修改。

关于接口的创建,参考如下的代码:

public interface MyInterface {

public int ID = 0;

String NAME = "";

public void test();

}

接口定义完成,接下来如何使用呢?对于类的使用我们已经很熟悉了,首先需要实例化一个类的对象,然后通过对对象的操作来完成功能。但是接口的使用就大不一样了,因为接口是不能被实例化的,它描述的是一个抽象的信息,抽象的信息当然是没有实例的。

我们需要实例化的是接口的实现类。实现类就是对接口的抽象方法进行具体实现的,实现类本身就是一个普通的 Java 类,创建实现类的基本语法如下:

public class 实现类名 implements 接口名{

public 返回值 方法名(参数列表){

}

}

通过关键字 implements 来指定实现类具体要实现的接口,在实现类的内部需要对接口的所有抽象方法进行实现,同时要求访问权限修饰符、返回值类型、方法名和参数列表必须完全一致,例如:

public class MyImplements implements MyInterface{

@Override

public void test() {

// TODO Auto-generated method stub

System.out.println("实现了接口的抽象方法");

}

}

在这里接口与继承也有一个可以对比的地方,继承只能实现单继承,即一个子类只能继承一个父类。接口可以多实现,即一个实现类可以同时实现多个接口。如何去理解这句话呢?

接口实际上描述的是功能,让某个类实现接口,就是让该类具备某些功能。类比现实生活中的例子,一个孩子只能有一个亲爹,所以是单继承,但是一个孩子可以同时具备多个技能,所以是多实现。例如:

public interface MyInterface {

public void fly();

}

public interface MyInterface2 {

public void run();

}

public class MyImplements implements MyInterface,MyInterface2{

@Override

public void fly() {

// TODO Auto-generated method stub

System.out.println("实现了fly的功能");

}

@Override

public void run() {

// TODO Auto-generated method stub

System.out.println("实现了run的功能");

}

}

public class Test {

public static void main(String[] args) {

MyImplements myImplements = new MyImplements();

myImplements.fly();

myImplements.run();

}

}

运行结果为:

实现了fly的功能

实现了run的功能

面向接口编程的实际应用

面向接口编程是一种常用的编程方式,可以有效地提高代码的复用性,增强程序的扩展性和维护性,我们通过下面这个例子来学习什么是面向接口编程。

某工厂生产成品 A,主要由设备 A 来完成生产,用程序模拟这一过程。分别创建 Factory 类和 EquipmentA 类,并将 EquipmentA 设置为 Factory 的成员变量,在 Factory 的业务方法中调用 EquipmentA 的方法来完成生产,具体实现代码如下:

1) 定义 EquipmentA 类:

public class EquipmentA {

public void work() {

System.out.println("设备A运行,生产成品A");

}

}

2) 定义 Factory 类:

public class Factory {

private EquipmentA equipmentA;

//getter、setter方法

public void work() {

System.out.println("开始生产...");

this.equipmentA.work();

}

}

3) Test 类中的工厂开始生产成品:

public class Test {

public static void main(String[] args) {

EquipmentA equipmentA = new EquipmentA();

Factory factory = new Factory();

factory.setEquipmentA(equipmentA);

factory.work();

}

}

运行结果为:

开始生产...

设备A运行,生产成品A

现在工厂接了一份新订单,要求生产成品 B,需要设备 B 来完成生产,用程序实现这一过程,首先需要创建 EquipmentB 类,同时修改 Factory 内部的属性,代码如下:

1) 定义 EquipmentB 类:

public class EquipmentB {

public void work() {

System.out.println("设备B运行,生产成品B");

}

}

2) 修改 Factory 类,将成员变量的数据类型改为 EquipmentB:

public class Factory {

private EquipmentB equipmentB;

//getter、setter方法

public void work() {

System.out.println("开始生产...");

this.equipmentB.work();

}

}

3) 在 Test 类中完成生产:

public class Test {

public static void main(String[] args) {

EquipmentB equipmentB = new EquipmentB();

Factory factory = new Factory();

factory.setEquipmentB(equipmentB);

factory.work();

}

}

运行结果为:

开始生产...

设备B运行,生产成品B

这种方式需要修改 Factory 类的内部结构,如果此时需求又改回到生产成品 A 或者生产成品 C,就需要创建新的类,同时再次修改 Factory 类的属性信息,这种当需求发生变更就要频繁修改类结构的方式是我们应该避免的。这种结构的程序扩展性非常差,如何改进呢?使用面向接口编程即可。

将 Equipment 以接口的形式集成到 Factory 类中,具体实现如下:

1) 定义 Equipment 接口:

public interface Equipment {

public void work();

}

2) 定义 Equipment 接口的实现类 EquipmentA 和 EquipmentB:

public class EquipmentA implements Equipment{

@Override

public void work() {

// TODO Auto-generated method stub

System.out.println("设备A运行,生产成品A");

}

}

public class EquipmentB implements Equipment{

@Override

public void work() {

// TODO Auto-generated method stub

System.out.println("设备B运行,生产成品B");

}

}

3) 修改 Factory 类,将 Equipment 接口设置为成员变量:

public class Factory {

private Equipment equipment;

//getter、setter方法

public void work() {

System.out.println("开始生产...");

this.equipment.work();

}

}

4) Test 类中的工厂生产成品 A:

public class Test {

public static void main(String[] args) {

Equipment equipment = new EquipmentA();

Factory factory = new Factory();

factory.setEquipment(equipment);

factory.work();

}

}

运行结果为:

开始生产...

设备A运行,生产成品A

如果此时需要生产成品 B,修改起来就方便多了。因为嵌入到 Factory 类中的是接口,所以 Factory 类不需要修改,只需要将 EquipmentB 组件以接口的形式赋给 Factory 实例对象即可,Test 类中修改一处代码,代码如下:

public class Test {

public static void main(String[] args) {

Equipment equipment = new EquipmentB();

Factory factory = new Factory();

factory.setEquipment(equipment);

factory.work();

}

}

运行结果为:

开始生产...

设备B运行,生产成品B

相关推荐

域名注册成功后怎么保存文件
365ba

域名注册成功后怎么保存文件

📅 07-27 👁️ 8613
《剑灵》御龙林主线任务-冲角团平南舰队支部
beat365官方app安卓版下载

《剑灵》御龙林主线任务-冲角团平南舰队支部

📅 07-31 👁️ 7365
含有“蛇”的成语150个
365bet品牌中文网

含有“蛇”的成语150个

📅 07-23 👁️ 3548