Java反射機制

談到Spring時大家都會想到它的核心原理是IOC/DI,其實Spring實現IOC/DI的背後依靠的是Java反射機制。不僅Spring使用反射機制,Hibernate的ORM框架也是使用反射機制實現的,AOP動態代理也是大量使用反射實現的,所以Java反射機制其實已經被大量應用在我們的程序中,只是平時我們做業務應用開發時直接使用Java反射的機會比較少,因爲我們不需要重複發明輪子,大部分應用的開發都會使用一些像Spring這樣成熟的框架,開發人員只需要提供框架配置文件即可。框架讓我們可以更專注業務邏輯開發,但對於提高架構設計能力,我們還是需要掌握框架背後的實現,說不定將來我們需要開發自己的框架或者定製開源框架。


Reflection,這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱爲introspection(內省、內觀、反省)。Reflection和introspection是常被並提的兩個術語。

反射被大量使用肯定有其優點,從上面的定義可以看出反射的優點就是動態靈活,一切都是在運行時期根據具體情況(配置)決定創建哪種對象、調用哪個方法等。這種靈活性可以方便我們設計解耦,這也是Spring做得最好的事情之一,所以我們可以很方便的替換某些class文件而不需要重新編譯相關類。事物都是兩面的,反射當然也有缺點,對於編譯期能確定的事情,編譯器可以或多或少做些優化,而運行期就像解釋執行一樣,編譯器一般沒法進行優化,所以使用反射性能要打點折扣;另外反射破壞類的封裝性,通過反射我們可以訪問類的任何成員,包括private成員,這違反了面向對象設計原則。


java.lang.Class類:反射中最常出現的類,它跟其它JAVA類沒什麼兩樣只是名字恰巧叫Class,都是繼承於Object類,其實它就是JAVA類的抽象,用來描述類的元數據,比如每個類都有類名、裝載器、哈希等,這些都是Class類的屬性。Class沒有public的構造函數,當Java虛擬機載入一個類的時候,它就會自動創建一個Class類的實例來表示這個類,比如java.lang.String,我們可以通過String.class來獲取對應的Class類實例;或者我們也可以通過Class.forName("類全名")來獲取一個Class對象。


反射常用的一些方法:

Class c = Class.forName("className"); //className必須爲全名 
Object obj = c.newInstance();//創建實例 
 
Constructor getConstructor(Class[] params);//根據指定參數獲得public構造函數

Constructor[] getConstructors();//獲得所有public構造函數

Method getMethod(String name, Class[] params); //根據方法名,參數類型獲得public方法

Method[] getMethods();//獲得所有的public方法

Field getField(String name);//根據變量名得到相應的public變量

Field[] getFields();//獲得類中所以public的變量


代碼示例:

package com.stevex.app.forkjoin;

import java.math.BigDecimal;

public class Account implements java.io.Serializable{
	public static final long serialVersionUID = -6928173218630110362L;
	private String id;
	private String clientId;
	private BigDecimal balance;

	public Account(){
		//default constructor
	}
	
	public Account(String id, String clientId) {
		this.id = id;
		this.clientId = clientId;
		balance = BigDecimal.ZERO;
	}

	public synchronized void withdraw(String clientId, BigDecimal amount){
		validateClient(clientId);
		
		if(amount.compareTo(balance) < 1){
			balance.subtract(amount);
		}
		else{
			throw new RuntimeException("餘額不足");
		}
	}
	
	private void validateClient(String clientId) {
		if(! this.clientId.equals(clientId)){
			throw new RuntimeException("非法客戶");
		}
	}

	public synchronized void deposit(BigDecimal amount){
		if(amount.compareTo(BigDecimal.ZERO) == 1){
			balance.add(amount);
		}
		else{
			throw new RuntimeException("不允許透支");
		}
	}
	
	public String getClientId() {
		return clientId;
	}
	
	public void setClientId(String clientId) {
		this.clientId = clientId;
	}
	
	public String getId() {
		return id;
	}
	
	public void setId(String id) {
		this.id = id;
	}
}


package com.stevex.app.forkjoin;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionTest {

	public static void main(String[] args) throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
		Class c = Class.forName("com.stevex.app.forkjoin.Account");
		Account account = (Account) c.newInstance();

		Constructor[] cons = c.getConstructors();
		Method[] methods = c.getMethods();
		Field[] fields = c.getFields();
		
		for(Constructor con : cons){
			System.out.println(con);
		}
		
		for(Method m : methods){
			System.out.println(m);
		}
		
		for(Field f : fields){
			System.out.println(f);
		}
	}

}


發現輸出結果蠻有趣的,就連父類Object中定義的public成員都輸出了。

public com.stevex.app.forkjoin.Account()
public com.stevex.app.forkjoin.Account(java.lang.String,java.lang.String)
public java.lang.String com.stevex.app.forkjoin.Account.getId()
public synchronized void com.stevex.app.forkjoin.Account.withdraw(java.lang.String,java.math.BigDecimal)
public synchronized void com.stevex.app.forkjoin.Account.deposit(java.math.BigDecimal)
public java.lang.String com.stevex.app.forkjoin.Account.getClientId()
public void com.stevex.app.forkjoin.Account.setClientId(java.lang.String)
public void com.stevex.app.forkjoin.Account.setId(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public static final long com.stevex.app.forkjoin.Account.serialVersionUID


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