zoukankan      html  css  js  c++  java
  • 第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对象是否代表一个代理类。

  • 相关阅读:
    样式问题
    布局
    通用模板实现可变参数函数
    使用单例模式实习string类
    动态生成一维数组和二维数组
    自定义的类传数据到主窗口控件上的方法
    使用TableView
    G480折腾上了黑苹果,完美了,哈哈
    error C2383: 此符号中不允许有默认参数
    动态链接库的隐式动态链接和显示动态链接
  • 原文地址:https://www.cnblogs.com/xuzhen97/p/7738189.html
Copyright © 2011-2022 走看看