Java的反射機(jī)制

學(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}}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 反射機(jī)制的功能 Java反射機(jī)制主要提供了以下功能:在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象...
    Anderson大碼渣閱讀 2,971評(píng)論 1 43
  • Using Java ReflectionBy Glen McCluskey January 1998 Refle...
    轉(zhuǎn)工閱讀 1,288評(píng)論 0 2
  • 定義 在Java 的運(yùn)行時(shí)環(huán)境中,對(duì)于任意的一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法,對(duì)于任意一個(gè)對(duì)象都能夠調(diào)用任...
    起個(gè)名忒難閱讀 602評(píng)論 0 1
  • 1.Java反射機(jī)制 在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用...
    不會(huì)code的程序猿閱讀 1,175評(píng)論 0 4
  • 起因 反射是一種很通用的技術(shù),幾乎在大部分的jar包中都會(huì)利用到反射機(jī)制,而且,我所學(xué)習(xí)到的幾門語言中,都存在反射...
    趙鎮(zhèn)閱讀 404評(píng)論 0 2

友情鏈接更多精彩內(nèi)容