學(xué)習(xí)Android的同學(xué)注意了?。?!
學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Android學(xué)習(xí)交流群,群號(hào)碼:364595326? 我們一起學(xué)Android!
Java反射機(jī)制主要提供了以下功能:
在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;
在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;
生成動(dòng)態(tài)代理。
·反射機(jī)制的利弊
其實(shí)好處就是,增加程序的靈活性,避免將程序?qū)懰赖酱a里;但是壞處也有,就是性能是一個(gè)問題,反射相當(dāng)于一系列解釋操作,通知jvm要做的事情,性能比直接的java代碼要慢很多。且不安全,通過反射機(jī)制我們能拿到類的私有成員。
·詳解
Reflection。這個(gè)字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以于運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對(duì)象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內(nèi)省、內(nèi)觀、反?。eflection和introspection是常被并提的兩個(gè)術(shù)語。
反射之中包含了一個(gè)“反”的概念,所以要想解釋反射就必須先從“正”開始解釋,一般而言,當(dāng)用戶使用一個(gè)類的時(shí)候,應(yīng)該先知道這個(gè)類,而后通過這個(gè)類產(chǎn)生實(shí)例化對(duì)象,但是“反”指的是通過對(duì)象找到類。
packagecn.mldn.demo;
classPerson {
}
PublicclassTestDemo{
Publicstaticvoidmain(String[] args) throwsException{
Person per = newPerson() ;// 正著操作
System.out.println(per.getClass().getName());// 反著來
}}
以上的代碼使用了一個(gè)getClass()方法,而后就可以得到對(duì)象所在的“包.類”名稱,這就屬于“反”了,但是在這個(gè)“反”的操作之中有一個(gè)getClass()就作為發(fā)起一切反射操作的開端。
Person的父類是Object類,而上面所使用getClass()方法就是Object類之中所定義的方法。
·取得Class對(duì)象:public final Class getClass(),反射之中的所有泛型都定義為?,返回值都是Object。
而這個(gè)getClass()方法返回的對(duì)象是Class類的對(duì)象,所以這個(gè)Class就是所有反射操作的源頭。但是在講解其真正使用之前還有一個(gè)需要先解釋的問題,既然Class是所有反射操作的源頭,那么這個(gè)類肯定是最為重要的,而如果要想取得這個(gè)類的實(shí)例化對(duì)象,Java中定義了三種方式:
方式一:通過Object類的getClass()方法取得,基本不用:
packagecn.mldn.demo;classPerson {
publicclassTestDemo {publicstaticvoidmain(String[] args) throwsException {
Person per = newPerson() ;// 正著操作
Class cls = per.getClass() ;// 取得Class對(duì)象
System.out.println(cls.getName());// 反著來
}}
方式二:使用“類.class”取得,在日后學(xué)習(xí)hibernate開發(fā)的時(shí)候使用
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class cls =Person.class;// 取得Class對(duì)象
System.out.println(cls.getName());// 反著來
}}
方式三:使用Class類內(nèi)部定義的一個(gè)static方法,主要使用
·取得Class類對(duì)象:
public static Class forName(String className) throws ClassNotFoundException;
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
System.out.println(cls.getName());// 反著來
}}
那么現(xiàn)在一個(gè)新的問題又來了,取得了Class類的對(duì)象有什么用處呢?對(duì)于對(duì)象的實(shí)例化操作之前一直依靠構(gòu)造方法和關(guān)鍵字new完成,可是有了Class類對(duì)象之后,現(xiàn)在又提供了另外一種對(duì)象的實(shí)例化方法:
·通過反射實(shí)例化對(duì)象:
public T newInstance() throws InstantiationException, IllegalAccessException;
范例:通過反射實(shí)例化對(duì)象
packagecn.mldn.demo;
classPerson {
@Override
publicString toString(){
return"Person Class Instance .";
}}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Object obj = cls.newInstance() ;// 實(shí)例化對(duì)象,和使用關(guān)鍵字new一樣
Person per = (Person) obj ;// 向下轉(zhuǎn)型
System.out.println(per);
}}
那么現(xiàn)在可以發(fā)現(xiàn),對(duì)于對(duì)象的實(shí)例化操作,除了使用關(guān)鍵字new之外又多了一個(gè)反射機(jī)制操作,而且這個(gè)操作要比之前使用的new復(fù)雜一些,可是有什么用?
對(duì)于程序的開發(fā)模式之前一直強(qiáng)調(diào):盡量減少耦合,而減少耦合的最好做法是使用接口,但是就算使用了接口也逃不出關(guān)鍵字new,所以實(shí)際上new是造成耦合的關(guān)鍵元兇。
范例:回顧一下之前所編寫的工廠設(shè)計(jì)模式
packagecn.mldn.demo;interfaceFruit {?
? publicvoideat() ;}
classApple implementsFruit {?
? publicvoideat() {? ? ? ?
System.out.println("吃蘋果。");? ? };}
classFactory {publicstaticFruitgetInstance(String className){
if("apple".equals(className)){returnnewApple() ;? ? ? ? }? ? ?
? returnnull;? ? }}
publicclassFactoryDemo {?
? publicstaticvoidmain(String[] args) {? ? ?
? Fruit f = Factory.getInstance("apple") ;? ? ? ?
f.eat() ;? ? }}
以上為之前所編寫最簡單的工廠設(shè)計(jì)模式,但是在這個(gè)工廠設(shè)計(jì)模式之中有一個(gè)最大的問題:如果現(xiàn)在接口的子類增加了,那么工廠類肯定需要修改,這是它所面臨的最大問題,而這個(gè)最大問題造成的關(guān)鍵性的病因是new,那么如果說現(xiàn)在不使用關(guān)鍵字new了,變?yōu)榱朔瓷錂C(jī)制呢?
反射機(jī)制實(shí)例化對(duì)象的時(shí)候?qū)嶋H上只需要“包.類”就可以,于是根據(jù)此操作,修改工廠設(shè)計(jì)模式。
packagecn.mldn.demo;interfaceFruit {? ??
publicvoideat() ;}
classApple implementsFruit {? ?
?publicvoideat() {? ? ? ??
System.out.println("吃蘋果。");? ? };}
classOrange implementsFruit {? ??
publicvoideat() {? ? ? ??
System.out.println("吃橘子。");? ? };}
classFactory {
publicstaticFruitgetInstance(String className){? ? ? ?
Fruit f =null;try{? ? ? ? ? ?
f = (Fruit) Class.forName(className).newInstance() ;? ? ? ? }
catch(Exception e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? ? ?
returnf ;? ? }}
publicclassFactoryDemo {? ?
publicstaticvoidmain(String[] args) {? ? ? ?
Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;? ? ?
? f.eat() ;? ? }}
發(fā)現(xiàn),這個(gè)時(shí)候即使增加了接口的子類,工廠類照樣可以完成對(duì)象的實(shí)例化操作,這個(gè)才是真正的工廠類,可以應(yīng)對(duì)于所有的變化。如果單獨(dú)從開發(fā)角度而言,與開發(fā)者關(guān)系不大,但是對(duì)于日后學(xué)習(xí)的一些框架技術(shù)這個(gè)就是它實(shí)現(xiàn)的命脈,在日后的程序開發(fā)上,如果發(fā)現(xiàn)操作的過程之中需要傳遞了一個(gè)完整的“包.類”名稱的時(shí)候幾乎都是反射機(jī)制作用。
3.12.2 、反射的深入應(yīng)用
以上只是利用了Class類作為了反射實(shí)例化對(duì)象的基本應(yīng)用,但是對(duì)于一個(gè)實(shí)例化對(duì)象而言,它需要調(diào)用類之中的構(gòu)造方法、普通方法、屬性,而這些操作都可以通過反射機(jī)制完成。
3.12.2 .1、調(diào)用構(gòu)造
使用反射機(jī)制也可以取得類之中的構(gòu)造方法,這個(gè)方法在Class類之中已經(jīng)明確定義了:
以下兩個(gè)方法
取得一個(gè)類的全部構(gòu)造:
public Constructor[] getConstructors() throws SecurityException
取得一個(gè)類的指定參數(shù)構(gòu)造:
public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException
現(xiàn)在發(fā)現(xiàn)以上的兩個(gè)方法返回的都是java.lang.reflect.Constructor類的對(duì)象。
范例:取得一個(gè)類之中的全部構(gòu)造
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;
classPerson {// CTRL + KpublicPerson() {}? ?
publicPerson(Stringname) {}? ?
publicPerson(Stringname,intage) {}}
publicclassTestDemo {?
? publicstaticvoidmain(String[] args) throwsException {? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Constructor cons [] = cls.getConstructors() ;// 取得全部構(gòu)造
for(intx =0; x < cons.length; x++) {? ? ? ? ?
? System.out.println(cons[x]);? ? ? ? }? ? }}
驗(yàn)證:在之前強(qiáng)調(diào)的一個(gè)簡單Java類必須存在一個(gè)無參構(gòu)造方法
范例:觀察沒有無參構(gòu)造的情況
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;classPerson {// CTRL + K
publicPerson() {}? ?
publicPerson(Stringname) {}? ?
publicPerson(Stringname,intage) {}
}
publicclassTestDemo {? ?
publicstaticvoidmain(String[] args) throwsException {? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Constructor cons [] = cls.getConstructors() ;// 取得全部構(gòu)造
for(intx =0; x < cons.length; x++) {? ? ? ? ? ?
System.out.println(cons[x]);? ? ? ? }? ?
}}
此時(shí)程序運(yùn)行的時(shí)候出現(xiàn)了錯(cuò)誤提示“java.lang.InstantiationException”,因?yàn)橐陨系姆绞绞褂梅瓷鋵?shí)例化對(duì)象時(shí)需要的是類之中要提供無參構(gòu)造方法,但是現(xiàn)在既然沒有了無參構(gòu)造方法,那么就必須明確的找到一個(gè)構(gòu)造方法,而后利用Constructor類之中的新方法實(shí)例化對(duì)象:
·實(shí)例化對(duì)象:
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;classPerson {// CTRL +K
privateString name;? ?
privateintage;? ?
publicPerson(String name,intage) {
this.name= name ;this.age= age ;? ? }? ?
@Override? ?
publicStringtoString(){
return"Person [name="+ name+", age="+ age+"]";?
? }}
publicclassTestDemo {? ?
publicstaticvoidmain(String[] args) throwsException {? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象// 取得指定參數(shù)類型的構(gòu)造方法
Constructor cons = cls.getConstructor(String.class,int.class) ;? ? ? ?
Object obj = cons.newInstance("張三",20);// 為構(gòu)造方法傳遞參數(shù)System.out.println(obj);? ? }}
很明顯,調(diào)用無參構(gòu)造方法實(shí)例化對(duì)象要比調(diào)用有參構(gòu)造的更加簡單、方便,所以在日后的所有開發(fā)之中,凡是有簡單Java類出現(xiàn)的地方,都一定要提供無參構(gòu)造。
3.12.2 .2、調(diào)用普通方法
當(dāng)取得了一個(gè)類實(shí)例化對(duì)象之后,下面最需要調(diào)用的肯定是類之中的方法,所以可以繼續(xù)使用Class類取得一個(gè)類中所定義的方法定義:
·取得全部方法:
public Method[] getMethods() throws SecurityException;
·取得指定方法:
public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException
發(fā)現(xiàn)以上的方法返回的都是java.lang.reflect.Method類的對(duì)象。
范例:取得一個(gè)類之中所定義的全部方法
packagecn.mldn.demo;importjava.lang.reflect.Method;classPerson {? ?
privateString name;? ?
publicvoidsetName(String name) {
this.name= name;? ? }
publicStringgetName(){? ? ? ?
returnname;? ? }}
publicclassTestDemo {? ?
publicstaticvoidmain(String[] args) throwsException {? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Method met [] = cls.getMethods() ;// 取得全部方法
for(intx =0; x < met.length; x++) {? ? ? ? ? ?
System.out.println(met[x]);? ? ? ?
}? ? }}
但是取得了Method類對(duì)象最大的作用不再于方法的列出(方法的列出都在開發(fā)工具上使用了),但是對(duì)于取得了Method類對(duì)象之后還有一個(gè)最大的功能,就是可以利用反射調(diào)用類中的方法:
·調(diào)用方法:
public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
之前調(diào)用類中方法的時(shí)候使用的都是“對(duì)象.方法”,但是現(xiàn)在有了反射之后,可以直接利用Object類調(diào)用指定子類的操作方法。(同時(shí)解釋一下,為什么setter、getter方法的命名要求如此嚴(yán)格)。
范例:利用反射調(diào)用Person類之中的setName()、getName()方法
packagecn.mldn.demo;importjava.lang.reflect.Method;classPerson {? ?
privateString name;? ?
publicvoidsetName(String name) {
this.name= name;? ? }
publicStringgetName(){? ? ? ?
returnname;? ? }}
publicclassTestDemo {? ?
publicstaticvoidmain(String[] args) throwsException {? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Object obj = cls.newInstance();// 實(shí)例化對(duì)象,沒有向Person轉(zhuǎn)型String attribute ="name";// 要調(diào)用類之中的屬性
Method setMet = cls.getMethod("set"+ initcap(attribute), String.class);// setName()
Method getMet = cls.getMethod("get"+ initcap(attribute));// getName()
setMet.invoke(obj,"張三") ;// 等價(jià)于:Person對(duì)象.setName("張三")
System.out.println(getMet.invoke(obj));// 等價(jià)于:Person對(duì)象.getName()
}
publicstaticStringinitcap(String str){? ? ? ? returnstr.substring(0,1).toUpperCase().concat(str.substring(1)) ;?
? }}
在日后的所有框架技術(shù)開發(fā)之中,簡單Java類都是如此應(yīng)用的,所以必須按照標(biāo)準(zhǔn)進(jìn)行。
3.12.2 .3、調(diào)用成員
類之中最后一個(gè)組成部分就是成員(Field,也可以稱為屬性),如果要通過反射取得類的成員可以使用方法如下:
·取得本類的全部成員:
public Field[] getDeclaredFields() throws SecurityException;
·取得指定的成員:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;
這兩個(gè)方法的返回值類型是java.lang.reflect.Field類的對(duì)象,下面首先觀察如何取得一個(gè)類之中的全部屬性。
范例:取得一個(gè)類之中的全部屬性
packagecn.mldn.demo;importjava.lang.reflect.Field;classPerson {? ?
privateString name;}
publicclassTestDemo {? ?
publicstaticvoidmain(String[] args) throwsException {
Class cls =Class.forName("cn.mldn.demo.Person") ;// 取得Class對(duì)象
Fieldfield [] = cls.getDeclaredFields() ;// 取得全部屬性
for(intx =0; x < field.length; x++) {
System.out.println(field[x]);? ? ? ? }? ? }}
但是找到Field實(shí)際上就找到了一個(gè)很有意思的操作,在Field類之中提供了兩個(gè)方法:
·設(shè)置屬性內(nèi)容(類似于:對(duì)象.屬性= 內(nèi)容):public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException;
·取得屬性內(nèi)容(類似于:對(duì)象.屬性):
public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException
可是從類的開發(fā)要求而言,一直都強(qiáng)調(diào)類之中的屬性必須封裝,所以現(xiàn)在調(diào)用之前要想辦法解除封裝。
·解除封裝:
public void setAccessible(boolean flag) throws SecurityException;
范例:利用反射操作類中的屬性
packagecn.mldn.demo;importjava.lang.reflect.Field;classPerson {? ?
privateString name;}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException{? ? ? ?
Class cls = Class.forName("cn.mldn.demo.Person");// 取得Class對(duì)象Object obj = cls.newInstance();//對(duì)象實(shí)例化屬性才會(huì)分配空間
Field nameField = cls.getDeclaredField("name") ;// 找到name屬性nameField.setAccessible(true) ;// 解除封裝了
nameField.set(obj,"張三") ;// Person對(duì)象.name = "張三"
System.out.println(nameField.get(obj));// Person對(duì)象.name}}