1. 程式人生 > >第六章 介面&&代理

第六章 介面&&代理

第六章 介面和代理

介面

什麼是介面?

介面不是類,而是對類的一組需求描述,這些類要遵從介面描述的統一格式進行定義。

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是工具構造者和類庫設計者感興趣的高階技術。對於應用程式設計師不是很重要。