漫谈Java程序设计中的接口应用

Java语言提供了一种接口(interface)机制。这种接口机制使Java的面向对象编程变得更加灵活。我们可以用接口来定义一个类的表现形式,但接口不能包含任何实现。在《Thinking in Java》一书中,作者对接口有这样的描述:“接口(interface)比抽象(abstract)的概念更进了一步。你可以把一个接口看成是一个纯的抽象类。”我认为作者对接口的这一解释再准确不过了。

  理解并用好接口机制将帮助我们更好的掌握Java这种面向对象的编程语言。下面我们来讨论一下接口的使用规则以及相关的应用。

  一、接口的定义及实现

   定义接口和定义类相似,只是要把 class关键字换为 interface。定义方法时只需要方法名,返回类型和参数列表,不能有方法体。接口中可以定义字段,这些字段都被暗指为 static 和 final,因此应该根据需要先定好这些字段的值。例如:

public interface Flyable {
void fly();
}

public interface Talkable {
void talk();
}

public interface Message {
int MAX_SIZE = 4096;
String getMessage();
}

  上面定义的几个接口中,Flyable 和 Talkable 只定义了一个方法,而 Message 里除了方法外还有一个字段 MAX_SIZE。可以看出这些接口只定义了类的表现形式,而不包含任何实现,所以不能直接使用。要使用这些接口就需要有相应的类去实现它们。实现接口时应该先在类名后用 implements 关键字申明将要实现的接口,如果要实现多个接口,应该用逗号将它们隔开,然后一一实现这些接口中定义的方法。如下面的例子:

public class Parrot implements Flyable, Talkable {

public void fly() {
System.out.println("Flying like a parrot…");
}

public void talk() {
System.out.println("Hello! I am a parrot!");
}
}

public class TextMessage implements Message {
String message;

public void setMessage(String msg) {
message = msg;
if (message.length() > MAX_SIZE)
message = message.substring(0, MAX_SIZE);
}

public String getMessage() {
return message;
}
}

  在 Parrot(鹦鹉)例子中,我们用接口 Flyable 来表示飞行能力,Talkable 表示说话能力,但它们并不包含具体实现。而 Parrot 同时具有这两种能力,所以我们为 Parrot 类同时实现了 Flyable 和 Talkable 这两个接口。同样我们还可以定义一个Swallow(燕子)类,但燕子只有飞行能力,所以我们只需要为 Swallow 实现 Flyable 就行了。因为它们各自的的飞行方法有所不同,所以它们有各自关于飞行的具体实现。

   另外,正因为一个类可以同时实现多个接口,使得Java的面向对象特性变得非常灵活。运用这种特性,我们可以实现类似C++语言中多继承那样的特性,甚至更灵活的一些特性。下面我们来讨论一下接口在实际中的应用。

  二、用接口来定义一些全局变量

   因为接口内的字段都是static和final的,所以我们可以很方便的利用这一点来创建一些常量。例如:

public interface Constants {
String ROOT = "/root";
int MAX_COUNT = 200;
int MIN_COUNT = 100;
}

  在使用时可以直接用Constants.ROOT这样的形式来引用其中的常量。我们还可以用下面这种方法来创建初始值不确定的常量。

public interface RandomColor {
int red = Math.random() * 255;
int green = Math.random() * 255;
int blue = Math.random() * 255;
}

  其中red、green和blue的值会在第一次被访问时建立,然后保持不变。

  三、用接口来定义基本数据结构

   在设计一套软件系统的初期,我们可以用接口来对一些基本数据元素的特性来进行一些描述,再根据需要进行不同的实现。请大家看看下面这个例子:

public interface User {
int getAge();
String getName();
String getPassword();
}

public class XMLUser implements User {
// 这里用XML技术实现User接口中的方法
public int getAge() { ... }
public String getName() { ... }
public String getPassword() { ... }
}

public abstract class UserFactory {
public static UserFactory getUserFactory() {
return new XMLUserFactory();
}

public User getUser(String name);
public User getAdmin();
public User createUser(String name, String password, int age);
public void addUser(User user);
public void delUser(User user);
}

