1. 程式人生 > >Java高階之反射

Java高階之反射

反射

1 為什麼學習反射

        剛開始接觸反射,是不是覺得很高大上,事實上也確實如此。筆者基本也是通過看視訊和查閱資料學習的,到現在也才是剛剛入門而已。         如果世界沒有反射,即使有陽光,有色彩,那麼我們還是不能看到這個美麗的世界。反射在Java中的地位也是這樣的重要,如果Java沒有反射,那麼Java現在的地位是非常堪憂的的,因為他很多技術都是基於反射來實現的         初學者一開始就接觸Java反射,那是很難接收和理解的。一開始,筆者在學習Java的時候,自動就跳過了反射這一章節,因為在實際寫程式碼的過程中,看起來我們幾乎不使用到反射的知識。那不好意思,你的想法從一開始就錯了,以為你一開始接觸到的Eclipse、Myeclipse和Idea本身就使用到了Java反射的知識
,除非你一直是用純文字寫的。
        反射雖然我們寫的不多,但是卻時時刻刻都在使用,比如框架、資料庫程式設計、JSP和Servlet等等都是人家已經封裝好的。既然是人家已經封裝好得程式碼,我們為什麼要去學他呢?這就相當於:導航不是有美國的GPS嗎,為什麼還要造個北斗衛星呢。所以說,學習,不能只學表面的東西,更應該要深入瞭解。

2 反射是什麼

反射的概念用官方的話來解釋太麻煩了,通俗來講,反射就是來操作Java類的一種機制,通過反射可以操作Java中的位元組碼檔案         大家接觸過Java的都知道,Java最終執行的是位元組碼檔案,而不是.java檔案。        反射的核心類是Class類
,它是類的類模板,即該類是用來描述Java類的。是不是很繞口,我們知道可以用類來描述事物,那麼是不是可以用類來描述類呢。描述的基本上就是一個類的組成部分:包名、類名、介面、父類、成員變數(Filed)、構造方法(Construct)、普通方法(Method)、註解(Annotation)等等。
        所以說,反射的操作基本上就是對這些類的元素進行操作

3 反射的基本操作

獲取Class位元組碼物件  Class類是用來操作位元組碼檔案的,那麼第一步就是如何獲取位元組碼檔案。前面也說過,Class是類的類模板,所以說它不屬於任何一個物件,只是一個類的模板。         獲取位元組碼物件有三種方式:類.class、Class.forName(路徑)、物件.getClass()。千萬要記住,獲取的是類的模板,所以只有一份
。三者輸出的結果是一樣的。
System.out.println("物件.getClass()獲取位元組碼檔案的地址:" + student.getClass().hashCode());
System.out.println("Class.forName(路徑)獲取位元組碼檔案的地址:" + Class.forName("base.Student").hashCode());
System.out.println("類名.class獲取位元組碼檔案的地址:" + Student.class.hashCode());
獲取類實現的介面和類的父類位元組碼物件
我們都知道Java是單繼承多實現,所以獲取介面的時候是獲取介面陣列。
System.out.println("getSuperclass()方法獲取父類的類模板的地址:" + Student.class.getSuperclass().hashCode());
System.out.println("getInterfaces()獲取的介面陣列,其長度為:" + Student.class.getInterfaces().length);
獲取類的構造方法並建立一個物件 我們剛開始接觸Java的封裝思想時,知道可以用new關鍵字建立物件,而且可以有預設的無參即有參構造方法。new的實質實現就是用Java反射來實現的,即獲取構造方法和建立物件兩個步驟,當然構造方法有時候Java虛擬機器會自動幫我們建立無參的預設構造方法。          這裡就會涉及到過載的問題了,因為建構函式在一個類中可以有多個,既可以獲取所有的構造方法陣列,也可以獲取某一個指定的構造方法。
//預設無參構造方法
Object object = clazz.newInstance();
Constructor con2 = clazz.getConstructor(String.class, String.class, String.class, Integer.class);
Object object2 = con2.newInstance("Jack", "男", "大二", 22);
通過位元組碼物件建立類的一個物件並執行其中的方法
方法的執行我們通常是用“.”來呼叫某個物件的方法的,其實也是通過Java反射來實現的。          方法都有過載,所以我們可以獲取方法陣列或者是某一個特定的方法。首先是獲取方法模板,然後指定該方法時屬於哪個物件的。
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
    Method method = methods[i];
    System.out.println(method.getName());
}
Method setAge = clazz.getMethod("setAge", Integer.class, String.class);
setAge.invoke(object2, 23, "Mary");

4 反射的使用小案例

如果接觸過Java資料庫程式設計,那麼應該都知道需要載入資料庫驅動,這一步就是用到了反射,而如果接觸過Spring的人來說,對於IOC也不是很陌生了。         IOC,不理解沒關係,這裡簡單說說就明白了。IOC說通俗點,A類中需要B類,那麼IOC就是將B類的建立交給第三方來建立,然後再注入到A類中,這樣就不需要在A類中用new關鍵字來直接建立物件了。這個第三方一般是通過配置檔案來設定。         配置檔案.properties的內容存放類的路徑:
##key=value##
className=java.util.ArrayList
       通過Java的IO流來讀取配置檔案的內容,然後動態地建立物件。這裡使用到Java自帶的一種類Properties類,專門用來處理.properties檔案中的鍵和值。
public static void main(String[] args) {
		InputStream in = null;
		try {
			//讀取檔案
			in = new FileInputStream("config/config-ioc.properties");
			//獲取值
			Properties properties = new Properties();
			properties.load(in);
			String value = properties.getProperty("className");
			System.out.println("值:" + value);
			//反射建立物件
			Class clazz = Class.forName(value);
			ArrayList list = (ArrayList) clazz.newInstance();
			list.add("Jack");
			list.add("Rose");
			for (int i = 0; i < list.size(); i++) {
				System.out.println(list.get(i));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}