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 + "]";
    }
}
View Code

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();
    }
}
View Code

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();
        }
    }
}
View Code

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();
    }
}
View Code

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);
    }
}
View Code

 

posted @ 2020-05-03 21:18  ayor  阅读(142)  评论(0)    收藏  举报