public class XMLUserFactory extends UserFactory {
// 这里用XML技术实现的UserFactory的抽象方法
}

  在这个例子中,我们定义了一个接口User和一个抽象类UserFactory。然后我们用XML技术实现这两个类。可以看出,我们只需要从用UserFactory的getUserFactory()就可以得到一个UserFactory的实例,而不用去考虑这个实例的具体实现方法。通过UserFactory的这个实例我们还可以直接得到User的实例,也不用去考具体的实现方法。

  如果我们决定用JDBC技术来实现User和UserFactory,我们只需要按上面的形式实现JDBCUser和JDBCUserFactory就行了。然后把UserFactory中的getUserFactory方法修改一下就可以改变了它们的实现方法。而我们已经写好的调用UserFactory和User的部分不需要做任何修改。

  这是用接口来定义数据结构的一个简单的例子,在实际应用中还有很多灵活的使用方法,大家需要在学习过程中不断的去体会。

  四、理解分布式应用的原理

  目前有很多软件项目都使用了分布式的技术。Java 有多种支持分布式应用的技术,早期用的比较多的有 RMI、CORBA 等技术,而现在 EJB 技术更为流行一些。但这些技术不管怎么发展,其实都是以接口为基础的。

  以远程方法调用 RMI(Remote Method Invocation)为例。在编写 RMI 应用时,我们需要做两件最基本的事,首先要定义一个接口,这个接口要继承 java.rmi.Remote 接口,这个接口中应该包含你要从远端调用的方法名。接下来就是写一个类来实现这个接口中的方法。例如:

public interface Product extends java.rmi.Remote {
String getName() throws java.rmi.RemoteException;
}

public class ProductImpl implements Product {
String name;

public ProductImpl(String n) {
name = n;
}

public String getName() throws java.rmi.RemoteException {
return name;
}
}

  在这个例子中,接口 Product 是放在客户端的,而 ProductImpl 是放在服务器端的,客户在使用时只需要用指定的规则得到Product 的实例就行了,不用去考虑 Product 接口里的方法是如何实现的。在定义好这两个类后,用Java开发包命令“rmic ProductImpl”就可以帮助我们自动生成两个类 ProductImpl_Skel 和 ProductImpl_Stub。这两个类就包含了RMI调用的运作机制。有兴趣的朋友可以把这两个类反编译后研究一下。你会发现其中 ProductImpl_Stub 实际上是接口 Product 的一个实现类。RMI 机制就是用这个类来生成 Product 的实例供客户端使用。另一个类 ProductImpl_Skel 则是在服务端响应 ProductImpl_Stub 的调用请求的类。而 RMI 最底层的通讯原理则是利用 ObjectInputStream 和 ObjetOutputStream 通过 Socket 将要调用的方法名及参数列表传到服务器端,服务器端再通过特定的方法调用实现类(在本例中是 ProductImpl)的对应方法,然后将结果通过 Socket 传回客户端就行了。由于 Skel 和 Stub 类是用工具生成的,所以就大大节省了开发的时间。另外,如果我们需要修改一些实现方法或错误,只需要对服务器端的实现类进行修改就可以了,也就是说这种分布式应用的大部分维护工作在服务器端就可以完成。

   现在越来越多的应用使用了 EJB 这种技术。EJB 是从 RMI 发展而来的一项技术,它比RMI定义得更加完善,可以获得更好的面向对象的特性。但它的规则要比RMI复杂一些。但是不管它多复杂,它同样是使用了接口来定义各种不同的 Bean,也同样需要编写相应的实现类来完成具体的功能,最后还要通过 Socket 来进行通讯。EJB的运作机制本身有一定的复杂性,所以其应用的效率理所当然就会受到一定的影响。因此在选择开发技术时应该根据应用的规模和特点仔细考虑,不一定流行的技术就一定能适应你的应用。如果你很好的掌握了面向对象的设计原则,你就可以自行设计。也许你可以根据自己应用的特点设计出更合适的分布式应用结构。

  五、结论

  除了上述的一些应用外,还有很多地方可以使用接口,比如在Java的事件机制中就常用到接口。另外,对于一些已经开发好的系统,在结构上进行较大的调整已经不太现实,这时可以通过定义一些接口并追加相应的实现来完成功能结构的扩展。

  总之,学好接口可以帮助我们更好的理解和运用面向对象的设计原则。使我们能设计出更好的软件系统。由于本人水平的限制,如有错误之处还请多多指正。

时间: 2024-08-07 20:25:37

漫谈Java程序设计中的接口应用的相关文章

java学习中,接口的使用(重要,常用知识点)(java 学习中的小记录)

