深入Java反射機制

目錄

 

1 Java反射機制

1.1 RTTI

1.2 Java反射機制是啥

1.2.1 類Class

1.2.2 Object類

1.2.3 利用Class類來創建實例

2 Java靜態代理

2.1 代理模式一般涉及到的角色

2.2 靜態代理例子

2.3 靜態代理的優缺點

2.3.1 優點

2.3.2 缺點

3 Java動態代理

3.1 java.lang.reflect.Proxy

3.2 java.lang.reflect.InvocationHandler

3.3 動態代理實例

3.4 動態代理的優缺點


1 Java反射機制

獲取Java運行時的類型信息有兩種方法

1. RTTI(Run-Time Type Identification):運行時的類型的識別

2. Java反射機制

1.1 RTTI

Shape類,三個子類,Circle,Square,Triangle

有一個數組,要去保存Shape類型的對象,實際上存的都是它的子類對象。

當從數組中取出元素時,會自動將結果轉型回Shape。這是RTTI最基本的使用形式,因爲在Java中,所有的類型轉換都是在運行時進行正確的檢查的。

大部分代碼儘可能少地瞭解對象的具體類型,而至於對象家族中的一個通用表示打交道。如上述例子中的Shape。

1.2 Java反射機制是啥

運行狀態中,能知道任意一個類的所有屬性和方法;能調用任意一個對象的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲Java語言的反射機制。

1.2.1 類Class

程序運行時,java系統會一直對所有對象進行運行時類型識別,這項信息記錄了每個對象所屬的類。通過專門的類可以訪問這些信息。用來保存這些信息的類是Class類

Class類是Java一個基礎類,每裝載一個新類時,JVM就會在Java堆中,創建一個Class的實例,這個實例就代表這個Class類型,通過實例獲取類型信息。

Method[] 	getMethods()

Field[] 	getFields()

Constructor<?>[] 	getDeclaredConstructors()

1.2.2 Object類

clone()
Creates and returns a copy of this object.

boolean 	equals(Object obj)

protected void 	finalize()
Called by the garbage collector on an object 

int 	hashCode()

void 	notify()

void 	notifyAll()

String 	toString()

void 	wait()

1.2.3 利用Class類來創建實例

構造Class對象有3種方式

 Class.forName()

Class clazz;
try {
	clazz = Class.forName("java.lang.String");
	//clazz.newInstance() is deprecated
	Object obj = clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
	e.printStackTrace();
}

類.class

Class stringClass = String.class;
System.out.println(stringClass);

Object.getClass()

String str = "s";
System.out.println(str.getClass());

運行結果:class java.lang.String

2 Java靜態代理

某些情況下,一個客戶不想或不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。

代理模式的作用:爲其他對象提供一種代理以控制對這個對象的訪問。

2.1 代理模式一般涉及到的角色

抽象角色:聲明真實對象和代理對象的共同接口

代理角色:代理對象角色內含有對真實對象的引用

真實角色:代理角色所代表的真實對象,即最終要引用的對象

2.2 靜態代理例子

//真實對象和代理對象的共同接口
abstract class Subject {
	public abstract void request();
}

//真實角色
class RealSubject extends Subject{

	public void request() {
		System.out.println("From Real Subject!");
	}
}

//代理角色
class ProxySubject extends Subject{
	//代理對象內部含有對真實對象的引用
	private RealSubject realSubject;

	public void request() {
		//在真實角色操作之前所附加的操作
		preRequest();
		//真實角色所完成的事情
		if(null == realSubject)
			realSubject = new RealSubject();
		realSubject.request();
		//在真實角色操作之後所附加的操作
		postRequest();
	}
	
	private void preRequest() {
		System.out.println("Pre Request...");
	}
	
	private void postRequest() {
		System.out.println("Post Request...");
	}
}
public class Client {
	public static void main(String[] args) {
		Subject subject = new ProxySubject();
		subject.request();
	}
}

2.3 靜態代理的優缺點

2.3.1 優點

業務類只需關注業務邏輯本身,保證了業務類的重用性。

2.3.2 缺點

代理對象的一個接口只服務於一種類型的對象,若代理的方法很多,勢必要爲每一種方法都進行代理,靜態代理在程序規模稍大時就無法勝任了。

若接口增加一個方法,除了所有實現類要實現這個方法外,所有代理類也需實現此方法。增加了代碼維護的複雜度。

3 Java動態代理

3.1 java.lang.reflect.Proxy

Java動態代理機制的主類,提供了一組靜態方法來爲一組接口動態地生成代理類及其對象。

//Returns the invocation handler for the specified proxy instance
//獲取指定代理對象所關聯的調用處理器
static InvocationHandler 	getInvocationHandler(Object proxy)

//Returns the Class object for a proxy class given a class loader and an array of interfaces
//獲取關聯於指定類加載器和一組接口的動態代理類的類對象
static Class<?> 	getProxyClass(ClassLoader loader, Class[] interfaces)

//用於判斷指定類對象是否是一個動態代理類
static boolean 	isProxyClass(Class<?> cl)

//用於爲指定類加載器,一組接口及調用處理器生成動態代理類實例
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

3.2 java.lang.reflect.InvocationHandler

這是調用處理器接口,自定義了一個invoke()方法,用於集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委託類的代理訪問。

Object invoke(Object proxy, Method method, Object[] args)

該方法負責集中處理動態代理類上的所有方法調用。第一個參數是代理類實例,第二個參數是被調用的方法對象,第三個參數是調用參數

3.3 動態代理實例

interface Subject1 {
	public void request1();
}
public interface Subject2 {
	public void request2();
}
public class RealSubject implements Subject1,Subject2{

	public RealSubject() {}
	
	public void request1() {
		System.out.println("From real subject1.");
	}

	public void request2() {
		System.out.println("From real subject2.");
	}
}

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;
	}
}
public class Client {
	public static void main(String[] args) {
		
		RealSubject realSubject = new RealSubject();
		
		InvocationHandler dynamicSubject = new DynamicSubject(realSubject);

		Class clazz = realSubject.getClass();
		
		//生成動態代理實例
		Subject1 subject1 = 
				(Subject1)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), dynamicSubject);
		
		//自動調用dynamicSubject.invoke()
		subject1.request1();
		
		//只需改下接口就可以了
		Subject2 subject2 = 
				(Subject2)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), dynamicSubject);
		
		subject2.request2();
	}
}

3.4 動態代理的優缺點

深入學習Java的無聊日子


本文學習自

學堂在線-清華大學-許斌《JAVA程序設計進階》

 

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