接口主要用来描述类具有什么功能,并且不给出每个功能的具体实现,接口不是类。
一个类可以实现(implement)一个或多个接口(interface),并在需要接口的地方,随时使用实现了相应接口的对象。
接口中的所有方法自动地属于 public。
接口绝不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务由实现接口的那个类完成。
可以将接口看成是没有实例域的抽象类。
实现一个接口,必须:1)将类声明为实现给定的接口
2)对接口中的所有方法进行定义
实例:Arrays 类中的 sort 方法可以对对象数组进行排序,但对象所属的类必须实现了 Comparable 接口,如下:
public interface Comparable<T> { int compareTo(T other); }
实现这个接口,必须提供 compareTo(Employee other) 方法。
实现接口:
class Employee implements Comparable<Employee> { //...... public int compareTo(Employee other) { if (salary < other.salary) return -1; if (salary > other.salary) return 1; return 0; } //..... }
完整程序:
import java.util.*; public class EmployeeSortTest { public static void main(String[] args) { Employee[] staff = new Employee[3]; staff[0] = new Employee("Ai",35000); staff[1] = new Employee("Bi",75000); staff[2] = new Employee("Ci",38000); Arrays.sort(staff); for (Employee e : staff) System.out.println("name="+e.getName()+",salary=" +e.getSalary()); } } class Employee implements Comparable<Employee> //实现接口的类 { public Employee(String n,double s) { name = n; salary = s; } public String getName() { return name; } public double getSalary() { return salary; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public int compareTo(Employee other) { if (salary < other.salary) return -1; if (salary > other.salary) return 1; return 0; } private String name; private double salary; }
运行结果
接口不是类,尤其不能使用 new 运算符实例化一个接口,但尽管不能构造接口的对象,却能声明接口的变量,接口的变量必须引用实现了接口的类对象:
Comparable x; x = new Employee(...);
也可以使用 instance 检查一个对象是否实现了某个特定的接口:if(anObject instanceof Comparable){...}
接口也可以被扩展(继承)。
在接口中不能包含实例域或静态方法,但却可以包含常量。
尽管每个类智能够拥有一个超类,但却可以实现多个接口(使用逗号隔开)。
为何不直接使用抽象类而使用接口?
因为每个类智能扩展于一个类(java不支持C++中的多继承),但每个类可以实现多个接口。
当拷贝一个变量时,原始变量与拷贝变量引用同一个对象,改变引用的对象时,会同步改变两个变量。如想要创建一个
对象的新的 copy 之后,不再相互影响,就需要使用 clone 方法。
clone 方法是 Object 类的一个 proteced 方法,若子对象可变,则必须重新定义 clone 方法,实现克隆子对象的深拷贝。
所有的数组类型均包含一个 clone 方法(public),可以利用这个方法创建一个包含所有数据元素拷贝的一个新数组。
对于每一个类,都需要做出如下判断:
1)默认的 clone 方法是否满足要求
2)默认的 clone 方法是否能够通过调用可变子对象的 clone 得到修补
3)是否不应该使用 clone ,实际上这个选择是默认的,如果要选前两项,类必须:
1)实现 Cloneable 接口(一种标记接口)
2)使用 public 访问修饰符重新定义 clone 方法。
实例:
import java.util.*; public class CloneTest { public static void main(String[] args) { try { Employee original = new Employee("Ai.Public",50000); original.setHireDay(1991,1,1); Employee copy = original.clone(); //克隆 copy.raiseSalary(10); copy.setHireDay(1993,12,31); System.out.println("original="+original); System.out.println("copy="+copy); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } class Employee implements Cloneable//实现接口 { public Employee(String n,double s) { name = n; salary = s; hireDay = new Date(); } //只要在 clone 中没有实现 Cloneable 接口的对象,Object 类的 clone 方法就会 //抛出一个 ClontNot-SupportException 异常,实现之后,还需声明异常如下: public Employee clone() throws CloneNotSupportedException { Employee cloned = (Employee) super.clone();//调用 Object.clone() cloned.hireDay = (Date) hireDay.clone();//克隆可变域 return cloned; } public void setHireDay(int year, int month, int day) { Date newHireDay = new GregorianCalendar(year,month-1,day).getTime(); hireDay.setTime(newHireDay.getTime()); } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public String toString() { return "Employee[name="+name+",salary="+salary +",hireDay="+hireDay+"]"; } private String name; private double salary; private Date hireDay; }
回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。
在 java.swing 包中有一个 Timer 类(定时器),使用时要求相应的类实现了 java.awt.event 包的 ActionListener 接口:
public interface ActionListener { void actionPerformed(ActionEvent event); }
import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; public class TimerTest { public static void main(String[] args) { ActionListener listener = new TimePrinter(); //构造一个调用 listener 的定时器,10秒钟每次 //Time(int interval,AvtionListener listener):构造 //一个定时器,每隔 interval 毫秒钟通告 listener 一次。 Timer t = new Timer(10000,listener); t.start();//启动定时器 //显示一个包含一条消息和 OK 按钮的对话框,位于屏幕的中央 JOptionPane.showMessageDialog(null,"Quit program?"); System.exit(0); } } class TimePrinter implements ActionListener //实现 ActionListener 接口的类 { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone,the time is "+now); Toolkit.getDefaultToolkit().beep(); //Toolkit.getDefaultToolkit():获得默认工具箱,beep():发出一声铃响 } }
内部类(inner class)是定义在另一个类中的类,使用内部类的原因:
a)内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
b)内部类可以对同一个包中的其他类隐藏起来
c)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。为了说明这个概念
我们将外围类对象的引用称为 outer,下面的 if(beep)相当于 if(outer.beep)
实例:
import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; public class InnerClassTest { public static void main(String[] agrs) { TalkingClock clock = new TalkingClock(1000,true); clock.start(); JOptionPane.showMessageDialog(null,"Quit program?"); System.exit(0); } } class TalkingClock { public TalkingClock(int interval,boolean beep) { this.interval = interval; this.beep = beep; } public void start() { ActionListener listener = new TimePrinter(); Timer t = new Timer(interval,listener); t.start(); } private int interval; private boolean beep; public class TimePrinter implements ActionListener //内部类 { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone,the time is "+now); if (beep) Toolkit.getDefaultToolkit().beep(); //这里的 beep 是引用外围类对象的数据域 } } }
上例中,TimePrinter 这个类名字只在 start 方法中创建这个类型的对象时使用了一次,因此,也可以就在此方法中定义局部类:
public void start() { class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone,the time is "+now); if (beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimePrinter(); Timer t = new Timer(interval,listener); t.start(); }
局部类有一个优势,即对外部世界(即使是 TalkingClock 类中的其他代码)可以完全地隐藏起来。
假如只创建这个局部内部类的一个对象,就不必命名了,这种类被称为匿名内部类。
匿名类不能有构造器,取而代之的是将构造器参数传递给超类构造器。下面实例中的匿名内部类的含义是:创建一个实现 ActionListener 接口的类的新对象,需要实现的方法 actionPerformed 定义在括号内。
import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; public class AnonymousInnerClassTest { public static void main(String[] agrs) { TalkingClock clock = new TalkingClock(); clock.start(1000,true); JOptionPane.showMessageDialog(null,"Quit program?"); System.exit(0); } } class TalkingClock { public void start(int interval,final boolean beep) { ActionListener listener = new ActionListener()//匿名内部类 { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone,the time is "+now); if (beep) Toolkit.getDefaultToolkit().beep(); } }; Timer t = new Timer(interval,listener); t.start(); } }
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围对象,为此,可以将内部类声明为 static。
public class StaticInnerClassTest { public static void main(String[] args) { double[] d = new double[20]; for (int i = 0; i < d.length; i++) d[i] = 100 * Math.random(); ArrayAlg.Pair p = ArrayAlg.minmax(d); System.out.println("min = " + p.getFirst()); System.out.println("max = " + p.getSecond()); } } class ArrayAlg { public static class Pair //包含最大值最小值的类 { public Pair(double f, double s) { first = f; second = s; } public double getFirst() { return first; } public double getSecond() { return second; } private double first; private double second; } public static Pair minmax(double[] values)//计算最大最小值的方法 { double min = Double.MAX_VALUE; double max = Double.MIN_VALUE; for (double v : values) { if (min > v) min = v; if (max < v) max = v; } return new Pair(min, max); } }
注:声明在接口中的内部类自动成为 static 和 public。
利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定要实现哪个接口时才有必要使用。
原文:http://blog.csdn.net/llhhyy1989/article/details/19708655