反射

1.Java 反射:

Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法

2.Java 反射的作用:

1)在運行時判斷任意一個對象所屬的類
2)在運行時構造任意一個類的對象
3)在運行時判斷任意一個類所具有的成員變量和方法
4)在運行時調用任意一個對象的成員變量和方法
5)生成動態代理

3.反射的API

java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor:代表類的構造方法

實例:

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.junit.Test;

public class TestReflection {

	@Test
	public void testClass() throws Exception{
		Class<Person> clazz = Person.class;
		
		/**
		 * 創建對象
		 */
		//1.創建運行時類Person的對象:newInstance()
		Person person = clazz.newInstance();
		
		/**
		 * 屬性操作
		 */
		//1.獲取public屬性名:getFields()
		Field[] fields = clazz.getFields();
		for(int i = 0;i < fields.length;i++){
			System.out.println("###" + fields[i].getName());
		}
		//2.獲取全部屬性名:getDeclaredFields()
		Field[] fields1 = clazz.getDeclaredFields();
		for(int i = 0;i < fields1.length;i++){
			System.out.println("!!!" + fields1[i].getName());
		}
		
		//3.獲取public屬性:getField(String fieldname)
		Field field = clazz.getField("name");
		//通過set方法,爲對應的類的對應屬性賦值
		field.set(person, "LiuDeHua");
		System.out.println("name : " + person.getName());
		
		//4.獲取非public屬性:getgetDeclaredField(String fieldName)
		Field field2 = clazz.getDeclaredField("age");
		//通過setAccessible方法,改變private屬性的封裝性
		field2.setAccessible(true);
		field2.set(person, 40);
		System.out.println("age : " + person.getAge());
		
		/**
		 * 方法操作
		 */
		//1.獲取運行時類方法的名稱:getDeclatedMethods()  getMethods()
		Method[] methods = clazz.getDeclaredMethods();
		for(int i = 0;i < methods.length;i++){
			System.out.println("@@@" + methods[i].getName());
		}
		
		//2.獲取運行時類指定的方法:getMethod(String methodName,Class...paramtype)
		Method method = clazz.getMethod("show");
		method.invoke(person);
		
		Method method2 = clazz.getMethod("display",String.class,int.class);
		method2.invoke(person, "xiaoming",10);
	}
}

4.java.lang.Class:是反射的源頭

1)public final Class getClass():在Object類中定義了該的方法,此方法將被所有子類繼承。該方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱

2)生成Class實例之後,我們可以進行的操作

(1)創建對應運行時類對象

(2)獲取對應運行時類的完整結構(屬性、方法、構造器,父類,所在的包,註解,異常等等)

(3)調用對應的運行時類指定的結構(屬性,方法,構造器)

(4)反射的應用:動態代理

3)如何獲取Class實例(3種)

	/**
	 * 獲取Class實例方法
	 * @throws Exception
	 */
	@Test
	public void testGetClassInstance() throws Exception{
		//1.調用運行時類本身的.class屬性
		Class clazz = Person.class;
		System.out.println(clazz.getName());
		
		//2.通過運行時類的對象獲取
		Person person = new Person();
		Class clazz1 = person.getClass();
		System.out.println(clazz1.getName());
		
		//3.通過Class靜態方法forName(String classpath)
		Class clazz2 = Class.forName("reflection.Person");
		System.out.println(clazz2.getName());
		
		//4.通過類的加載器:
		ClassLoader classLoader = this.getClass().getClassLoader();
		Class clazz3 = classLoader.loadClass("reflection.Person");
		System.out.println(clazz3.getName());
	}

4)Class類的常用方法


5)ClassLoader

	/**
	 * ClassLoader獲取輸入流
	 * @throws Exception 
	 */
	@Test
	public void testClassLoeader() throws Exception{
		ClassLoader classLoader = this.getClass().getClassLoader();
		//通過getResourceStream(String filepath)可以得到一個輸入流
		InputStream inputStream = classLoader.getResourceAsStream("classloader.txt");
		FileOutputStream fos = new FileOutputStream("classloader1.txt");
		byte[] b = new byte[10];
		int len;
		while((len = inputStream.read(b)) != -1){
			fos.write(b,0,len);
		}
	}
5.創建類對象:調用Class對象的newInstance()方法       要  求:1)類必須有一個無參數的構造器。2)類的構造器的訪問權限需要足夠。

	/**
	 * 創建運行時類對象,使用newInstance(),實際上就是調用了運行時類的空參構造器
	 * 要求:1)對應的運行時類要有空參構造器
	 *      2)構造器的權限要足夠
	 * @throws Exception 
	 */
	@Test
	public void testNewInstance() throws Exception{
		String className = "reflection.Person";
		Class clazz = Class.forName(className);
		Person person = (Person)clazz.newInstance();
		System.out.println(person);
	}
