1、認(rèn)識(shí)反射
“反”,有反就有“正”。正常情況先有類,再產(chǎn)生對(duì)象。所謂的反就是可以利用對(duì)象找到對(duì)象的出處:
在Object類里面提供有一個(gè)方法:取得Class對(duì)象:
public final Class getClass();
可以輸出類的完整名稱,就找到了對(duì)象的出處。
Class類對(duì)象的實(shí)例化:
java.lang.Class是一個(gè)類。這個(gè)類是反射操作的源頭,所有的反射都要從此類開(kāi)始進(jìn)行。最關(guān)鍵的是這個(gè)類有三種實(shí)例化方式。
1、調(diào)用Object類中的getClass()方法:Class cls = mIntent.getClass();需要實(shí)例化對(duì)象,需要import導(dǎo)入類
2、使用“類.class”取得:Class cls = Intent.class。不需要實(shí)例化對(duì)象,需要import導(dǎo)入類
3、調(diào)用Class類提供的方法:Class.forName(String className)。不需要import語(yǔ)句導(dǎo)入一個(gè)明確的類
反射實(shí)例化對(duì)象。
當(dāng)拿到一個(gè)類的時(shí)候,肯定用關(guān)鍵字new進(jìn)行對(duì)象的實(shí)例化操作,但是如果有了Class類對(duì)象后,就可以不用New關(guān)鍵字也可以進(jìn)行對(duì)象的實(shí)例化操作:
public T newInstance()相當(dāng)于使用new調(diào)用無(wú)參構(gòu)造函數(shù)。
有了反射之后,進(jìn)行實(shí)例化的操作不再只是單單依靠關(guān)鍵字new完成了。反射也可以。
所以用反射獲取一個(gè)對(duì)象實(shí)例化的步驟為:
1、先獲取Class類的實(shí)例化對(duì)象:Class cls = Class.forName(“xxxxxx”);
2、用Class類的實(shí)例化對(duì)象獲取制定類的實(shí)例化對(duì)象:Book book=(Book)cls.newInstance();
但是本來(lái)用關(guān)鍵字new一行代碼就可以完成實(shí)例化操作,反射需要兩步,這樣好嗎?
2、理解反射的作用
在任何開(kāi)發(fā)中,一起的耦合都起源于new。
看工程模式。如果想要擴(kuò)展,就必須改動(dòng)工廠類中的if else。如果一直擴(kuò)展,就需要一直修改工廠類。因?yàn)楣S類中是通過(guò)New產(chǎn)生對(duì)象實(shí)例的,所以New就是問(wèn)題的關(guān)鍵。
如果工廠中用反射代替new,就不需要if else和new實(shí)例化每個(gè)if else中的對(duì)象。只需要傳入類的完整名稱,就可以解耦和。擴(kuò)展性非常的強(qiáng)!
3、利用反射調(diào)用類的結(jié)構(gòu)
a、使用反射調(diào)用構(gòu)造:
之前所說(shuō)的newInstance()方法實(shí)際上等于調(diào)用了無(wú)參構(gòu)造函數(shù),但是實(shí)際中可能么有無(wú)參構(gòu)造函數(shù),
Class中有方法可以取到構(gòu)造:public Constructor[]getConstructors():取得全部構(gòu)造。
和:public Constructor getConstructor(Class… paramterTypes):取得一個(gè)指定參數(shù)順序的構(gòu)造函數(shù)。
Constructor類是java.lang.reflect,這時(shí)候真正到了反射中。
實(shí)例化對(duì)象方法public T newInstance(Object… initargs)
所以用反射獲取一個(gè)沒(méi)有無(wú)參構(gòu)造函數(shù)的類的實(shí)例化對(duì)象步驟為:
1、先獲取Class類對(duì)象:Class cls = Class.forName(“xxxxx”);
2、獲取指定參數(shù)類型順序的構(gòu)造函數(shù):Constructor con = cls.getConstructor(Sring.class,double.class);
3、使用獲取到的構(gòu)造函數(shù)實(shí)例化對(duì)象:Object obj = con.newInstance(“第一個(gè)參數(shù)字符串類型”,10086.8);
所以建議,不管有多少個(gè)構(gòu)造方法,都盡量提供一個(gè)無(wú)參構(gòu)造函數(shù),不然太麻煩了。
b、反射調(diào)用方法:
Class類中提供了一下方法用來(lái)獲取類的方法:
public Method[]getMethods()
public Method getMethod(String methodName,Class… paramterTypes)
Method類似java.lang.reflect包下的,其中有個(gè)
public Object invoke(Object obj,Object… args)方法。
所以用反射調(diào)用方法的步驟為:
1、先獲取Class對(duì)象:Class cls = Class.forName(“xxxxx”);
并獲取對(duì)象Object object = cls.newInstance();//必須給出實(shí)例化對(duì)象
2、獲取指定方法:Method setTitleMethod = cls.getMethod(“setTitle”,String.class);
3、調(diào)用方法:setTitleMethod.invoke(object,“一本書的標(biāo)題”);//等價(jià)于Book對(duì)象.setTitle(“一本書的標(biāo)題”)
但是這個(gè)過(guò)程中完全沒(méi)有出現(xiàn)過(guò)Book。也就是說(shuō),利用反射可以實(shí)現(xiàn)任意類的制定方法的調(diào)用。
c、反射調(diào)用成員:
類中的屬性一定要在本類實(shí)例化對(duì)象產(chǎn)生后才可以分配內(nèi)存空間。
Class類中提供了取得成員的方法:
1、取得全部成員:public Field[]getDeclaredFields()
2、取得指定成員:public Field getDeclaredField(String fieldName)
Field是java.lang.reflect包下。其中有:
1、取得屬性內(nèi)容:public Object get(Object obj);
2、設(shè)置屬性內(nèi)容:public void set(Object obj,Object value)
所以反射調(diào)用成員的步驟為:
假設(shè)一個(gè)Book類,里面只有一個(gè)屬性private String title;沒(méi)有g(shù)etter/setter方法。
1、先獲取Class對(duì)象:Class cls = Class.forName(“xxx”);
2、獲取對(duì)象:Object obj = cls.newInstance();
3、獲取指定成員:Field titleField = cls.getDeclaredField(“title”);
4、設(shè)置屬性內(nèi)容:titleField.set(obj,“書的名字”);//相當(dāng)于:Book類對(duì)象.title =“書的名字“
但是調(diào)用get還是會(huì)報(bào)錯(cuò)。因?yàn)榉庋b性。
這時(shí)候需要用到:AccessbleObject
在java.lang.reflect.AccessibleObject類下面(JDK1.8修改):
~ Executable//可執(zhí)行的
~ Constrictor
~Method
~Field
在這個(gè)類中有一個(gè)方法:
public void setAccessible(boolean flag)設(shè)置是否封裝,
設(shè)置為false后,就是取消封裝,這個(gè)時(shí)候再調(diào)用剛才的get就可以正常調(diào)用了。
構(gòu)造方法和普通方法一樣可以取消封裝,只不過(guò)很少這樣去做,而且對(duì)屬性的訪問(wèn)還是應(yīng)該是getter和setter方法完成。
學(xué)習(xí)完這些,反射算是入門了。