第六章 接口&&代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37726222/article/details/84591529

接口

什么是接口?

接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。

Arrays 类中的sort方法可以对对象数组排序,但要求对象所属的类实现了COMparable接口。

1) **接口中方法自动为public。**故不必声明即可
2)接口的附加要求,compareTo必须确实比较两个对象的内容。
3)接口不可以有实例域。
4)接口的关键字:implements

为了让一个类实现一个接口,怎么做呢?

  1. 将类声明为实现给定的接口
  2. 对接口中的所有方法进行定义

前面的sort方法由于必须使用compareTo方法,而实现了Comparable接口的类必须实现compareTo方法。所以我们要求实现了Comparable接口为必须的。

接口有哪些特性呢?

  1. 可声明接口变量,但只可引用实现了接口的类的对象。可使用instanceOf进行检查
  2. 接口支持扩展(一个接口扩展另一个接口)如下:
  3. 接口可包含常量,但被自动设置为public static final
  4. 一个类可以实现多个接口 如下:
public interface Pow extends Mov,Cloneable{
doubel SPEED=15;
}

接口与抽象类的区别?

java不支持多重继承
也就是说,一个类只可以extends另一个类,而不可以是多个
但是却可以implements多个接口。

关于接口的方法:

  • 接口中定义静态方法是允许的。
  • 可以在接口中定义默认方法,关键字是default +返回值+函数名
  • 默认方法可以用来在“接口演化过程”中定义新加入接口的类,防止错误的发生。P220
  • 默认方法的冲突问题:
    • 类优先规则
    • 两个接口的默认方法重名的情况,必须覆盖这个方法来解决冲突:
    •   public String getName(return Person.super.getName());
      
  • 存在这样一种特殊情况,两个接口中的共享方法都没有提供实现。那么如果实现类没有实现这个共享方法,那么这个类本身是抽象的。

接口与回调

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

package timer;

/**
   @version 1.01 2015-05-12
   @author Cay Horstmann
*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer; 
// to resolve conflict with java.util.Timer

public class TimerTest
{  
   public static void main(String[] args)
   {  
      ActionListener listener = new TimePrinter();

      // construct a timer that calls the listener
      // once every 10 seconds
      Timer t = new Timer(500, listener);
      t.start();

      JOptionPane.showMessageDialog(null, "Quit program?");//弹窗
      System.exit(0);
   }
}

class TimePrinter implements ActionListener
{  
   public void actionPerformed(ActionEvent event)
   {  
      System.out.println("At the tone, the time is " + new Date());
      Toolkit.getDefaultToolkit().beep();
   }
}

使用Comparator接口

Arrays.sort方法的第二个版本,有一个数组一个比较器作为参数,比较器是实现了Comparator接口的类的实例。

Arrays.sort(friends,new LengthComparator());//使用方法

public interface Comparator<T>
{
int compare(T first,T second)
}
//如何实现呢?
class LengthComparator implements Comparator<String>
{
public int compare(String first,String second){
	return first.length-second.length;
}
}
注意,compare方法要在比较器对象上调用,而不是在字符串本身上调用。

对象的克隆

关于浅拷贝与深拷贝的区别?
浅拷贝对于类的实例域包含对象的引用变量的,浅拷贝之后,仍然与原类共享该实例域的相同存储区域。所以是浅拷贝。

那如何实现深拷贝呢?
在clone()方法中,对克隆对象中可变的实例域单独地进行克隆。

注意Cloneable接口只是一个标记而已。还有很重要的一个作用是,需要在实现Cloneable接口的类中将clone()方法重定义为public。为什么?
**子类只能调用受保护的clone方法克隆自己的对象。重定义clone()为public可以允许所有方法克隆对象。**这句话不太理解诶。。。各位看官谁帮帮我呀?

反正重写的clone()函数一定要是被声明为public的。

下面是一个例子:

 public Employee clone() throws CloneNotSupportedException
   {
      // call Object.clone()
      Employee cloned = (Employee) super.clone();

      // clone mutable fields
      cloned.hireDay = (Date) hireDay.clone();

      return cloned;
   }

代理

为什么使用代理?

需要程序处于运行状态时定义一个新类。代理类可以在运行时创建全新的类,这样的代理类能够实现指定的接口。尤其是它具有下列方法:

  • 指定接口需要的全部方法
  • Object类的全部方法toString equals等
    然而不能在运行时定义这些方法的新代码。而是要提供一个调用处理器。它是实现了InvocationHandler接口的类对象。这个接口有一个方法:
    Object invoke(Object proxy,Method method,Object[]args)
    == 无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出 处理调用的方式 ==

如何创建代理对象?

使用Proxy类的newProxyInstance() method
There are three parameters:

  • class loader
  • a Class Object-ArrayList
  • invoke handler

Two problems:

  • How to define a handler?
  • What do we use proxy for?
    • 在程序运行期间,将用户接口事件与动作联系起来
    • 为调试,跟踪方法调用
    • 路由对远程服务器的方法调用

proxy代理:

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);
         //该如何理解此处的调用处理器?
         //构造实现指定接口的代理类的一个新的实例。
         //所有方法会调用给定处理器对象的Invoke方法。
         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);
   }
}

Integer类实现了Comparable接口。接口对象属于在运行时定义的类。这个类实现了Comparable接口。
这个类也实现了Comparable接口。然而,它的compareTo方法调用了代理对象处理器的invoke方法。

由于数组中填充了代理对象,
数组填充了代理对象
所以compareTo调用了TraceHander类中的invoke方法。这个方法打印出了方法名和参数。之后用包装好的Integer对象调用compareTo。

注意,即使不属于Compareable接口,toString方法也被代理。下一节中看到,有相当一部分的Object方法都被代理。

代理类有什么特性呢?

  • 它是在运行过程中被创建。然而一旦被创建,就变成了常规类。
  • 所有的代理类都扩展于Proxy类。一个代理类只有一个实例域—调用处理器,其定义在Proxy的超类中,为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。比如,上面的程序,代理Comparable对象时,TraceHandler包装了实际的对象。
  • 所有的代理类覆盖了Object类中的toString equals 和hashcode方法。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的invoke。Object类中的其他方法没有被重新定义。
  • 对于特定的类加载器和预设的一组接口来说,只能有一个代理类。也就是说,同一个类加载器和接口数组调用两次newProxyInstance方法的话,那么只能得到同一个类的两个对象。

总而言之,clone和proxy是工具构造者和类库设计者感兴趣的高级技术。对于应用程序员不是很重要。

猜你喜欢

转载自blog.csdn.net/weixin_37726222/article/details/84591529