6.通過反射調用類的完整結構

1)獲取屬性

	/**
	 * 獲取屬性相關信息
	 * @throws SecurityException 
	 * @throws Exception 
	 */
	@Test
	public void testGetFieldInfo() throws Exception{<span style="background-color: rgb(255, 153, 0);">
</span>		Class clazz = Person.class;
		
		//1.獲取public屬性名:getFields()
		Field[] fields = clazz.getFields();

		//2.獲取全部屬性名:getDeclaredFields()
		Field[] fields1 = clazz.getDeclaredFields();
		
		//3.獲取public屬性:getField(String fieldname)
		Field field = clazz.getField("name");
		
		//4.獲取非public屬性:getgetDeclaredField(String fieldName)
		Field field2 = clazz.getDeclaredField("age");
		
		//5.獲取屬性的權限符  變量類型  變量名稱
		for(Field f : fields1){
			//1)權限符 
			int i = f.getModifiers();
			String name = Modifier.toString(i);
			System.out.println(name);
			//2)變量類型
			Class type = f.getType();
			System.out.println(type.getName());
			//3)變量名稱
			String methodName = f.getName();
			System.out.println(methodName);
		}
	}
2)獲取方法

	/**
	 * 獲取方法相關屬性
	 * @throws SecurityException 
	 * @throws Exception 
	 */
	@Test
	public void testGetMethodInfo() throws Exception{
		Class clazz = Person.class;
		
		//1.getMethods():返回運行時類及其父類中所有public的方法
		Method[] methods = clazz.getMethods();
		for(Method m : methods){
			System.err.println(m);
		}
		
		//getDeclatedMethods():返回運行時類自己本身聲明的所有方法
		Method[] methods2 = clazz.getDeclaredMethods();
		for(Method m : methods2){
			System.out.println(m);
		}
		
		//2.獲取運行時類指定的方法:getMethod(String methodName,Class...paramtype)
		Method method = clazz.getMethod("show");
		Method method2 = clazz.getMethod("display",String.class,int.class);
	}
3)獲取父類泛型

	/**
	 * 獲取父類的泛型:JDBC中會用到
	 * @throws SecurityException 
	 * @throws Exception 
	 */
	@Test
	public void testGenericType() throws Exception{
		Class clazzClass = Person.class;
		Type type = clazzClass.getGenericSuperclass();
		ParameterizedType paraType = (ParameterizedType)type;
		Type[] types = paraType.getActualTypeArguments();
		for(Type t : types){
			System.out.println(((Class)t).getName());
		}
	}

7.通過反射調用類中指定方法和屬性

1)調用指定屬性

public Field getField(String name) 返回此Class對象表示的類或接口的指定的public的Field。

public Field getDeclaredField(String name)返回此Class對象表示的類或接口的指定的Field。

public Object get(Object obj) 取得指定對象obj上此Field的屬性內容

public void set(Object obj,Object value) 設置指定對象obj上此Field的屬性

public void setAccessible(true)訪問私有屬性時,讓這個屬性可見

	/**
	 * 調用指定屬性 
	 */
	@Test
	public void test() throws Exception{
		Class clazz = Person.class;
		//1.創建Person對象
		Person person = (Person)clazz.newInstance();
		//2.爲public屬性賦值並獲取
		Field field = clazz.getField("name");
		field.set(person, "xiaoming");
		field.get(person);
		System.out.println(field.get(person));
		//3.爲非public屬性賦值並獲取
		Field field2 = clazz.getDeclaredField("age");
		field2.setAccessible(true);
		field2.set(person, 10);
		field2.get(person);
		System.out.println(field2.get(person));
	}	
2)調用指定方法

通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。

之後使用Object invoke(Object obj, Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息。

	/**
	 * 調用指定方法 
	 */
	@Test
	public void test2() throws Exception{
		Class clazz = Person.class;
		Person person = (Person)clazz.newInstance();
		
		Method method = clazz.getMethod("show");
		Object returnVal = method.invoke(person);
		System.out.println("show's returnVal : " + returnVal);
		
		Method method2 = clazz.getMethod("toString");
		Object returnVal2 = method2.invoke(person);
		System.out.println("toString's returnVal : " + returnVal2);
		
		Method method3 = clazz.getMethod("display", String.class,int.class);
		Object returnVal3 = method3.invoke(person, "xiaohong",10);
		System.out.println("display's returnVal : " + returnVal3);
	}
8.動態代理
1)動態代理是指客戶通過代理類來調用其它對象的方法,並且是在程序運行時根據需要動態創建目標類的代理對象。

