什么是接口,Java接口详解
接口是 Java 程序开发中很重要的一种思想,准确地讲不仅仅是 Java 编程,对于其他高级编程语言来说接口也是非常重要的,在实际开发中使用非常广泛。
接口是由抽象类衍生出来的一个概念,并由此产生了一种编程方式:面向接口编程。我们已经掌握了面向对象编程思想,那什么是面向接口编程呢?
面向接口编程不是一种思想,更准确地讲它应该是一种编程方式。
面向接口编程就是将程序的业务逻辑进行分离,以接口的形式去对接不同的业务模块。接口只串联不实现,真正的业务逻辑实现交给接口的实现类来完成。当用户需求变更的时候,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响。
上面这段解释不是很容易理解,我们通过一个现实生活中的例子来类比面向接口编程。
我们都知道计算机可以通过 USB 插口来连接外部设备,例如鼠标、U盘、散热架等。假如没有 USB 插口这种配置,那么外部设备就是固定在计算机上的,不能更换。例如一个鼠标固定在计算机上,但我现在希望换成 U 盘,怎么办呢?我们可能就需要把计算机拆了,重新组装计算机和 U 盘的内部连接。同理,每一次希望更换外置设备的时候,都需要把计算机拆了,移除之前的结构并重新组装,这种方式很显然是不可取的。维护成本太高,效率太低,用专业的语言来描述,叫作耦合性太高,模块和模块结合得太紧密,不灵活,要更换就必须重新构建内部组成来替换原有的结构。
但是有了 USB 插口之后,上述问题就迎刃而解了。通过 USB 插口连接到电脑上的外部设备是非常灵活的,即插即用,需要更换就拔掉旧的,把新的设备插入即可,这就是接口的思想。在设计制作计算机的时候,不需要考虑到底是跟鼠标、U 盘还是散热架连接,只需要把这个对接的部分提取出来,设计成一个接口。计算机内部只需要跟这个接口进行连接即可,你插入鼠标,计算机就识别鼠标,插入 U 盘就识别 U 盘。USB 插口就相当于接口,鼠标、U 盘、散热架就相当于接口的实现类,这样就很好理解了。
使用 USB 插口的方式可以很好地实现计算机与外部设备之间的各种切换,这是因为它们的连接是松散的、不紧密的,用专业的语言来讲就叫作低耦合,模块和模块之间连接松散,自然很容易切换。这里我们又引出来耦合性这个概念,很显然在实际开发中应该尽量降低程序的耦合性,以提高程序的扩展性,便于维护。
面向接口编程就具备以下优点:
了解完接口的概念,以及我们为什么要使用接口,接下来就是如何使用接口了。
接口其实就是一个极度抽象的抽象类。为什么这么说呢?抽象类的概念我们知道,一个类中一旦存在没有具体实现的抽象方法,那么该类就必须定义为抽象类,同时抽象类中是允许存在非抽象方法的。但是接口完全不同,接口中不能存在非抽象方法,必须全部是抽象方法,所以我们说接口是极度抽象的抽象类。
因为接口中全部是抽象方法,所以修饰抽象方法的 abstract 可以省略,不需要添加在方法定义处。当然,在定义方法时添加 abstract,程序也不会报错。
我们知道了接口中的方法必须全部是抽象的,那么接口中可以存在成员变量吗?答案是肯定的,接口中可以定义成员变量,但是有如下要求:
关于接口的创建,参考如下的代码:
我们需要实例化的是接口的实现类。实现类就是对接口的抽象方法进行具体实现的,实现类本身就是一个普通的 Java 类,创建实现类的基本语法如下:
接口实际上描述的是功能,让某个类实现接口,就是让该类具备某些功能。类比现实生活中的例子,一个孩子只能有一个亲爹,所以是单继承,但是一个孩子可以同时具备多个技能,所以是多实现。例如:
某工厂生产成品 A,主要由设备 A 来完成生产,用程序模拟这一过程。分别创建 Factory 类和 EquipmentA 类,并将 EquipmentA 设置为 Factory 的成员变量,在 Factory 的业务方法中调用 EquipmentA 的方法来完成生产,具体实现代码如下:
1) 定义 EquipmentA 类:
2) 定义 Factory 类:
3) Test 类中的工厂开始生产成品:
现在工厂接了一份新订单,要求生产成品 B,需要设备 B 来完成生产,用程序实现这一过程,首先需要创建 EquipmentB 类,同时修改 Factory 内部的属性,代码如下:
1) 定义 EquipmentB 类:
2) 修改 Factory 类,将成员变量的数据类型改为 EquipmentB:
3) 在 Test 类中完成生产:
将 Equipment 以接口的形式集成到 Factory 类中,具体实现如下:
1) 定义 Equipment 接口:
2) 定义 Equipment 接口的实现类 EquipmentA 和 EquipmentB:
3) 修改 Factory 类,将 Equipment 接口设置为成员变量:
4) Test 类中的工厂生产成品 A:
如果此时需要生产成品 B,修改起来就方便多了。因为嵌入到 Factory 类中的是接口,所以 Factory 类不需要修改,只需要将 EquipmentB 组件以接口的形式赋给 Factory 实例对象即可,Test 类中修改一处代码,代码如下:
接口是由抽象类衍生出来的一个概念,并由此产生了一种编程方式:面向接口编程。我们已经掌握了面向对象编程思想,那什么是面向接口编程呢?
面向接口编程不是一种思想,更准确地讲它应该是一种编程方式。
面向接口编程就是将程序的业务逻辑进行分离,以接口的形式去对接不同的业务模块。接口只串联不实现,真正的业务逻辑实现交给接口的实现类来完成。当用户需求变更的时候,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响。
上面这段解释不是很容易理解,我们通过一个现实生活中的例子来类比面向接口编程。
我们都知道计算机可以通过 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
将 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