黑馬程序員_反射

----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------

反射是java中的難點,同時也是學習javaweb,以及之後的3大框架的基礎。學習反射有助於對java源碼的理解,提升對面向對象的理解。反射就是把Java類中的各種成分映射成相應的java類。例如,一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示,就像汽車是一個類,汽車中的發動機,變速箱等等也是一個個的類。表示java類的Class類顯然要提供一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是Field、Method、Contructor、Package等等。

首先介紹下如何創建對象。

1、創建實例對象:不可用new Class()的方式,因爲Class沒有這樣的構造方法。而是將字節碼對象賦值給Class變量。如Class c1 =Person.class。如Person類,它的字節碼:首先要將Person的java文件編譯爲class文件放於硬盤上,即爲二進制代碼,再將這些代碼加載到內存中,接着用它創建一個個對象。就是把類的字節碼加載進內存中,再用此字節碼創建一個個對象。當有如Person、Math、Date等等的類,那麼這些字節碼就是分別的一個Class對象。即Class c2 =Date.class;。
2、獲得類的字節碼對象:如Class.forName(”java.lang.String”)即獲得String.class。得到這個字節碼對象有兩種情況:
1)此類已經加載進內存:若要得到此類字節碼,不需要再加載。
2)此類還未加載進內存:類加載器加載此類後,將字節碼緩存起來,forName()方法返回加載進來的字節碼。
3、得到各字節碼對應的實例對象(Class類型)的方式:
1)類名.class:如System.class,String.class等等
2)對象.class:如new Date().getClass()或者d.getClass()。(Date d = new Date())
3)Class.forName(“類名”):如Class.forName(”java.lang.String”)
當獲取類名的時候,是不知道此類的名稱的,forName(字符串參數)方法中傳入字符串型的變量作爲對外訪問的入口,即傳入什麼類名就獲得什麼類名,從而得知相應的類名。

Constructor類
1、概述:Constructor代表某個類的構造方法
2、獲取構造方法:
1)如何得到摸個類的所有構造方法:如得到String類的所有構造方法
Constructor[] cons = Class.forName(“java.lang.String”).getConstructors();
2)獲取某一個構造方法:
Constructor con =String.class.getConstructor(StringBuffer.class);①
3、創建實例對象:
1)通常方式:String str = new String(new StringBuffer (”abc”));
2)反射方式:String str = (String)con.newInstance(new StringBuffer(“abc”));②
調用獲得的方法時要用到上面相同類型的實例對象,即兩個StringBuffer()要對應相等。
NewInstance():構造出一個實例對象,每調用一次就構造一個對象。
注意:上面的兩個地方①②都要用到StringBuffer,這必須是一致的。
第①個是指定要帶StringBuffer參數類型的構造方法,即所需使用的是含StringBuffer類型的構造方法。
第②個是用這個構造方法創建對象,要傳入的參數類型是StringBuffer。

4、Class.newInstance():創建一個對象,不帶參數的構造方法。
forName()是靜態方法,是反射中使用的一種方式獲取字節碼的實例對象。
每個類的字節碼對象只有唯一的一個,如任何字符串對象,對應唯一的String.clas字節碼。


下面介紹下反射中和類的方法相關的類

Method類

1、概述:Method類代表某個類中的一個成員方法。

調用某個對象身上的方法,要先得到方法,再針對某個對象調用。

2、專家模式:誰調用這個數據,就是誰在調用它的專家。

如人關門:

調用者:是門調用管的動作,對象是門,因爲門知道如何執行關的動作,通過門軸之類的細節實現。

指揮者:是人在指揮門做關的動作,只是給門發出了關的信號,讓門執行。

總結:變量使用方法,是方法本身知道如何實現執行的過程,也就是“方法對象”調用方法,才執行了方法的每個細節的。

3、獲取某個類中的某個方法:(如String str = ”abc”)

1)通常方式:str.charAt(1)

2)反射方式:

Method charAtMethod =

       Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

charAtMethod.invoke(str,1);

說明:如果傳遞給Method對象的invoke()方法的第一個參數爲null,說明Method對象對應的是一個靜態方法

4、用反射方式執行某個main方法:

首先要明確爲何要用反射:在寫源程序時,並不知道使用者傳入的類名是什麼,但是雖然傳入的類名不知道,而知道的是這個類中的方法有main這個方法,所以可以通過反射的方式,通過使用者傳入的類名(可定義字符串型變量作爲傳入類名的入口,通過這個變量代表類名),內部通過傳入的類名獲取其main方法,然後執行相應的內容。

