接口与回调
回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。
假设我们构造一个定时器,当达到时间间隔时响一下。
我们可以使用 java.awt.event 包中的 ActionListener 接口,当达到指定的时间间隔时,定时器就会调用接口中的 actionPerformed()
。
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) {
ActionListener listener = new TimePrinter();
// construct a timer that calls the listener
// once every 10 seconds
Timer t = new Timer(10000, listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
}
Comparator 接口
Arrays.sort()
方法中,有一个版本的参数为一个数组和比较器(commparator),比较器是实现了 Comparator 接口的类的实例,所以就可以自定义数组的排序方式。
java.util.Comparator.java
public interface Comparator<T> {
int comapre(T o1, T o2);
... some defalut and static method
}
如果你要按长度比较字符串,就可以定义一个实现 Comparator
public LenghtComparator implements Comparator<String> {
public int compare(String first, String second) {
return first.length() - second.length();
}
}
就可以这样使用:
String[] friends = { "Peter", "Kang", "Jan" };
Arrays.sort(friends, new LengthComparator());
对象克隆
Cloneable 接口指示一个类提供了一个安全的 clone 方法。
在开始之前,先了解克隆和拷贝的含义。
使用拷贝,即 =
。原变量和副本都是同一个对象的引用,而拷贝则不同:
不过并没有这么简单,clone 方法是 Object 的一个 protected 方法,只有在 Employee 所在的包里面,你才能调用该克隆方法。并且 Object 中的 clone 方法只能对基本类型进行拷贝,如果对象包含子对象的引用,拷贝域就会得到相同的引用,与拷贝类似。
比如,有一个 Employee 类:
public Employee {
String name;
double salary;
Date hireDay;
}
默认的 clone()
是「浅拷贝」,并没有克隆对象中引用的其他对象:
可以看到上述的实例域中,salary
由于是 double 基本类型,所以会克隆一份。但 name
和 hireDay
是对象引用,他们只会引用上一个对象的拷贝。
果子对象属于一个不可变类,如 String,没有更改器方法会改变它,没有方法会生成它的引用,这种情况是允许的。但是,通常子对象是可变的,这时就必须要重新定义 clone()
来建立一个深拷贝,同时克隆所有子对象。
查看源码发现 Cloneable 接口里面什么都没有,其实 Cloneable 并没有实际性的作用,它只是作为一个标记,指示类设计者了解克隆的过程,真正的 clone()
是从 Object 类中继承的。但是,对象对于克隆很「偏执」,如果一个对象请求克隆,但没有实现这个接口,就会生成一个受查异常
Cloneable 接口是 Java 提供的一组标记接口(tagging interface)之一。标记接口不包含任何方法;它唯一的作用就是允许在类型查询中使用 instanceof
上面讲到 Object 类中的 clone()
是 protected 权限。所以,即使是浅拷贝也要重新定义 clone()
为 public:
定义浅拷贝
class Employee implements Cloneable {
public Employee clone() throws CloneNotSupportedException {
return (Employee) super.clone();
}
...
}
当然深拷贝也要重新定义,并且要克隆对象中可变的实例域:
class Employee implements Cloneable {
public Employee clone() throws CloneNotSupportedException {
// call Object.clone()
Employee cloned = (Employee) super.clone();
// clone mutable fields
cloned.hireDay = (Date) hireDay.clone(); // Date 类也是实现了 clone 方法
return cloned;
}
...
}
如果在一个对象上调用 clone,但这个对象的类并没有实现 Cloneable 接口,Object 类的 clone 方法就会抛出一个 CloneNotSupportedException。当然,Employee 和 Date 类实现了 Cloneable 接口,所以不会抛出这个异常。不过,编译器并不了解这一点,因此,我们声明了这个异常
如果子类中也有可变类,那子类中要覆盖 clone()
。如果没有可变类,可以不覆盖。
所有的数组类型有一个 public 的 clone()
,可以用该方法建立一个新数组。