第六章 介面&&代理
第六章 介面和代理
介面
什麼是介面?
介面不是類,而是對類的一組需求描述,這些類要遵從介面描述的統一格式進行定義。
Arrays 類中的sort方法可以對物件陣列排序,但要求物件所屬的類實現了COMparable介面。
1) **介面中方法自動為public。**故不必宣告即可
2)介面的附加要求,compareTo必須確實比較兩個物件的內容。
3)介面不可以有例項域。
4)介面的關鍵字:implements
為了讓一個類實現一個介面,怎麼做呢?
- 將類宣告為實現給定的介面
- 對介面中的所有方法進行定義
前面的sort方法由於必須使用compareTo方法,而實現了Comparable介面的類必須實現compareTo方法。所以我們要求實現了Comparable介面為必須的。
介面有哪些特性呢?
- 可宣告介面變數,但只可引用實現了介面的類的物件。可使用instanceOf進行檢查
- 介面支援擴充套件(一個介面擴充套件另一個介面)如下:
- 介面可包含常量,但被自動設定為
public static final
- 一個類可以實現多個介面 如下:
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是工具構造者和類庫設計者感興趣的高階技術。對於應用程式設計師不是很重要。