本篇文章的主要内容关于Java的接口。

在《Java核心技术卷》中,这部分的内容稍显混乱,笔者第一次阅读时感觉不知所云,想了半天应该怎么写这篇文章。

为了条例更加清晰,这里从以下几个方面来阐述接口 :

  • 接口基本概念
  • 接口示例
  • 接口的应用场景(与抽象类的差别)

接口基本概念

接口的基本使用

使用接口,要先声明,再实现。

  • 声明接口

    1
    2
    3
    4
    5
    6
    //声明一个Comparable接口,但没有实现
    public interface Comparable<T>
    {
    //接口中的方法自动声明为public
    int compareTo(T other) ; // parameter has type T
    }
  • 实现接口

    实现接口通常有两个步骤 :

    1. 将类声明为实现给定的接口(使用关键字 implements:)

      1
      class Employee implements Comparable
    2. 对接口中的所有方法进行定义。

      1
      2
      3
      4
      5
      6
      //注意 ,实现接口的时候要声明public (和声明接口不同)
      public int compareTo(Object otherObject)
      {
      Employee other = (Employee) otherObject;
      return Double ,compare(sal ary, other,sal ary);
      }

接口是什么?

接口是

  • 对类的一组需求描述。

  • 一系列方法的声明

  • 是一些方法特征的集合

  • 一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

接口不是

  • 类。(类是使用class关键字定义的,接口是使用interface定义的)
  • 抽象类 。(接口不是类,自然也不是抽象类。这里单独提出来的原因是,可以将接口看成没有实例域的抽象类)

接口特性

  1. 接口不是类,尤其不能使用 new 运算符实例化一个接口
  2. 接口可以被扩展
  3. 接口中不能包含实例域或静态方法,但却可以包含常量。

接口示例

这里是一个使用定时器接口的示例。

在 java.swing 包中有一个 Timer 类,可以使用它在到达给定的时间间隔时发出通告,即可以使用Timer类构造定时器。

构造定时器时,我们通常需要一些参数,比如在A秒之后做B事,这个A、B就需要传给定时器。

下面是定时器的接口:

1
2
3
4
5
public interface ActionListener
{
//ActionEvent 参数 : 提供了事件的相关信息
void actionPerfonned(ActionEvent event);
}

在 Java 标准类库中的类采用的是面向对象方法,所以将参数传给定时器的方法是将某个类的对象传递给定时器,然后,定时器调用这个对象的方法。

(面向过程的话,就是提供一个函数名, 定时器周期性地调用它。)

因此,定时器需要知道调用对象的哪个方法,并且对象所属的类实现了java.awt.event 包的 ActionListener 接口。

当到达指定的时间间隔时,定时器就调用 actionPerformed 方法。

假设希望每隔 10 秒钟打印一条信息“ 11111111111”, 然后响一声。需要做到事情是

  1. 应该定义一个实现类
  2. 上面定义的类要实现ActionListener 接口
  3. 然后将需要执行的语句放在 actionPerformed方法中。
  4. 构造这个类的一个对象, 并将它传递给 Timer 构造器。
  5. 启动定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package timer;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
// to resolve conflict with java.util.Timer

//测试
public class TimerTest
{
public static void main(String[] args)
{
//4.构造这个类的一个对象
ActionListener listener = new TimePrinter();
//4.并将它传递给 Timer 构造器,这里是每隔10秒调用
//定时器构造函数 :Timer(int interval , ActionListener listener),第一个参数是发出通告的时间间隔,第二个参数是监听器对象。
Timer t = new Timer(10000, listener);
//5.启动定时器:
t.start();
//程序启动后, 将会立即显示一个包含“ Quit program?”字样的对话框, 10 秒钟之后, 第 1 条定时器消息才会显示出来。
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
//1.应该定义一个实现 ActionListener 接口的**类**
//2.实现 ActionListener 接口的**类**
class TimePrinter implements ActionListener
{
//3.然后将需要执行的语句放在 **actionPerformed方法**中。
//ActionEvent 参数 : 提供了事件的相关信息
public void actionPerformed(ActionEvent event)
{
//打印然后响一声
System.out.println("11111111111");
Toolkit.getDefaultToolkit().beep();
}
}

接口的应用场景

前面提到,接口可以看成没有实例域的抽象类。那为什么要有接口?如何选择使用接口或抽象类?

因此抽象类和接口的主要区别汇总如下 :

Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以,这大概就是Java抽象类的优点吧。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点

但是,一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。这是Java抽象类的缺点

在这一点上,Java接口的优点就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。

1
class Employee extends Person implements Comparable,Runnable // OK

使用场景

接口可以用来定义契约行为,也可以充当两个系统之间的契约来进行交互

而抽象类主要用于定义子类的默认行为,这意味着所有子类都应该执行相同的功能。

  • 抽象类

    • 定义某个领域的固有属性,也就是本质
    • 当需要为一些类提供公共的实现代码时,应优先考虑抽象类 。因为抽象类中的非抽象方法可以被子类继承下来,使实现功能的代码更简单。
    • 如果基本功能在不断变化,使用抽象类(否则每个实现接口的类中都要做出一些更改)
    • is-a的关系 ,例如学生和人,巴士和交通工具
  • 接口

    • 定义某个领域的扩展功能。

    • 想实现多继承时,可以用接口解决。

    • like-a的关系,A like a B,那么B就是A的接口。 即例如手机属于通讯工具,手机is通讯工具。但是随着技术进步,手机居然能玩游戏了,这显然不是通讯工具有的功能。另外也有手表、汽车等都能新增了玩游戏的功能了,那就可以做一个“玩游戏”接口。手机like玩游戏、手表like玩游戏、汽车like玩游戏。