下面介紹下關於數組類的反射特點:

四、數組的反射

1、數組字節碼的名字:有[和數組對應類型的縮寫,如int[]數組的名稱爲:[I

2、基本數據類型的一維數組不能轉換爲Object數組,如:

int[] a = new int[3];Object[] obj= a;這樣是不成立的。

3、如何得到某個數組中的某個元素的類型:

例:int a = newint[3];Object[] obj= new Object[]{”ABC”,1};

無法得到某個數組的具體類型,只能得到其中某個元素的類型,如

Obj[0].getClass().getName()得到的是java.lang.String

若通過b.getClass().getName(),結果是:[Ljava.lang.Object;

下面介紹下反射類的實際應用,以類的加載器爲例:

1、簡述:類加載器是將.class的文件加載經內存,也可將普通文件中的信息加載進內存。

2、文件的加載問題:

1)eclipse會將源程序中的所有.java文件加載成.class文件,以確保編譯,然後放到classPath指定的目錄中去。並且會將非.java文件原封不動的複製到.class指定的目錄中去。在真正編譯的時候,使用classPath目錄中的文件,即放置.class文件的目錄。

2)寫完程序是要講配置文件放到.class文件目錄中一同打包,這些都是類加載器加載的,資源文件(配置文件)也同樣加載了配置文件。

3)框架中的配置文件都要放到classPath指定的文件夾中,原因是它的內部就是用類加載器加載的文件。

3、資源文件的加載:是使用類加載器。

1)由類加載器ClassLoader的一個對象加載經內存,即用getClassLoader()方法加載。若要加載普通文件,可用getResourseAsStream(String name)在classPath的文件中逐一查找要加載的文件。

2)在.class身上也提供了方法來加載資源文件,其實它內部就是先調用了Loader方法,再加載的資源文件。

如:Reflect.class.getResourseAsStream(String name)

4、配置文件的路徑問題:

第一、用絕對路徑,通過getRealPath()方法運算出來具體的目錄,而不是內部編碼出來的。

一般先得到用戶自定義的總目錄,在加上自己內部的路徑。可以通過getRealPath()方法獲取文件路徑。對配置文件修改是需要要儲存到配置文件中,那麼就要得到它的絕對路徑才行,因此,配置文件要放到程序的內部。

第二、name的路徑問題:

①如果配置文件和classPath目錄沒關係,就必須寫上絕對路徑,

②如果配置文件和classPath目錄有關係,即在classPath目錄中或在其子目錄中(一般是資源文件夾resource),那麼就得寫相對路徑,因爲它自己瞭解自己屬於哪個包,是相對於當前包而言的。

示例:

配置文件內容:

className=java.util.ArrayList

程序示例

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

public class ReflectTest2 {
	public static void main(String [] args)throws Exception{
		//讀取系統文件到讀取流中
		//方式一:
		//InputStream ips = new FileInputStream("config.propert");
		/*getRealPath()--得到完整的路徑//如:金山詞霸/內部
		 * 一定要用完整的路徑,但完整的路徑不是硬編碼出來的,而是運算出來的。*/
		//方式二:
		//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/text1/config.propert");
		//方式三:
			//第一種:配置文件(資源文件)在當前包中
		InputStream ips = ReflectTest2.class.getResourceAsStream("resourse/config.propert");
			//第二種:配置文件(資源文件)不在當前包中,和此包沒太大關係
		//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/test2/resourse/config.properties");
		
		//加載文件中的鍵值對
		Properties props = new Properties();
		props.load(ips);
		//關閉資源,即ips調用的那個系統資源
		//注意:關閉的是ips操作的流,加載進內存後,就不再需要流資源了,需要關閉
		ips.close();
		//定義變量,將文件中的類名賦值給變量
		String className = props.getProperty("className");
		//通過變量,創建給定類的對象
		Collection cons = 
				(Collection)Class.forName(className).newInstance();
		
		//將元素添加到集合中
		/*Collection cons = new HashSet();*/
		ReflectPoint pt1 = new ReflectPoint(3,3);
		ReflectPoint pt2 = new ReflectPoint(5,5);
		ReflectPoint pt3 = new ReflectPoint(3,3);
		cons.add(pt1);
		cons.add(pt2);
		cons.add(pt3);
		cons.add(pt1);
		//移除元素
		cons.remove(pt1);
		System.out.println(cons.size());
	}
}
----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------

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