Java核心技术点之接口
1. 为什么使用接口
Java中的接口是一组对需求的描述。接口通过声明接口方法来对外宣布:“要想具有XX功能,就得按我说的做(即实现接口方法)。” 而接口的实现类通过实现相应接口的方法来宣布:“我已经按你说的做了,请赐予我XX功能吧!” 例如,以下是Comparable<T>接口的定义:
public interface Comparable<T> {
int compareTo(T o);
}
Comparable<T>接口中定义了一个compareTo方法,这个方法就是它所描述的需求。也就是说Comparable接口对外宣布:“要想拥有进行比较的功能,得按我说的做(实现compareTo方法)。” 若我们想调用Arrays.sort方法对一个People对象数组进行比较,那么People对象必须是”可比较的”,即People类需要实现Comparable<T>接口。接口的实现类需要实现接口中定义的方法。People类通过实现Comparable<T>接口的方法来宣称:”我已经按你说的做啦,请让我变为可比较的吧!“ 也就是说,接口描述了一组需求,而实现一个接口的类就需要实现这个接口所描述的需求。比如我们想要People类对象是可比较的,我们可以这样:
public class People implements Comparable<People> {
...
public int compareTo(People p) {
//定义具体的比较标准
}
...
}
实际上,Arrays.sort方法之所以要求它所比较的对象需实现了Comparable接口,是因为它在比较对象时调用的对象的compareTo方法(因为它不知道评价一个对象大小的标准,这个标准是由我们来定的)。那么,我们为什么不直接在People类中定义一个compareTo实例方法来定义People对象的比较标准,而是要去实现一个Comparable<T>接口呢?让我们举例来说明以下,假如我们调用了以下代码来对People对象数组peoples进行比较:
Arrays.sort(peoples);
在sort方法内部,实际上调用了类似下面这样的代码来比较People对象:
if (peoples[i].compareTo(peoples[j]) > 0) {
//若为true说明peoples[i] 大于peoples[j]
}
也就是说Arrays.sort方法内部调用了People对象的compareTo方法,那么编译器如何知道People类中确实定义了一个方法呢?若People类没有实现Comparable接口,编译器就只有检查这个类是否实现了这个方法,这样做无疑会增大开销,若接口中的方法不只一个,开销就更大了。而且需要比较的对象可能不只一个,假如后面又有Date对象、Job对象需要我们比较呢?如果每次调用相应对象的compareTo方法都要去检查一下它究竟实现了这个方法没有,将无形中增加很多本不必要的开销。
反过来,我们看看引入接口的好处。People类头部的"implements Comparable<People>“就像是在告诉编译器”我是可比较的,可以直接调用我的compareTo方法而不用检查我有没有这个方法“。这样一来,所有可比较的对象只要实现Comparable<T>接口,编译器就能够知道它一定定义了compareTo方法。一个接口实际上是描述了一种规范,它只说明了这个接口需要实现什么需求,而没有强制规定这个需求如何实现。就像之前我们提到的Comparable<T>接口,需要比较大小的对象不只People对象一个,既然大家都需要比较大小,那索性就来个规定,所有想比较大小的对象的类都实现Compareble<T>接口,统一规定一下可比较的对象究竟该满足什么需求(这里的需求就是compareTo方法)。
2. 接口的特性
Comparable<People> p = new People(...);
if (p instanceof Comparable) {
...
}
我们也可以”继承“一个接口:
public interface InA extends InB {
...
}
接口还有一个很有用的特性就是一个类可以实现多个接口。其实以上提到的接口特性只要”接口只是一种规范”这个本质来看就很好理解:比如接口中不能实例化、不能含有实例变量,就是因为接口不同于类那样是一个“蓝图”,接口更像一个“标签”。
3.接口与抽象类的比较
4. 接口与回调(callback)
回调(callback)是一种常见的设计模式,利用这种模式,我们可以指出在某个事件发生时应该采取什么动作。比如,Java类库中有个Timer类,可以使通过它在过了指定的时间后发出通知,就像一个闹钟一样。初始化Time类时,需要指定一个时间和到达这个时间后要执行的动作。在这里,告诉Timer要执行的动作就是通过传递给它一个对象,然后到达指定时间后,Timer会调用这个对象的方法。Timer要求我们传递给它的对象需要实现ActionListener接口,这个接口的定义如下:
public interface ActionListener {
public void actionPerformed(ActionEvent event);
}
我们传递给Time构造器的对象必然定义了actionPerformed方法,我们可以在这个方法中定义我们在定时间隔到达后需要执行的动作。请看以下代码:
public class TimerTest {
public static void main(String[] args) {
ActionListener listener = new TimePrinter();
Timer t = new Timer(10000, listener);
t.start();
System.exit(0);
}
}
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("The time is " + now);
}
}
在以上代码中,会每隔10秒调用一次TimerPrinter中的actionPerformed方法,获取并输出一次当前时间。actionPerformed方法就是回调方法,在回调模式中,通常都是一个类(TimePrinter)实现一个接口(ActionListener),然后另一个类(Timer)的对象通过持有实现该接口的类对象引用(listener)来调用相应的回调方法。
5.参考资料
Java核心技术点之接口的更多相关文章
- Java核心技术卷一基础知识-第6章-接口与内部类-读书笔记
第6章 接口与内部类 本章内容: * 接口 * 对象克隆 * 接口与回调 * 内部类 * 代理 接口技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现(implement)一 ...
- Java核心技术点之泛型
1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小 ...
- Java核心技术点之集合框架
1. 概述 Java集合框架由Java类库的一系列接口.抽象类以及具体实现类组成.我们这里所说的集合就是把一组对象组织到一起,然后再根据不同的需求操纵这些数据.集合类型就是容纳这些对象的一个容 ...
- Java核心技术点之内部类
1. 为什么要使用内部类 内部类就是定义在一个类内部的类,那么为什么要使用内部类呢?主要原因有以下几点:第一,内部类中定义的方法能访问到它所在外部类的私有属性及方法:第二,外部类无法实现对同一 ...
- Java核心技术点之动态代理
本篇博文会从代理的概念出发,介绍Java中动态代理技术的使用,并进一步探索它的实现原理.由于个人水平有限,叙述中难免出现不清晰或是不准确的地方,希望大家可以指正,谢谢大家:) 一.概述 1. 什么是代 ...
- java核心技术学习笔记之一程序设计概述
Java 核心技术之一程序设计概述 一. Java语言的特点 简单行 :取经于C++,排除了C++不常用的指针.结构等,增加垃圾回收. 面向对象:与C++不同是单继承,但是可以继承多接口.完全面向 ...
- java核心技术之流与文件
InputStream和OutputStream构成了输入/输出类层次结构的基础.用于按字节进行读写.而与之处在同一等级的Reader/Writer同样作为抽象类定义了用于对字符进行读取的类层次结构, ...
- Java命名和目录接口——JNDI
JNDI即Java命名和目录接口(JavaNaming and Directory Interface),它属于J2EE规范范畴,是J2EE的核心技术之一,提供了一组接口.类和关于命名空间的概念.JD ...
- Java核心技术第五章——1.类、超类、子类(2)
继上一篇Java核心技术第五章——1.类.超类.子类(1) 6.重载解析 假如调用ClassName.Method(args) 1.编译器列出类ClassName所有名为Method的方法. 2.编译 ...
随机推荐
- JavaSE环境下的shiro(源自腾讯课堂)
Shiro作用: 认证(登录).授权(鉴权).加密(用户名/密码加密).会话管理(session).Web集成.缓存 apache官网可以下载 图一 图二 图三 图一 .二是配置文件内容,对于图三的: ...
- redis的主从配置
redis的主备配置比较简单,只需要在配置上新增slaveof属性即可,如果主节点需要密码验证,则在加上masterauth属性. 测试安装一个备用redis,备份前一章的节点redis的docker ...
- PHP 通过命令异步执行PHP程序
通过PHP执行系统命令调用PHP执行程序,让进程挂起到后台执行,不影响用户页面交互. 控制器调用命令,不用等待,后台创建一个进程执行程序. system(“nohup php command.php ...
- Python学习手册之元组拆包、三元运算符和 else 语句深入
在上一篇文章中,我们介绍了 Python 之禅. Python 编程规范和函数参数,现在我们介绍 Python 的元组拆包.三元运算符和对 Python 的 else 语句深入讲解.查看上一篇文章请点 ...
- 04IP编址(网络层)
帧中type为0x0800,送给ip ip报文结构 TTL 生存时间最大为255,经过三层设备就减1 protocol:协议号 version:4,6 source ip address:源ip编 ...
- springBoot -webSocket 基于STOMP协议交互
浅谈WebSocket WebSocket是在HTML5基础上单个TCP连接上进行全双工通讯的协议,只要浏览器和服务器进行一次握手,就可以建立一条快速通道,两者就可以实现数据互传了.说白了,就是打破了 ...
- MySQL高级-索引优化
索引失效 1. 2.最佳左前缀法则 4. 8. 使用覆盖索引解决这个问题. 二.索引优化 1.ORDER BY 子句,尽量使用Index方式排序,避免使用FileSort方式排序 MySQL支持两种方 ...
- Redis系列八 使用Jedis
使用Jedis jar操作Redis 1.配置redis.conf文件,修改 2.建java工程,加入 jedis jar包 3.代码示例: package com.ntjr.redis; impor ...
- C#中创建二维数组,使用[][]和[,]的区别
C#中,我们在创建二维数组的时候,一般使用arr[][]的形式,例如 int[][] aInt = new int[2][]; 但声明二维数组还有一种方法,是使用arr[,]的形式.两者有什么区别呢? ...
- 关于Python的多重排序
Python预置的list.sort().sorted()方法可实现各种数组的排序,但支持的只限于一个key,如果要多重排序,目前所知的方法只有自定义了. Help on built-in funct ...