6 接口与内部类
6.1 接口
在Java中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。
public interface Comparable { int compareTo(Object other); }
接口当中的所有方法自动属于public。因此,在接口当中声明方法时,不必提供关键字public。
接口当中可能包含多个方法。
接口绝不能含有实例域,也不能在接口当中实现方法。
要将类声明为实现某个接口,需要使用关键词implements:
class Employee implements Comparable
compareTo方法的实现:
public int compareTo(Object otherObject) { Employee other = (Employee) otherObject; return Double.compare(salary, other.salary); }
在Java5.0 SE当中,可以替换为Comparable<Employee> 接口的实现:
class Employee implements Comparable<Employee> { public int compareTo(Employee other) { return Double.compare(salary, other.salary) } }
Java是一种强类型语言。在调用方法时,编译器会检查这个方法是否存在。
package com.company.interfaces; import java.util.Arrays; public class EmployeeSortTest { public static void main(String[] args) { Employee[] staff = new Employee[3]; staff[0] = new Employee("Kate", 10000); staff[1] = new Employee("Ann", 12000); staff[2] = new Employee("Lucy", 11500); Arrays.sort(staff); for (Employee e : staff) { System.out.println("name=" + e.getName() + ", salary=" + e.getSalary()); } } } class Employee implements Comparable<Employee> { private String name; private double salary; 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) { return Double.compare(salary, other.salary); } }
6.1.1 接口的特性
接口不是类,尤其不能用new运算符实例化一个接口。
但是可以声明接口的变量。
接口变量必须引用实现了接口的类对象。
也可以使用instanceof检查一个对象是否实现了某个特定的接口。
接口也可以扩展:
public interface Powered extends Moveable { double milesPerGallon(); }
接口当中可以虽然不能包含实例域和静态方法,但是可以包含常量。
接口当中的域将被自动设置为public static final。
一个类可以实现多个接口,只需要用逗号分隔开。
6.1.2 接口与抽象类
Java没有多继承,接口可以实现多继承的大部分好处,又可以避免多继承的复杂性和低效性。
6.2 对象克隆
当拷贝一个对象的时候,原始变量与拷贝变量引用同一个对象。如果创建一个新的对象的,但以后将可以各自改变各自的状态,那就需要clone方法。
clone方法是Object类的一个protected方法,在用户编写的代码里面不能直接调用。
默认的克隆操作是浅拷贝,并没有克隆包含在对象中的内部对象。
如果子对象是可变的,必须重新定义clone方法,以便实现对子对象的深拷贝。
1,实现Cloneable接口。
2,使用public访问修饰符重新定义clone方法。
import java.util.Date; import java.util.GregorianCalendar; public class CloneTest { public static void main(String[] args) { try { Employee original = new Employee("Ann", 23000); original.setHireDay(2000, 1, 1); Employee copy = original.clone(); copy.raiseSalary(10); copy.setHireDay(2001, 2, 3); System.out.println("original=" + original); System.out.println("copy=" + copy); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } class Employee implements Cloneable { private String name; private double salary; private Date hireDay; public Employee(String n, double s) { name = n; salary = s; hireDay = new Date(); } public Employee clone() throws CloneNotSupportedException { Employee cloned = (Employee) super.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 + "]"; } }
6.3 接口与回调
回调callback是一种常用的程序设计模式,可以指出某个特定事件发生时应该采用的动作。
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; public class TimerTest { public static void main(String[] args) { ActionListener listener = new TimerPrinter(); Timer t = new Timer(10000, listener); t.start(); JOptionPane.showMessageDialog(null, "Quit Program?"); System.exit(0); } } class TimerPrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); Toolkit.getDefaultToolkit().beep(); } }
6.4 内部类
内部类是定义在另一个类中的类。为什么需要使用内部类?
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据;
- 内部类可以对一个包当中其他类隐藏起来。
- 想要定义一个回调函数又不想编写大量代码的时候,使用匿名内部类比较便捷。
6.4.1 使用内部类访问对象状态
内部类访问外部类的实例域。
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; public class InnerClassTest { public static void main(String[] args) { TalkingClock clock = new TalkingClock(1000, true); clock.start(); JOptionPane.showMessageDialog(null, "Quit Program?"); System.exit(0); } } class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval, boolean beep) { this.interval = interval; this.beep = beep; } public void start() { ActionListener listener = new TimerPrinter(); Timer t = new Timer(interval, listener); t.start(); } public class TimerPrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At this tone, the time is" + now); if(beep) Toolkit.getDefaultToolkit().beep(); } } }
6.4.2 内部类的特殊语法规则
外围类的引用:OuterClass.this。
引用内部类:OuterClass.InnerClass。
6.4.3 内部类是否有用,必要和安全
6.4.4 局部内部类
6.4.5 由外部方法访问final变量
6.4.6 匿名内部类
import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import javax.swing.*; public class AnonymousInnerClassTest { public static void main(String[] args) { 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() { @Override public void actionPerformed(ActionEvent e) { Date now = new Date(); System.out.println("At this tone, this time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } }; Timer t = new Timer(interval, listener); t.start(); } }
6.4.7 静态内部类
只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象。为此,可以将内部类声明为static,以便取消产生的引用。
6.5 代理
代理,是Java se1.3 新增加的特性。利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定实现哪个接口的时候才有必要使用。
要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法,有三个参数:一个类加载器;一个Class对象数组,每个元素都是需要实现的接口;一个调用处理器。
调用处理器是实现了InvocationHandler接口的类对象,在这个接口中只有一个方法:
Object.invoke(Object proxy, Method method, Object[] args)
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Random; public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[1000]; for (int i = 0; i < elements.length; i++) { Integer value = i + 1; InvocationHandler handler = new TraceHandler(value); Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler); elements[i] = proxy; } Integer key = new Random().nextInt(elements.length) + 1; int result = Arrays.binarySearch(elements, key); if (result >= 0 ) System.out.println(elements[result]); } } class TraceHandler implements InvocationHandler { private Object target; public TraceHandler(Object t) { target = t; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { System.out.println(target); System.out.print("." + m.getName() + "("); if (args != null) { for (int i = 0; i < args.length; i++) { System.out.print(args[i]); if (i < args.length - 1) { System.out.print(", "); } } } System.out.println(")"); return m.invoke(target, args); } }

浙公网安备 33010602011771号