接口与回调

回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。
假设我们构造一个定时器,当达到时间间隔时响一下。
我们可以使用 java.awt.event 包中的 ActionListener 接口,当达到指定的时间间隔时,定时器就会调用接口中的 actionPerformed()

  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.util.*;
  4. import javax.swing.*;
  5. import javax.swing.Timer;
  6. // to resolve conflict with java.util.Timer
  7. public class TimerTest {
  8. public static void main(String[] args) {
  9. ActionListener listener = new TimePrinter();
  10. // construct a timer that calls the listener
  11. // once every 10 seconds
  12. Timer t = new Timer(10000, listener);
  13. t.start();
  14. JOptionPane.showMessageDialog(null, "Quit program?");
  15. System.exit(0);
  16. }
  17. }
  18. class TimePrinter implements ActionListener {
  19. public void actionPerformed(ActionEvent event) {
  20. System.out.println("At the tone, the time is " + new Date());
  21. Toolkit.getDefaultToolkit().beep();
  22. }
  23. }

Comparator 接口

Arrays.sort() 方法中,有一个版本的参数为一个数组和比较器(commparator),比较器是实现了 Comparator 接口的类的实例,所以就可以自定义数组的排序方式。
java.util.Comparator.java

  1. public interface Comparator<T> {
  2. int comapre(T o1, T o2);
  3. ... some defalut and static method
  4. }

如果你要按长度比较字符串,就可以定义一个实现 Comparator 的类:

  1. public LenghtComparator implements Comparator<String> {
  2. public int compare(String first, String second) {
  3. return first.length() - second.length();
  4. }
  5. }

就可以这样使用:

  1. String[] friends = { "Peter", "Kang", "Jan" };
  2. Arrays.sort(friends, new LengthComparator());

对象克隆

Cloneable 接口指示一个类提供了一个安全的 clone 方法。
在开始之前,先了解克隆拷贝的含义。
使用拷贝,即 = 。原变量和副本都是同一个对象的引用,而拷贝则不同:
cloningvscopying
不过并没有这么简单,clone 方法是 Object 的一个 protected 方法,只有在 Employee 所在的包里面,你才能调用该克隆方法。并且 Object 中的 clone 方法只能对基本类型进行拷贝,如果对象包含子对象的引用,拷贝域就会得到相同的引用,与拷贝类似。
比如,有一个 Employee 类:

  1. public Employee {
  2. String name;
  3. double salary;
  4. Date hireDay;
  5. }

默认的 clone() 是「浅拷贝」,并没有克隆对象中引用的其他对象:
chapter_VI-ShallowClone
可以看到上述的实例域中,salary 由于是 double 基本类型,所以会克隆一份。但 namehireDay 是对象引用,他们只会引用上一个对象的拷贝。
果子对象属于一个不可变类,如 String,没有更改器方法会改变它,没有方法会生成它的引用,这种情况是允许的。但是,通常子对象是可变的,这时就必须要重新定义 clone() 来建立一个深拷贝,同时克隆所有子对象。
查看源码发现 Cloneable 接口里面什么都没有,其实 Cloneable 并没有实际性的作用,它只是作为一个标记,指示类设计者了解克隆的过程,真正的 clone() 是从 Object 类中继承的。但是,对象对于克隆很「偏执」,如果一个对象请求克隆,但没有实现这个接口,就会生成一个受查异常

Cloneable 接口是 Java 提供的一组标记接口(tagging interface)之一。标记接口不包含任何方法;它唯一的作用就是允许在类型查询中使用 instanceof

上面讲到 Object 类中的 clone() 是 protected 权限。所以,即使是浅拷贝也要重新定义 clone() 为 public:
定义浅拷贝

  1. class Employee implements Cloneable {
  2. public Employee clone() throws CloneNotSupportedException {
  3. return (Employee) super.clone();
  4. }
  5. ...
  6. }

当然深拷贝也要重新定义,并且要克隆对象中可变的实例域:

  1. class Employee implements Cloneable {
  2. public Employee clone() throws CloneNotSupportedException {
  3. // call Object.clone()
  4. Employee cloned = (Employee) super.clone();
  5. // clone mutable fields
  6. cloned.hireDay = (Date) hireDay.clone(); // Date 类也是实现了 clone 方法
  7. return cloned;
  8. }
  9. ...
  10. }

如果在一个对象上调用 clone,但这个对象的类并没有实现 Cloneable 接口,Object 类的 clone 方法就会抛出一个 CloneNotSupportedException。当然,Employee 和 Date 类实现了 Cloneable 接口,所以不会抛出这个异常。不过,编译器并不了解这一点,因此,我们声明了这个异常
如果子类中也有可变类,那子类中要覆盖 clone()。如果没有可变类,可以不覆盖。
所有的数组类型有一个 public 的 clone(),可以用该方法建立一个新数组。