JAVA反射

Java反射
一、概念
java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類中的所有屬性和方法;對於任意一個對象,都能調用它的任意一個方法和屬性;

二、通過Java反射查看類信息
每個類被加載後,系統就會爲改類生成一個對於的Class對象。通過該Class對象就可以訪問到JVM中的這個類。
獲得Class對象通常有如下三種方式
(1)使用Class類的froName(String classname)靜態方法。改方法需要傳入字符串參數,該字符串參數的值是某個類的全限定名(必須添加完成包名)
class1 = Class.forName(“com.glc.test.Teacher”);
(2)調用某個類的class屬性來獲取對應的Class對象。
class1 = Teacher.class;
(3)調用某個對象的getClass方法。
Teacher teacher = new Teacher();
Class<?> class1 = teacher.getClass();
2.1 獲取Class對象的成員變量

	Field[] allFields = class1.getDeclaredFields();		獲取到Class對象的所有屬性
	Field[] publicFields = class1.getFields();			獲取到Class對象的所有public屬性
	Field ageField = class1.getDeclaredField("age")		獲取Class對象的指定屬性
	Filed nameField = class1.getField("name")			獲取Class對象的指定public屬性

2.2 獲取Class對象構造函數

	Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//獲取class對象的所有聲明構造函數
	Constructor<?>[] publicConstructors = class1.getConstructors();//獲取class對象public構造函數
	Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//獲取指定聲明構造函數
	Constructor publicConstructor = class1.getConstructor(String.class);//獲取指定聲明的public構造函數

2.3 其他方法

	Annotation[] annotations = (Annotation[]) class1.getAnnotations();//獲取class對象的所有註解
	Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//獲取class對象指定註解
	Type genericSuperclass = class1.getGenericSuperclass();//獲取class對象的直接超類的 Type
	Type[] interfaceTypes = class1.getGenericInterfaces();//獲取class對象的所有接口的type集合

2.4 獲取Class對象的信息

	boolean isPrimitive = class1.isPrimitive();//判斷是否是基礎類型
	boolean isArray = class1.isArray();//判斷是否是集合類
	boolean isAnnotation = class1.isAnnotation();//判斷是否是註解類
	boolean isInterface = class1.isInterface();//判斷是否是接口類
	boolean isEnum = class1.isEnum();//判斷是否是枚舉類
	boolean isAnonymousClass = class1.isAnonymousClass();//判斷是否是匿名內部類
	boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判斷是否被某個註解類修飾
	String className = class1.getName();//獲取class名字 包含包名路徑
	Package aPackage = class1.getPackage();//獲取class的包信息
	String simpleName = class1.getSimpleName();//獲取class類名
	int modifiers = class1.getModifiers();//獲取class訪問權限
	Class<?>[] declaredClasses = class1.getDeclaredClasses();//內部類
	Class<?> declaringClass = class1.getDeclaringClass();//外部類

三、通過反射生成並操作對象
3.1 生成類的實例對象
1.使用Class對象的newInstance()方法來創建該Class對象對應類的實例。這種方式要求該Class對象的對應類有默認構造器,而執行newInstance()方法時實際上是利用默認構造器來創建該類的實例。
2.先使用Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建該Class對象對應類的實例。通過這種方式可以選擇使用指定的構造器來創建實例。

	//第一種方式 Class對象調用newInstance()方法生成
	
	Object obj = class1.newInstance();
	
	//第二種方式 對象獲得對應的Constructor對象,再通過該Constructor對象的newInstance()方法生成
	Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//獲取指定聲明構造函數
	obj = constructor.newInstance("hello");

3.2 調用類的方法
1.通過Class對象的getMethods()方法或者getMethod()方法獲得指定方法,返回Method數組或對象。
2.調用Method對象中的Object invoke(Object obj, Object… args)方法。第一個參數對應調用該方法的實例對象,第二個參數對應該方法的參數。
// 生成新的對象:用newInstance()方法
Object obj = class1.newInstance();
//首先需要獲得與該方法對應的Method對象
Method method = class1.getDeclaredMethod(“setAge”, int.class);
//調用指定的函數並傳遞參數
method.invoke(obj, 28);
當通過Method的invoke()方法來調用對應的方法時,Java會要求程序必須有調用該方法的權限。如果程序確實需要調用某個對象的private方法,則可以先調用Method對象的如下方法。
setAccessible(boolean flag):將Method對象的acessible設置爲指定的布爾值。值爲true,指示該Method在使用時應該取消Java語言的訪問權限檢查;值爲false,則知識該Method在使用時要實施Java語言的訪問權限檢查。
3.3 訪問成員變量值
1.通過Class對象的getFields()方法或者getField()方法獲得指定方法,返回Field數組或對象。
2.Field提供了兩組方法來讀取或設置成員變量的值:
getXXX(Object obj):獲取obj對象的該成員變量的值。此處的XXX對應8種基本類型。如果該成員變量的類型是引用類型,則取消get後面的XXX。

	setXXX(Object obj,XXX val):將obj對象的該成員變量設置成val值。
	//生成新的對象:用newInstance()方法 
	Object obj = class1.newInstance();
	//獲取age成員變量
	Field field = class1.getField("age");
	//將obj對象的age的值設置爲10
	field.setInt(obj, 10);
	//獲取obj對象的age的值
	field.getInt(obj);

