- Java程序设计与应用开发(第3版)
- 吴敏 於东军 李千目主编 成维莉 邵杰 姜小花副主编
- 2134字
- 2025-03-22 04:39:51
4.1 接 口
在Java语言中,接口是一个特殊的语法结构,其中可以包含一组方法声明(没有实现的方法)和一些常量。接口和类构成Java的两个基本类型,但接口和类有着不同的目的,它可用于在树形层次结构上毫不相关的类之间进行交互。一个Java类可以实现多个Java接口,这也弥补了Java类不支持多重继承带来的弱点。
4.1.1 接口定义
Java接口的定义方式与类基本相同,不过接口定义使用的关键字是interface,其格式如下:
public interface InterfaceName extends I1,...,Ik //接口声明 {//接口体,其中可以包含方法声明和常量 ... }
接口定义由接口声明和接口体两部分组成。具有public访问控制符的接口,允许任何类使用;没有指定为public的接口,其访问将局限于所属的包。
在接口定义中,InterfaceName指定接口名。
在接口定义中,还可以包含关键词extends,表明接口的继承关系,接口继承没有唯一性限制,一个接口可以继承多个接口。
位于extends关键字后面的I1,…,Ik就是被继承的接口,接口InterfaceName称为I1,…,Ik的子接口(subinterface),I1,…,Ik称为InterfaceName的父接口(superinterface)。
由一对花括号{}括起来的部分是接口体,可以在其中定义抽象方法(abstract methods,参见4.2小节)和常量。在接口内也可以嵌套类和接口的定义,不过这并不多见。
在接口体中,方法声明的常见格式如下:
ReturnType MethodName(Parameter-List);
此方法声明由方法返回值类型(ReturnType)、方法名(MethodName)和方法参数列表(Parameter-List)组成,不需要其他修饰符。在Java接口中声明的方法,将隐式地声明为公有的(public)和抽象的(abstract)。
由于接口没有为其中声明的方法提供实现,在方法声明后会需要一个分号。如果把分号换成一对花括号{},即使花括号{}中没有任何内容,也表示一个方法被实现,只是这是一个没有任何操作的空方法。
在Java接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为public、static和final,即常量,所以接口中定义的变量必须初始化。
interface MyInterface { //变量a声明不合法,a为常量,必须初始化 int a; //下面的变量声明,等同于public static final int b = 200; int b = 200; //下面的方法声明,等同于public abstract void m(); void m(); }
与类不同,一个Java接口可以继承多个父接口,子接口也可以对父接口的方法和变量进行覆盖。例如:
interface A{ int x = 1; void method1(); } interface B{ int x = 2; void method2(); } interface C extends A,B{ int x = 3; void method1(); void method2(); }
在该例中,接口C的常量x覆盖了父接口A和B中的常量x,方法method1()覆盖了父接口A中的方法method1(),方法method2()覆盖了父接口B中的方法method2()。
Java接口和类还有一个重要的区别,那就是在Java接口中不存在构建器。
4.1.2 接口的实现
Java接口中声明了一组抽象方法,它构成了实现该接口的不同类共同遵守的约定。在类定义中可以用关键字implements来指定其实现的接口。一个类实现某个接口,就必须为该接口中的所有方法(包括因继承关系得到的方法)提供实现,它也可以直接引用接口中的常量。
例4.1 Example.java
interface A{ int x = 1; void method1(); } interface B extends A{ int x = 2; void method2(); } public class Example implements B{ public void method1(){ System.out.println("x = " + x); System.out.println("A.x = " + A.x); System.out.println("B.x = " + B.x); System.out.println("Example.x = " + Example.x); } public void method2(){ } public static void main(String[] args){ Example d = new Example(); d.method1(); } }
程序运行结果:
x = 2 A.x = 1 B.x = 2 Example.x = 2
在上面的例子中,类Example实现了接口B,它为接口B中声明的方法method2()提供了实现,虽然method2()的方法体为空。在类Example中,还要实现接口B继承接口A得到的方法method1()。从类Example的方法method1()中,可以引用其实现接口B而继承的变量x,此变量属于类成员,我们也可以通过类名来引用。
注意:Java类只允许单一继承,即一个类只能继承(extends)一个父类;但一个类可以实现多个接口,Java支持接口的多重继承。在Java类定义中,可以同时包括extends子句和implements子句,如果存在extends子句,则implements子句应跟随在extends子句后面。
Java接口常用于在不同对象之间进行通信,接口定义对象之间通信的协议,下面通过一个具体的例子来说明。
例4.2 EventProducer.java
import java.util.Vector; //事件 class SimpleEvent{ } //凡需要处理SimpleEvent事件的监听器,要求实现该接口 interface EventListener{ void processEvent(SimpleEvent e); } //SimpleEvent事件的监听器,实现EventListener接口 class EventConsumer implements EventListener{ public void processEvent(SimpleEvent e){ System.out.println("Receive event: " + e); } } //事件源,产生SimpleEvent事件 public class EventProducer{ //对象容器,存储事件监听器 Vector listeners = new Vector(); //事件监听器向事件源注册自身 public synchronized void registeListener(EventListener listener){ listeners.add(listener); } public void demo() { SimpleEvent e = new SimpleEvent(); for(int i=0; i<listeners.size(); i++) { EventConsumer consumer = (EventConsumer)listeners.elementAt(i); consumer.processEvent(e); } } public static void main(String[] args) { EventProductor productor = new EventProductor(); EventConsumer consumer = new EventConsumer(); productor.registeListener(consumer); productor.demo(); } }
程序运行结果:
Receive event: SimpleEvent@9cab16
4.1.3 接口作为类型
与类一样,Java接口也是一种数据类型,可以在任何使用其他数据类型的地方使用接口名来表示数据类型。我们可以用接口名来声明一个类变量、一个方法的形参或者一个局部变量。
用接口名声明的引用型变量,可以指向实现该接口的任意类的对象,如例4.3。
例4.3 Server.java
class Worker implements Runnable{ public void run(){ System.out.print("Worker run!"); } } public class Server{ public static void main(String[] args){ Runnable w = new Worker(); (new Thread(w)).start(); } }
程序运行结果:
Worker run!
该例中的Runnable是Java语言包中一个非常重要的接口,Worker是实现了Runnable接口的类,在程序中我们创建了Worker对象,并赋给了声明为Runnable类型的变量w。有关本例中使用的接口Runnable和类Thread,可参见本书第11章。
4.1.4 接口不应改变
一个接口声明了方法,但没有实现它们。位于树型结构中任何位置的任何类都可以实现它,实现某个接口的类,要为这个接口中的每个方法提供具体的实现,由此形成某些一致的行为协议。
如果有一天,用户想修改某个接口,为其添加一个方法,这个简单的修改可能会造成牵一发而动全身的局面:所有实现这个接口的类都将无法工作,因为现在它们已经不再实现这个接口了。用户要么放弃对这个接口的修改,要么连带修改所有实现这个接口的所有类。
在设计接口的最初,预测出接口的所有功能,这可能是不太现实的。如果觉得接口非改不行,那么可以创建一个新的接口或者扩展这个接口,算是一种折中的解决方法。其他相关的类可以保持不变,或者重新实现这个新的接口。