Thinking In Java Part08(newInstance、instanceof、動態代理)

1、newInstance
	Class的newInstance()是實現“虛擬構造器”的一種途徑,虛擬構造器允許你聲明:我不知道你的確切類型,但是無論如何要正確地創建你自己。使用newInstance()來創建的類,必須帶有默認的構造器。
2、類字面常量
	一種爲Class.forname("xxx"),Java還提供了另一種方法生成對Class對象的引用,使用類字面常量。即Cookie.class
	當使用".class"來創建對Class對象的引用時,不會自動地初始化該Class對象。爲了使用類而做的準備工作實際包含三個步驟:
		2.1、加載,由類加載器執行的。該步驟將查找字節碼(通常在classpath所指定的路徑中查找,但這並非是必須的),並從這些字節碼中創建一個Class對象。
		2.2、鏈接。在鏈接階段將驗證類中的字節碼,爲靜態域分配存儲空間,並且如果必需的話,將解析這個類創建的對其他類的所有引用。
		2.3、初始化。如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。
		初始化被延遲到了對靜態方法(構造器隱式地是靜態的)或者非常數靜態域進行首次引用才執行。
3、instanceof與Class的等價性
	class Base {
	}

	class Derived extends Base {
	}

	public class InstanceofAndClass {
	    static void test(Object x) {
	       static void test(Object x) {
	        //base Derived
	        System.out.println("Testing x of type:" + x.getClass());
	        //true true
	        System.out.println("x instanceof Base:" + (x instanceof Base));
	        //false true
	        System.out.println("x instanceof Derived:" + (x instanceof Derived));
	        //true  true
	        System.out.println("Base.isInstance(x):" + Base.class.isInstance(x));
	        //false true
	        System.out.println("Derived.isInstance(x):" + Derived.class.isInstance(x));
	        //true false
	        System.out.println("x.getClass() ==Base.class:" + (x.getClass() == Base.class));
	        //false true
	        System.out.println("x.getClass() ==Derived.class:" + (x.getClass() == Derived.class));
	        //true false
	        System.out.println("x.getClass().equals(Base.class):" + (x.getClass().equals(Base.class)));
	        //false true
	        System.out.println("x.getClass().equals(Derived.class):" + (x.getClass().equals(Derived.class)));

    }

	    }

	    public static void main(String[] args) {
	        test(new Base());
	        test(new Derived());
	    }
	}

	instanceof和isInstance()生成的結果完全一樣,equals()和==也一樣。但是測試的結論卻不同。instanceof保持了類型的概念,指的是“你是這個類嗎,或者你是這個類的派生類嗎?”,而如果用==比較實際的Class對象,就沒有考慮繼承——它或者是這個確切的類型,或者不是。
4、反射,運行時的類信息
	如果不知道某個對象的確切類型,RTTI可以告訴你,但是需要這個類型在編譯時必須已知,這樣才能使用RTTI識別它,並利用這些信息做一些有用的事。也就是編譯時編譯器必須知道所有要通過RTTI來處理的類。
	如果從磁盤文件或者網絡連接中獲取了一串字節【RMI,允許一個Java程序將對象分佈到多態機器上】,並且被告知這些字節代表了一個類,我們怎麼去使用在編譯器爲我們的程序生成代碼之後很久纔出現的這個類?
	Class類和java.lang.reflect類庫一起對反射的概念進行了支持,該類庫包含了Field、Method、Constructor類(每個類都實現了Member接口)。這些類型的對象是由JVM在運行時創建的,用以表示未知類裏對應的成員。這樣就可以使用Constructor創建新的對象,用get和set方法讀取和修改和Field對象關聯的對象,用invoke方法調用與Method對象關聯的方法。另外,還可以調用getField、getMethods、getConstructors等便利的方法,以返回表示字段、方法以及構造器的對象的數組。這樣的話匿名對象的類信息就能在運行時被完全確定下來,而且在編譯時不需要知道任何事情。
	當通過反射和一個未知類型的對象打交道時,JVM只是簡單地檢查這個對象,看它屬於哪個特定的類(就像RTTI)。在用他做其他事情之前必須先加載那個類的CLass對象。RTTI和反射之間真正的區別只在於:對RTTI來說,編譯器在編譯時打開和檢查.class文件(我們可以用“普通”方式調用對象的所有方法)。對於反射機制來說,.class文件在編譯時是不可獲取的,所以在運行時打開和檢查.class文件。
