第6章 接口与内部类

接口

对象克隆

接口与回调

内部类

代理

6.1 接口

在Java程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。

接口使用interface关键字声明。

要将类声明为实现某个接口,需要使用关键字implements。

6.1.1 接口的特性

  • 接口不能使用new运算符实例化。
  • 可以使用instanceof检查一个对象是否实现了某个特定的接口。
  • 接口可以被扩展,接口扩展接口使用extends关键字。
  • 接口中的所有方法自动地属于public。
  • 接口中不能包含实例域或静态方法,但却可以包含常量,接口中的域将被自动设为public static final。
  • 每个类能够拥有一个超类,但却可以实现多个接口。

6.1.2 接口与抽象类

java中不存在多继承。

6.2 对象克隆

对于每个类,都需要做出下列判断:

  • 默认的clone方法是否满足要求
  • 默认的clone方法是否能够通过调用可变子对象的clone得到修补。
  • 是否不应该使用clone.

实际上,选项3是默认的。如果要选择1或2,类必须:

  • 实现Cloneable接口。
  • 使用public 访问修饰符重新定义clone方法。

6.3 接口与回调

回调(callback)是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。

6.4 内部类

内部类(inner class)是定义在另一个类中的类。使用内部类的主要原因有以下三点:

内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。

内部类可以对同一个包中的其他类隐藏起来。

当想要定义一个回调函数且不想编写大量代码是,使用匿名(anonymous)内部类比较便捷。

6.4.1 使用内部类访问对象状态

内部类既可以访问自身的数据域,也可以访问创建它的外围对象的数据域。

6.4.2 内部类的特殊语法规则

内部类使用外围类引用的语法OuterClass.this

在外围类的作用域之外,可以这样引用内部类OuterClass.InnerClass

6.4.3 内部类是否又用、必要和安全

内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用$分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。

6.4.4 局部内部类

   

public void start(){

class TimePrinter implements ActionListener{

public void actionPerformed(ActionEvent event){

 

}

}

ActionListener listener = new TimePrinter();

Timer t = new Timer(interval,listener);

t.start();

}

 

局部类有一个优势,即对外部世界可以完全地隐藏起来。除了声明它的方法之外,没有任何方法知道它的存在。

6.4.5 由外部方法访问final变量

与其他内部类相比较,局部类还有一个优点。它们不仅能够访问包含它们的外部类,还可以访问局部变量。

6.4.6 匿名内部类

假如只创建这个类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous inner class)。

   

public void start(int interval,final boolean beep){

ActionListener listener = new ActionListener(){

public void actionPerformed(ActionEvent event){

}

}

Timer t = new Timer(interval,listener);

t.start();

}

 

6.4.7 静态内部类

有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围对象。为此,可以将内部类声明为static,以便取消产生的引用。

6.5 代理

利用代理(proxy)可以在运行是创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。

代理类可以在运行时创建全新的类。这样的代理类能够实现指定的接口。尤其是,它具有些列方法:

  • 指定接口所需要的全部方法。
  • Object类中的全部方法,例如,toString、equals等。

然而,不能在运行时定义这些方法的新代码。而是要提供一个调用处理器(invocation handler)。调用处理器是实现了InvocationHandler接口的类对象。

要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法。这个方法有三个参数:

  • 一个类加载器(class loader)。
  • 一个Class对象数组,每个元素都是要实现的接口。
  • 一个调用处理器。

   

package proxy;

import java.lang.reflect.*;

import java.util.*;

/**

* This program demonstrates the use of proxies.

* @version 1.00 2000-04-13

* @author Cay Horstmann

*/

public class ProxyTest

{

public static void main(String[] args)

{

Object[] elements = new Object[1000];

// fill elements with proxies for the integers 1 ... 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;

}

// construct a random integer

Integer key = new Random().nextInt(elements.length) + 1;

// search for the key

int result = Arrays.binarySearch(elements, key);

// print match if found

if (result >= 0) System.out.println(elements[result]);

}

}

/**

* An invocation handler that prints out the method name and parameters, then

* invokes the original method

*/

class TraceHandler implements InvocationHandler

{

private Object target;

/**

* Constructs a TraceHandler

* @param t the implicit parameter of the method call

*/

public TraceHandler(Object t)

{

target = t;

}

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable

{

// print implicit argument

System.out.print(target);

// print method name

System.out.print("." + m.getName() + "(");

// print explicit arguments

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(")");

// invoke actual method

return m.invoke(target, args);

}

}

 

代理类的特性

所有的代理类都扩展于Proxy类。

一个代理类只有一个实例域——调用处理器,它定义在Proxy的超类中。

对于特定的类加载器和预设的一组接口来说,只能有一个代理类。也就是说,如果使用同一个类加载器和接口数组调用两次newProxyInstance方法的话,那么只能够得到同一个类的两个对象,也可以利用getProxyClass方法获得这个类。

   

Class proxyClass = Proxy.getProxyClass(null,interfaces);

代理类一定是public和final。如果代理类实现的所有接口都是public,代理类就不属于某个特定的包;否则,所有非公共有的接口都必须属于同一个包。同时,代理类也属于这个包。

    可以通过调用Proxy类中的isProxyClass检测一个特定的Class对象是否代表一个代理类。

posted @ 2017-10-26 18:00  Bug的梦魇  阅读(263)  评论(0编辑  收藏  举报