2)代理設計模式的原理:使用一個代理將對象包裝起來, 然後用該代理對象取代原始對象. 任何對原始對象的調用都要通過代理. 代理對象決定是否以及何時將方法調用轉到原始對象上

3)代理類的作用:

(1)授權機制: 不同級別的用戶對同一對象擁有不同的訪問權利,如Jive論壇系統中,就使用Proxy進行授權機制控制,訪問論壇有兩種人:註冊用戶和遊客(未註冊用戶),Jive中就通過類似ForumProxy這樣的代理來控制這兩種用戶對論壇的訪問權限.

(2)某個客戶端不能直接操作到某個對象,但又必須和那個對象有所互動。舉例兩個具體情況:如果那個對象是一個是很大的圖片,需要花費很長時間才能顯示出來,那麼當這個圖片包含在文檔中時,使用編輯器或瀏覽器打開這個文檔,打開文檔必須很迅速,不能等待大圖片處理完成,這時需要做個圖片Proxy來代替真正的圖片。如果那個對象在Internet的某個遠端服務器上,直接操作這個對象因爲網絡速度原因可能比較慢,那我們可以先用Proxy來代替那個對象。總之原則是,對於開銷很大的對象,只有在使用它時才創建,這個原則可以爲我們節省很多寶貴的Java內存. 所以,有些人認爲Java耗費資源內存,我以爲這和程序編制思路也有一定的關係。

(3)現實中,Proxy應用範圍很廣,現在流行的分佈計算方式RMI和Corba等都是Proxy模式的應用


靜態代理--接口

interface ClothFactory{
	public void productCloth();
}
靜態代理--被代理類

class NikeClothFactory implements ClothFactory{

	@Override
	public void productCloth() {
		System.out.println("生產一批Nike衣服");
	}
}

class ADDSClothFactory implements ClothFactory{

	@Override
	public void productCloth() {
		System.out.println("生產一批ADDS衣服");
	}
	
}
靜態代理--代理類

class Proxy implements ClothFactory{
<span style="white-space:pre">	</span>private ClothFactory cf;
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>public Proxy(ClothFactory cf) {
<span style="white-space:pre">		</span>this.cf = cf;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void productCloth() {
<span style="white-space:pre">		</span>System.out.println("Proxy fee is $2000");
<span style="white-space:pre">		</span>cf.productCloth();
<span style="white-space:pre">	</span>}
}
靜態代理--實體類

public class StaticProxy {
	
	@Test
	public void test(){
		Proxy proxy = new Proxy(new NikeClothFactory());
		proxy.productCloth();
		
		Proxy proxy2 = new Proxy(new ADDSClothFactory());
		proxy2.productCloth();
	}
}

動態代理類:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.junit.Test;

interface Subject {
	void action();
}

// 被代理類
class RealSubject implements Subject {

	@Override
	public void action() {
		System.out.println("real subject ...");
	}
}

/**
 * 動態代理類: 1)實現InvocationHandler接口 2)調用java.lang.Reflect.Proxy類中的靜態方法:
 * newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
 * InvocationHandler,h) ①.loader:與被代理類的類加載器一致 ②.interfaces:與被代理類實現的接口一致
 * ③.h:實現了InvocationHandler接口的類對象,通常爲this
 */
class MyInvocationHandler implements InvocationHandler {
	// 實現了接口的被代理類的對象聲明(這裏指RealSubject類對象)
	Object object;

	/**
	 * 作用: 1.給被代理類對象實例化:就是給上面的Object對象實例化(RealSubject)
	 * 2.返回一個代理類對象:返回MyInvocationHandler對象
	 * 
	 * @param obj
	 * @return 返回MyInvocationHandler對象
	 */
	public Object blind(Object obj) {
		this.object = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);
	}

	/**
	 * 當代理對象發起對重寫方法的調用時,都會轉爲對如下invoke方法的調用
	 * 
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		method.invoke(object, args);
		return null;
	}

}

public class DynamicProxy {
	@Test
	public void test() {
		// 創建被代理類RealSubject對象
		RealSubject realSubject = new RealSubject();
		// 創建實現了InvocationHandler接口的類對象
		MyInvocationHandler handler = new MyInvocationHandler();
		// 調用blind(realSubject)方法,動態的返回一個同樣實現了被代理(RealSubject)
		// 所實現接口的代理類(MyInvocationHandler)對象
		Object obj = handler.blind(realSubject);
		Subject subObject = (Subject)obj;
		//轉到實現了InvocationHandler接口的類對象的invoke()方法
		subObject.action();
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章