java学习中,接口的使用(重要,常用知识点)(java 学习中的小记录)作者:王可利(Star·星星) 接口(功能:用来添加拓展功能的) 例子:铅笔.带橡皮檫的铅笔. 两个功能:     1.写字......> 铅笔     2.擦字......> 橡皮擦 想法:定义一个铅笔类,定义一个橡皮擦类,定义一个带橡皮擦的铅笔类继承 铅笔类和橡皮擦类 但是java是单继承的.于是就有了解决的方法:接口(可以添加拓展功能) 如:一个铅笔类,给它添加一个接口给它一个拓展类(橡皮擦类) 接口的定义模式 用

Java 8 中函数接口,陈货翻新了炒,只是为了Lambda表达式

Java开发人员应该对java.lang.Runnable,java.util.Comparator,java.util.concurrent.Callable 等等接口不会感到陌生.他们都只有一个单一的抽象方法.这样的接口,我们通常叫单一抽象方法接口(SAM,Single Abstract Method Interface). 以前大家应该经常使用下面的代码片段 public class InnerAnonymousClassSample { public static void main(S

java类中定义接口

今天看到一个java类中定义了接口,写个备忘录,记录一下 1 package com.gxf.test; 2 3 public class Test_interface { 4 public interface show{ 5 public void show(); 6 } 7 static class TestInterface implements show{ 8 9 @Override 10 public void show() { 11 System.out.println("this

Java集合中Map接口的使用方法

Map接口 Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value: Map中的键值对以Entry类型的对象实例形式存在: 建(key值)不可重复,value值可以重复,一个value值可以和很多key值形成对应关系,每个建最多只能映射到一个值. Map支持泛型,形式如:Map<K,V> Map中使用put(K key,V value)方法添加 HashMap类 HashMap是Map的一个重要实现类,也是最常用的,基于哈希表实现

Java基础中map接口和实现类

1.Map接口常用的实现类有HashMap和TreeMap. 2.HashMap类实现的Map集合对于添加和删除映射关系效率更高.HashMap是基于哈希表的Map接口的实现,HashMap通过哈希码对其内部的映射关系进行快速查询,由HashMap类实现的Map集合对于添加或删除映射关系效率较高. 3.TreeMap中的映射关系存在一定的顺序,如果希望Map集合中的对象存在一定的顺序,该使用TreeMap类实现Map集合. HashMap类 ①此类不保证映射的顺序,特别是不保证该顺序恒久不变 ②

Java Listener中Spring接口注入的使用

在项目中使用Spring通常使用他的依赖注入可以很好的处理,接口与实现类之间的耦合 性,但是通常的应用场景中都是Service层和DAO层,或者web层的话, 也是与Strust2来整合,那么如何在Listener中使用Spring自动注入的接口呢. 接下来开始记录下今天做的一个小工具. 这个小工具是通过这个Listener来开启一个线程, 然后定时访问数据库中的数据,将数据获取出来后,然后逐条分析数据里边的手机号码,来通过淘宝提供的一个接口来分析手机号的归属地.那么在Listener中如何来访

java语言中的接口概述

接口 接口中的成员修饰符是固定的 1,全局常量:public staticfinal 2,抽象方法:public abstract 接口中的成员都是公共的权限. 类与类之间是继承关系,类与接口之间是实现关系. 接口不可以实例化 只能有实现了接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化.否则,这个子类就是一个抽象类. 调用接口的常量时,接口名,接口的子类名,子类的对象都可以进行调用. 在java中不直接支持多继承,因为会出现调用的不确定性. 所以java将多继承机制进行改良,在ja

Java集合中的接口实现与继承

很多人都对接口以及抽象实现类认识混乱 ,注意区分实现接口与继承的区别,图中深色为接口,浅色为实现类,extend为继承,implements为接口实现 List与Set接口继承自Collection接口 LinkedList是继承自AbstractSequentialList(抽象类,实现了List接口) ArrayList与Vector是继承自AbstractList(抽象类,实现了List接口) AbstractSet是实现了Set接口的,本身是一个抽象类.继承自AbstractCollec

【转】Predicate和Consumer接口– Java 8中java.util.function包下的接口

原文链接 http://ifeve.com/predicate-and-consumer-interface-in-java-util-function-package-in-java-8/ 原文链接 作者:   Mohamed Sanaulla  译者: 李璟([email protected]) 早先我写了一篇<函数式接口>,探讨了部分Java 8中函数式接口的用法.我也提及了Predicate接口属于java.util.function包, 在这篇文章中,我将展示如何应用Predicat