四、代理模式
4.1 定義
給某個對象提供一個代理對象,並由代理對象控制對於原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
4.2 分類
靜態代理:代理類是在編譯時就實現好的。也就是說 Java 編譯完成後代理類是一個實際的 class 文件。
動態代理:代理類是在運行時生成的。也就是說 Java 編譯完之後並沒有實際的 class 文件,而是在運行時動態生成的類字節碼,並加載到JVM中。
4.3 代理模式的簡單實現

		public class ProxyDemo {
			public static void main(String args[]){
				RealSubject subject = new RealSubject();
				Proxy p = new Proxy(subject);
				p.request();
			}
		}
		interface Subject{
			void request();
		}
		class RealSubject implements Subject{
			public void request(){
				System.out.println("request");
			}
		}
		class Proxy implements Subject{
			private Subject subject;
			public Proxy(Subject subject){
				this.subject = subject;
			}
			public void request(){
				System.out.println("PreProcess");
				subject.request();
				System.out.println("PostProcess");
			}
		}

目標對象(RealSubject )以及代理對象(Proxy)都實現了主題接口(Subject)。在代理對象(Proxy)中,通過構造函數傳入目標對象(RealSubject ),然後重寫主題接口(Subject)的request()方法,在該方法中調用目標對象(RealSubject )的request()方法,並可以添加一些額外的處理工作在目標對象(RealSubject )的request()方法的前後。

五、反射機制與動態代理
5.1 動態代理
動態代理是指在運行時動態生成代理類。即,代理類的字節碼將在運行時生成並載入當前代理的 ClassLoader。與靜態處理類相比,動態類有諸多好處。
①不需要爲(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,爲每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標對象和代理類都要修改,不利於系統維護;
②使用一些動態代理的生成方法甚至可以在運行時制定代理類的執行邏輯,從而大大提升系統的靈活性。
5.2 動態代理涉及的主要類
java.lang.reflect.Proxy:這是生成代理類的主類,通過Proxy類生成的代理類都繼承了Proxy類。Proxy提供了用戶創建動態代理類和代理對象的靜態
方法,他是所有動態代理類的父類。
java.lang.reflect.invocationHandler:這裏稱他爲“調用處理器”,它是一個接口。當調用動態代理類中的方法時,將會直接轉接到執行自定義的
InvocationHandler中的invoke()方法。即我們動態生成的代理類需要完成的具體內容需要自己定義一個類,而這個類必須實現InvocationHandler接口
,通過重寫invoke()方法來執行具體內容。
Proxy提供瞭如下兩個方法來創建動態代理類和動態代理實例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces) 返回代理類的java.lang.Class對象。第一個參數是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區),第二個參數是接口(表明你這個代理類需要實現哪些接口),第三個參數是調用處理器類實例(指定代理類中具體要幹什麼),該代理類將實現interfaces所指定的所有接口,執行代理對象的每個方法時都會被替換執行InvocationHandler對象的invoke方法。

			static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理類實例。參數與上述方法一致。
			//創建一個InvocationHandler對象
			InvocationHandler handler = new MyInvocationHandler(.args..);
			//使用Proxy生成一個動態代理類
			Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
			//獲取proxyClass類中一個帶InvocationHandler參數的構造器
			Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
			//調用constructor的newInstance方法來創建動態實例
			RealSubject real = (RealSubject)constructor.newInstance(handler);
			//創建一個InvocationHandler對象
			InvocationHandler handler = new MyInvocationHandler(.args..);
			//使用Proxy直接生成一個動態代理對象
			RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
		newProxyInstance這個方法實際上做了兩件事:第一,創建了一個新的類【代理類】,這個類實現了Class[] interfaces中的所有接口,並通過你指定的ClassLoader將生成的類的字節碼加載到JVM中,創建Class對象;第二,以你傳入的InvocationHandler作爲參數創建一個代理類的實例並返回。
		Proxy 類還有一些靜態方法,比如:
			InvocationHandler getInvocationHandler(Object proxy):獲得代理對象對應的調用處理器對象。
			Class getProxyClass(ClassLoader loader, Class[] interfaces):根據類加載器和實現的接口獲得代理類。
		InvocationHandler 接口中有方法:
			invoke(Object proxy, Method method, Object[] args)

這個函數是在代理對象調用任何一個方法時都會調用的,方法不同會導致第二個參數method不同,第一個參數是代理對象(表示哪個代理對象調用了method方法),第二個參數是 Method 對象(表示哪個方法被調用了),第三個參數是指定調用方法的參數。
5.3 動態代理模式的簡單實現

	public class DynamicProxyDemo {
		public static void main(String[] args) {
			//1.創建目標對象
			RealSubject realSubject = new RealSubject();    
			//2.創建調用處理器對象
			ProxyHandler handler = new ProxyHandler(realSubject);    
		   //3.動態生成代理對象
			Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
															RealSubject.class.getInterfaces(), handler);   
			//4.通過代理對象調用方法   
			proxySubject.request();    
		}
	}
	/**
	 * 主題接口
	 */
	interface Subject{
		void request();
	}
	/**
	 * 目標對象類
	 */
	class RealSubject implements Subject{
		public void request(){
			System.out.println("====RealSubject Request====");
		}
	}
	/**
	 * 代理類的調用處理器
	 */
	class ProxyHandler implements InvocationHandler{
		private Subject subject;
		public ProxyHandler(Subject subject){
			this.subject = subject;
		}
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			//定義預處理的工作,當然你也可以根據 method 的不同進行不同的預處理工作
			System.out.println("====before====");
		   //調用RealSubject中的方法
			Object result = method.invoke(subject, args);
			System.out.println("====after====");
			return result;
		}
	}

可以看到,我們通過newProxyInstance就產生了一個Subject 的實例,即代理類的實例,然後就可以通過Subject .request(),就會調用InvocationHandler中的invoke()方法,傳入方法Method對象,以及調用方法的參數,通過Method.invoke調用RealSubject中的方法的request()方法。同時可以在InvocationHandler中的invoke()方法加入其他執行邏輯。

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