5、動態代理
	代理是基本的設計模式之一,爲了提供額外的或不同的操作,而插入的用來代替“實際”對象的對象。代理通常充當着中間人的角色。
	interface Interface {
	    void doSomething();

	    void somethingElse(String arg);
	}

	class RealObject implements Interface {

	    @Override
	    public void doSomething() {
	        System.out.println("doSomething");
	    }

	    @Override
	    public void somethingElse(String arg) {
	        System.out.println("somethingElse" + arg);
	    }
	}

	class SimpleProxy  implements Interface{
	    private Interface proxied;
	    public SimpleProxy(Interface proxied){
	        this.proxied = proxied;
	    }
	    @Override
	    public void doSomething() {
	        System.out.println("SimpleProxy doSomething");
	        proxied.doSomething();
	    }

	    @Override
	    public void somethingElse(String arg) {
	        System.out.println("SimpleProxy somethingElse" + arg);
	        proxied.somethingElse(arg);
	    }
	}
	public class SimpleProxyDemo{
	    public static void consumer(Interface ifce){
	        ifce.doSomething();
	        ifce.somethingElse("boo");
	    }
	    public static void main(String[] args) {
	        consumer(new RealObject());
	        consumer(new SimpleProxy(new RealObject()));
	    }
	}
	因爲consumer()接受的是Interface,所以它無法知道正在獲得的倒地是RealObject還是Simpleproxy,因爲兩者都實現了Interface。但是SimpleProxy已經被插入到了客戶端和RealObject之間,因此她會執行操作,然後調用RealObject上相同的方法。
	任何時刻,你想要將額外的操作從“實際”對象中分離到不同的地方,特別是當你希望能夠很容易地做出修改,從沒有使用額外操作轉爲使用這些操作,或者反過來時,代理就很有用(設計模式的關鍵就是封裝修改)。代理使得我們可以很容易地添加或移除其他額外操作的代碼。
	Java動態代理可以動態地創建代理並動態地處理對所代理方法的調用。在動態代理上所做的所有調用都會被重定向到單一的調用處理器上,它的工作是揭示調用的類型並確定相應的對策。
	public class SimpleDynamicProxyDemo{
	    public static void consumer(Interface ifce){
	        ifce.doSomething();
	        ifce.somethingElse("boo");
	    }
	    public static void main(String[] args) {
	        RealObject realObject = new RealObject();
	        consumer(realObject);
	        Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(realObject));
	        consumer(proxy);
	    }
	}

	class DynamicProxyHandler implements InvocationHandler{
	    private Object proxied;
	    public DynamicProxyHandler(Object proxied){
	        this.proxied = proxied;
	    }
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	        System.out.println("proxy:"+proxy.getClass()+";method:"+method+";args:"+args);
	        if(args !=null){
	            for (Object arg:args){
	                System.out.println(" " +args);
	            }
	        }
	        return method.invoke(proxied,args);
	    }
	}
	通過調用靜態方法Proxy.newProxyInstance()可以創建動態代理,這個方法需要得到一個類加載器(你通常可以從已經被加載的對象中獲取其類加載器,然後傳遞給他),一個你希望該代理實現的接口列表(不能是類或抽象類),以及Invocationhandler接口的一個實現。動態代理可以將所有調用重定向到調用處理器,因此通常會向調用處理器的構造器傳遞一個“實際”對象的引用,從而使得調用處理器在執行其中介任務時,可以將請求換髮。
    invoke方法中傳遞進了代理對象,以防你需要區分請求的來源,但是在許多情況下,你並不關心這一點。然而,在invoke內部,在代理上調用方法時需要格外當心,因爲對接口的調用將被重定向爲對代理的調用。
	在invoke之前可以通過傳遞其他的參數,來過濾某些方法調用
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("interesting")) {
            System.out.println("Proxy detected the interesting method");
        }
        return method.invoke(proxied, args);
    }
第一個參數proxy是動態代理文件不能直接用於invoke,是用來區分來源,需要使用在新建時綁定的對象proxied。
因爲生成的動態代理文件 會自動繼承Proxy 所以jdk動態代理不能對類,只能對接口實現代理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章