Java的反射機制

學習Android的同學注意了!!!

學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Android學習交流群,群號碼:364595326? 我們一起學Android!

Java反射機制主要提供了以下功能:

在運行時判斷任意一個對象所屬的類;

在運行時構造任意一個類的對象;

在運行時判斷任意一個類所具有的成員變量和方法;

在運行時調用任意一個對象的方法;

生成動態代理。

·反射機制的利弊

其實好處就是,增加程序的靈活性,避免將程序寫死到代碼里;但是壞處也有,就是性能是一個問題,反射相當于一系列解釋操作,通知jvm要做的事情,性能比直接的java代碼要慢很多。且不安全,通過反射機制我們能拿到類的私有成員。

·詳解

Reflection。這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以于運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),并生成其對象實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內省、內觀、反省)。Reflection和introspection是常被并提的兩個術語。

反射之中包含了一個“反”的概念,所以要想解釋反射就必須先從“正”開始解釋,一般而言,當用戶使用一個類的時候,應該先知道這個類,而后通過這個類產生實例化對象,但是“反”指的是通過對象找到類。

packagecn.mldn.demo;

classPerson {

}

PublicclassTestDemo{

Publicstaticvoidmain(String[] args) throwsException{

Person per = newPerson() ;// 正著操作

System.out.println(per.getClass().getName());// 反著來

}}

以上的代碼使用了一個getClass()方法,而后就可以得到對象所在的“包.類”名稱,這就屬于“反”了,但是在這個“反”的操作之中有一個getClass()就作為發起一切反射操作的開端。

Person的父類是Object類,而上面所使用getClass()方法就是Object類之中所定義的方法。

·取得Class對象:public final Class getClass(),反射之中的所有泛型都定義為?,返回值都是Object。

而這個getClass()方法返回的對象是Class類的對象,所以這個Class就是所有反射操作的源頭。但是在講解其真正使用之前還有一個需要先解釋的問題,既然Class是所有反射操作的源頭,那么這個類肯定是最為重要的,而如果要想取得這個類的實例化對象,Java中定義了三種方式:

方式一:通過Object類的getClass()方法取得,基本不用:

packagecn.mldn.demo;classPerson {

publicclassTestDemo {publicstaticvoidmain(String[] args) throwsException {

Person per = newPerson() ;// 正著操作

Class cls = per.getClass() ;// 取得Class對象

System.out.println(cls.getName());// 反著來

}}

方式二:使用“類.class”取得,在日后學習hibernate開發的時候使用

packagecn.mldn.demo;

classPerson {}

publicclassTestDemo {

publicstaticvoidmain(String[] args) throwsException {

Class cls =Person.class;// 取得Class對象

System.out.println(cls.getName());// 反著來

}}

方式三:使用Class類內部定義的一個static方法,主要使用

·取得Class類對象:

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對象

System.out.println(cls.getName());// 反著來

}}

那么現在一個新的問題又來了,取得了Class類的對象有什么用處呢?對于對象的實例化操作之前一直依靠構造方法和關鍵字new完成,可是有了Class類對象之后,現在又提供了另外一種對象的實例化方法:

·通過反射實例化對象:

public T newInstance() throws InstantiationException, IllegalAccessException;

范例:通過反射實例化對象

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對象

Object obj = cls.newInstance() ;// 實例化對象,和使用關鍵字new一樣

Person per = (Person) obj ;// 向下轉型

System.out.println(per);

}}

那么現在可以發現,對于對象的實例化操作,除了使用關鍵字new之外又多了一個反射機制操作,而且這個操作要比之前使用的new復雜一些,可是有什么用?

對于程序的開發模式之前一直強調:盡量減少耦合,而減少耦合的最好做法是使用接口,但是就算使用了接口也逃不出關鍵字new,所以實際上new是造成耦合的關鍵元兇。

范例:回顧一下之前所編寫的工廠設計模式

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() ;? ? }}

以上為之前所編寫最簡單的工廠設計模式,但是在這個工廠設計模式之中有一個最大的問題:如果現在接口的子類增加了,那么工廠類肯定需要修改,這是它所面臨的最大問題,而這個最大問題造成的關鍵性的病因是new,那么如果說現在不使用關鍵字new了,變為了反射機制呢?

反射機制實例化對象的時候實際上只需要“包.類”就可以,于是根據此操作,修改工廠設計模式。

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() ;? ? }}

發現,這個時候即使增加了接口的子類,工廠類照樣可以完成對象的實例化操作,這個才是真正的工廠類,可以應對于所有的變化。如果單獨從開發角度而言,與開發者關系不大,但是對于日后學習的一些框架技術這個就是它實現的命脈,在日后的程序開發上,如果發現操作的過程之中需要傳遞了一個完整的“包.類”名稱的時候幾乎都是反射機制作用。

3.12.2 、反射的深入應用

以上只是利用了Class類作為了反射實例化對象的基本應用,但是對于一個實例化對象而言,它需要調用類之中的構造方法、普通方法、屬性,而這些操作都可以通過反射機制完成。

3.12.2 .1、調用構造

使用反射機制也可以取得類之中的構造方法,這個方法在Class類之中已經明確定義了:

以下兩個方法

取得一個類的全部構造:

