Java review--反射

ZeroWM發表於2016-08-04

為什麼要使用反射?

物件在執行的時候有兩種型別:編譯時型別和執行時型別。執行時我們可能無法知道物件和類,程式只能靠執行時資訊來發現該物件和類的真實資訊,這就需要用到反射。 


獲得Class物件

每個類被載入的時候,系統會為它生成一個對應的Class物件,通過Class物件,就可以訪問到JVM的這個類。Java中獲取Class物件通常採用3種方式:

1.使用Class類的forName(String clazzName)靜態方法。

2.呼叫某個類的.class屬性獲取當前的Class物件。

3.呼叫某個物件的getClass()方法。

第一種方式和第二種方式都是直接根據類來取得該類的Class物件,對比一下,第二種優勢:程式碼更安全(編譯階段可以檢查Class物件是否存在),程式效能好(不用調方法)。


從Class中獲取資訊

Class提供的功能非常豐富,它可以獲取該類包含的構造器、方法、內部類、屬性資訊等。


使用反射操作物件


物件的建立

反射生成物件有兩種方式:

1.使用Class物件的NewInstance()方法來建立類的例項。要求:類有預設構造器。

2.使用Class物件獲取指定的Constructor物件(getConstructor()方法),再呼叫Constructor物件的newInstance()方法來建立Class物件對應的例項。


呼叫方法

獲取該類對應的Class物件後,通過Class物件的getMethods()方法或者getMethod()方法來獲取全部方法。每個Method裡面有一個invoke方法Object invoke(Object obj,Object...args):obj是執行方法的主調,後面的args是執行方法時傳入方法的實參。


訪問屬性

通過Class物件的getFields()或者getField()獲取該類全部的或者指定的Field。


反射和JDK動態代理

JDK動態代理只為介面建立動態代理,下面有一個動態代理的demo:

Test.java

<span style="font-size:14px;">public class Test {
	public static void main(String[] args) {
		Dog target =new GunDog();
		Dog dog =(Dog)MyProxyFactory.getProxy(target);
		dog.info();
		dog.run();
	}
}
   </span>

Dog.java
<span style="font-size:14px;">public interface Dog {
	
	 void info();
	 void run();
}
</span>

GunDog.java

<span style="font-size:14px;">public class GunDog implements Dog {
	public void info(){
		System.out.println("我是一個只獵狗");
	}
	public void run(){
		System.out.println("我會跑");
	}
}
</span>

MyProxyFactory.java
<span style="font-size:14px;">import java.lang.annotation.Target;
import java.lang.reflect.Proxy;


public class MyProxyFactory {
	public static Object getProxy(Object target){
		//建立一個MyInvokationHandler物件
		MyInvocationHandler handler =new MyInvocationHandler();
		handler.setTarget( target);
		//建立一個動態代理
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
	}
}
</span>

MyInvocationHandler.java
<span style="font-size:14px;">import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.omg.CORBA.PRIVATE_MEMBER;


public class MyInvocationHandler implements InvocationHandler {

	private Object target;
	
	public void setTarget(Object target){
		this.target =target;
	}

	//執行動態代理物件的所有方法時,都會被替代成執行如下的invoke方法
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		DogUtil du=new DogUtil();
		//執行DogUtil 物件中的method1方法
		du.method1();
		//以Target為主調來執行method方法,回撥了target的原有方法
		Object result=method.invoke(target, args); 
		du.method2();
		return result;

		
	}

}
</span>


執行結果:



  Proxy和InvocationHandler就可以實現,當程式呼叫info()方法和run()方法時,系統可以自動的將method1()和method2()兩個通用方法插入info()和run()方法中執行。

  MyInvocationHandler類,該實現類的invoke()方法將會作為代理物件的方法實現。

       MyProxyFactory動態代理工廠類提供了一個getProxy方法, 該方法為target物件生成一個動態代理物件,這個動態代理物件與target實現了同樣的介面。當呼叫動態代理物件指定的方法的時候,實際將變成執行MyInvocationHandler物件的invoke方法。

      從這個例子中可以發現,動態代理十分的靈活,代理類不僅僅可以實現被代理類要實現的介面,而且可以動態的擴充套件一些公共的方法,在原實現的前,後。









相關文章