1. 代理模式
代理模式的作用是:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:聲明真實對象和代理對象的共同接口;
代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當于對真實對象進行封裝。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。(參見文獻1)
以下以《Java與模式》中的示例為例:
抽象角色:
abstract public class Subject
{
abstract public void request();
}
真實角色:實現了Subject的request()方法。
public class RealSubject extends Subject
{
public RealSubject()
{
}
public void request()
{
System.out.println("From real subject.");
}
}
代理角色:
public class ProxySubject extends Subject
{
private RealSubject realSubject; //以真實角色作為代理角色的屬性
public ProxySubject()
{
}
public void request() //該方法封裝了真實對象的request方法
{
preRequest();
if( realSubject == null )
{
realSubject = new RealSubject();
}
realSubject.request(); //此處執行真實對象的request方法
postRequest();
}
private void preRequest()
{
//something you want to do before requesting
}
private void postRequest()
{
//something you want to do after requesting
}
}
客戶端調用:
Subject sub=new ProxySubject();
Sub.request();
由以上代碼可以看出,客戶實際需要調用的是RealSubject類的request()方法,現在用ProxySubject來代理RealSubject類,同樣達到目的,同時還封裝了其他方法(preRequest(),postRequest()),可以處理一些其他問題。
另外,如果要按照上述的方法使用代理模式,那么真實角色必須是事先已經存在的,并將其作為代理對象的內部屬性。但是實際使用時,一個真實角色必須對應一個代理角色,如果大量使用會導致類的急劇膨脹;此外,如果事先并不知道真實角色,該如何使用代理呢?這個問題可以通過Java的動態代理類來解決。
2.動態代理類
Java動態代理類位于Java.lang.reflect包下,一般主要涉及到以下兩個類:
(1). Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動態實現。
(2).Proxy:該類即為動態代理類,作用類似于上例中的ProxySubject,其中主要包含以下內容:
Protected Proxy(InvocationHandler h):構造函數,估計用于給內部的h賦值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,返回后的代理類可以當作被代理類使用(可使用被代理類的在Subject接口中聲明過的方法)。
所謂Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,然后該class就宣稱它實現了這些
interface。你當然可以把該class的實例當作這些interface中的任何一個來用。當然啦,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。(參見文獻3)
在使用動態代理類時,我們必須實現InvocationHandler接口,以第一節中的示例為例:
抽象角色(之前是抽象類,此處應改為接口):
public interface Subject
{
abstract public void request();
}
具體角色RealSubject:同上;
代理角色:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class DynamicSubject implements InvocationHandler {
private Object sub;
public DynamicSubject() {
}
public DynamicSubject(Object obj) {
sub = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling " + method);
method.invoke(sub,args);
System.out.println("after calling " + method);
return null;
}
}
該代理類的內部屬性為Object類,實際使用時通過該類的構造函數DynamicSubject(Object obj)對其賦值;此外,在該類還實現了invoke方法,該方法中的
method.invoke(sub,args);
其實就是調用被代理對象的將要被執行的方法,方法參數sub是實際的被代理對象,args為執行被代理對象相應操作所需的參數。通過動態代理類,我們可以在調用之前或之后執行一些相關操作。
客戶端:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Client
{
static public void main(String[] args) throws Throwable
{
RealSubject rs = new RealSubject(); //在這里指定被代理類
InvocationHandler ds = new DynamicSubject(rs); //初始化代理類
Class cls = rs.getClass();
//以下是分解步驟
/*
Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces()) ;
Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});
Subject subject =(Subject) ct.newInstance(new Object[]{ds});
*/
//以下是一次性生成
Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(),ds );
subject.request();
}
通過這種方式,被代理的對象(RealSubject)可以在運行時動態改變,需要控制的接口(Subject接口)可以在運行時改變,控制的方式(DynamicSubject類)也可以動態改變,從而實現了非常靈活的動態代理關系(參見文獻2)。
參考文獻:
1. 閻宏,《Java 與模式》
2. 透明,《動態代理的前世今生》
3. Forest Hou,《Dynamic Proxy 在 Java RMI 中的應用》
在Java SE1.5中, 增加了一個新的特性:泛型(日本語中的總稱型)。何謂泛型呢?通俗的說,就是泛泛的指定對象所操作的類型,而不像常規方式一樣使用某種固定的類型去指定。
泛型的本質就是將所操作的數據類型參數化,也就是說,該數據類型被指定為一個參數。這種參數類型可以使用在類、接口以及方法定義中。
一、為什么使用泛型呢?
在以往的J2SE中,沒有泛型的情況下,通常是使用Object類型來進行多種類型數據的操作。這個時候操作最多的就是針對該Object進行數據的強制轉換,而這種轉換是基于開發者對該數據類型明確的情況下進行的(比如將Object型轉換為String型)。倘若類型不一致,編譯器在編譯過程中不會報錯,但在運行時會出錯。
使用泛型的好處在于,它在編譯的時候進行類型安全檢查,并且在運行時所有的轉換都是強制的,隱式的,大大提高了代碼的重用率。
二、
泛型的簡單例子:
首先,我們來看看下面兩個普通的class定義
public class
getString {
private String myStr;
public String getStr() {
return myStr;
}
public void setStr(str) {
myStr = str;
}
}
public class
getDouble {
private Double myDou;
public Double getDou() {
return myDou;
}
public void setDou(dou) {
myDou = dou;
}
}
這兩個class除了所操作的數據類型不一致,其他機能都是相同的。現在,我們可以使用泛型來將上面兩個class合并為一個,從而提高代碼利用率,減少代碼量。
public class getObj<T> {
private T myObj ;
public T getObj() {
return myObj;
}
public void setObj<T obj> {
myObj = obj;
}
}
那么,使用了泛型后,如何生成這個class的實例來進行操作呢?請看下面的代碼:
getObj<String> strObj = new getObj<String>();
strObj.setObj(“Hello Nissay”);
System.out.println(strObj.getObj());
getObj<Double> douObj = new getObj<Double>();
douObj.setObj(new Double(“116023”));
System.out.println(douObj.getObj());
三、例子分析
現在我們來分析上面那段藍色字體的代碼:
1、<T>是泛型的標記,當然可以使用別的名字,比如。使用<T>聲明一個泛型的引用,從而可以在class、方法及接口中使用它進行數據定義,參數傳遞。
2、<T>在聲明的時候相當于一個有意義的數據類型,編譯過程中不會發生錯誤;在實例化時,將其用一個具體的數據類型進行替代,從而就可以滿足不用需求。
四、泛型的規則和限制
通過上述的例子,我們簡單理解了泛型的含義。在使用泛型時,請注意其使用規則和限制,如下:
1、泛型的參數類型只能是類(class)類型,而不能是簡單類型。
比如,<int>是不可使用的。
2、可以聲明多個泛型參數類型,比如<T, P,Q…>,同時還可以嵌套泛型,例如:<List<String>>
3、泛型的參數類型可以使用extends語句,例如<T extends superclass>。
4、泛型的參數類型可以使用super語句,例如< T super childclass>。
5、泛型還可以使用通配符,例如<? extends
ArrayList>
五、
擴展
1、extends語句
使用extends語句將限制泛型參數的適用范圍。例如:
<T extends
collection> ,則表示該泛型參數的使用范圍是所有實現了collection接口的calss。如果傳入一個<String>則程序編譯出錯。
2、super語句
super語句的作用與extends一樣,都是限制泛型參數的適用范圍。區別在于,super是限制泛型參數只能是指定該class的上層父類。
例如<T super
List>,表示該泛型參數只能是List和List的上層父類。
3、通配符
使用通配符的目的是為了解決泛型參數被限制死了不能動態根據實例來確定的缺點。
舉個例子:public
class SampleClass < T extends S> {…}
假如A,B,C,…Z這26個class都實現了S接口。我們使用時需要使用到這26個class類型的泛型參數。那實例化的時候怎么辦呢?依次寫下
SampleClass<A>
a = new SampleClass();
SampleClass<B>
a = new SampleClass();
…
SampleClass<Z>
a = new SampleClass();
這顯然很冗余,還不如使用Object而不使用泛型,呵呵,是吧?
別著急,咱們使用通配符,就OK了。
SampleClass<?
Extends S> sc = new SampleClass();