public Constructor[] getConstructors() throws SecurityException

取得一個類的指定參數構造:

public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException

現在發現以上的兩個方法返回的都是java.lang.reflect.Constructor類的對象。

范例:取得一個類之中的全部構造

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對象

Constructor cons [] = cls.getConstructors() ;// 取得全部構造

for(intx =0; x < cons.length; x++) {? ? ? ? ?

? System.out.println(cons[x]);? ? ? ? }? ? }}


驗證:在之前強調的一個簡單Java類必須存在一個無參構造方法

范例:觀察沒有無參構造的情況

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對象

Constructor cons [] = cls.getConstructors() ;// 取得全部構造

for(intx =0; x < cons.length; x++) {? ? ? ? ? ?

System.out.println(cons[x]);? ? ? ? }? ?

}}

此時程序運行的時候出現了錯誤提示“java.lang.InstantiationException”,因為以上的方式使用反射實例化對象時需要的是類之中要提供無參構造方法,但是現在既然沒有了無參構造方法,那么就必須明確的找到一個構造方法,而后利用Constructor類之中的新方法實例化對象:

·實例化對象:

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對象// 取得指定參數類型的構造方法

Constructor cons = cls.getConstructor(String.class,int.class) ;? ? ? ?

Object obj = cons.newInstance("張三",20);// 為構造方法傳遞參數System.out.println(obj);? ? }}

很明顯,調用無參構造方法實例化對象要比調用有參構造的更加簡單、方便,所以在日后的所有開發之中,凡是有簡單Java類出現的地方,都一定要提供無參構造。

3.12.2 .2、調用普通方法

當取得了一個類實例化對象之后,下面最需要調用的肯定是類之中的方法,所以可以繼續使用Class類取得一個類中所定義的方法定義:

·取得全部方法:

public Method[] getMethods() throws SecurityException;

·取得指定方法:

public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException

發現以上的方法返回的都是java.lang.reflect.Method類的對象。

范例:取得一個類之中所定義的全部方法

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對象

Method met [] = cls.getMethods() ;// 取得全部方法

for(intx =0; x < met.length; x++) {? ? ? ? ? ?

System.out.println(met[x]);? ? ? ?

}? ? }}

但是取得了Method類對象最大的作用不再于方法的列出(方法的列出都在開發工具上使用了),但是對于取得了Method類對象之后還有一個最大的功能,就是可以利用反射調用類中的方法:

·調用方法:

public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException

之前調用類中方法的時候使用的都是“對象.方法”,但是現在有了反射之后,可以直接利用Object類調用指定子類的操作方法。(同時解釋一下,為什么setter、getter方法的命名要求如此嚴格)。

范例:利用反射調用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對象

Object obj = cls.newInstance();// 實例化對象,沒有向Person轉型String attribute ="name";// 要調用類之中的屬性

Method setMet = cls.getMethod("set"+ initcap(attribute), String.class);// setName()

Method getMet = cls.getMethod("get"+ initcap(attribute));// getName()

setMet.invoke(obj,"張三") ;// 等價于:Person對象.setName("張三")

System.out.println(getMet.invoke(obj));// 等價于:Person對象.getName()

}

publicstaticStringinitcap(String str){? ? ? ? returnstr.substring(0,1).toUpperCase().concat(str.substring(1)) ;?

? }}

在日后的所有框架技術開發之中,簡單Java類都是如此應用的,所以必須按照標準進行。

3.12.2 .3、調用成員

類之中最后一個組成部分就是成員(Field,也可以稱為屬性),如果要通過反射取得類的成員可以使用方法如下:

·取得本類的全部成員:

public Field[] getDeclaredFields() throws SecurityException;

·取得指定的成員:

public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;

這兩個方法的返回值類型是java.lang.reflect.Field類的對象,下面首先觀察如何取得一個類之中的全部屬性。

范例:取得一個類之中的全部屬性

packagecn.mldn.demo;importjava.lang.reflect.Field;classPerson {? ?

privateString name;}

publicclassTestDemo {? ?

publicstaticvoidmain(String[] args) throwsException {

Class cls =Class.forName("cn.mldn.demo.Person") ;// 取得Class對象

Fieldfield [] = cls.getDeclaredFields() ;// 取得全部屬性

for(intx =0; x < field.length; x++) {

System.out.println(field[x]);? ? ? ? }? ? }}

但是找到Field實際上就找到了一個很有意思的操作,在Field類之中提供了兩個方法:

·設置屬性內容(類似于:對象.屬性= 內容):public void set(Object obj, Object value)

throws IllegalArgumentException, IllegalAccessException;

·取得屬性內容(類似于:對象.屬性):

public Object get(Object obj)

throws IllegalArgumentException, IllegalAccessException

可是從類的開發要求而言,一直都強調類之中的屬性必須封裝,所以現在調用之前要想辦法解除封裝。

·解除封裝:

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對象Object obj = cls.newInstance();//對象實例化屬性才會分配空間

Field nameField = cls.getDeclaredField("name") ;// 找到name屬性nameField.setAccessible(true) ;// 解除封裝了

nameField.set(obj,"張三") ;// Person對象.name = "張三"

System.out.println(nameField.get(obj));// Person對象.name}}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容