詳解Java反射機(jī)制

?????反射是程序在運(yùn)行狀態(tài)下婶恼,動(dòng)態(tài)的獲取某個(gè)類的內(nèi)部信息的一種操作桑阶。例如:類名,包名勾邦,所有屬性的集合蚣录,所有方法的集合,構(gòu)造方法的集合等眷篇。該操作發(fā)生在程序的運(yùn)行時(shí)狀態(tài)萎河,所以編譯管不著有關(guān)反射的一些代碼,通常只有在運(yùn)行時(shí)才能暴露出程序的內(nèi)部錯(cuò)誤蕉饼。反射的核心在于‘Class’這個(gè)類虐杯,本篇將從Class這個(gè)類開始介紹有關(guān)反射的一些基本的概念,主要內(nèi)容如下:

  • 獲取Class對象
  • 從Class對象中讀取字段信息
  • 從Class對象中讀取方法信息
  • 獲取Class對象中的所有構(gòu)造方法并使用它們動(dòng)態(tài)創(chuàng)建類對象
  • 反射的一些其他細(xì)節(jié)

一昧港、獲取Class對象
?????此處的Class是一個(gè)具體的類(java.lang.Class)擎椰,并不是我們自定義一個(gè)類時(shí)所使用的關(guān)鍵字class。這是一個(gè)泛型類创肥,通常有兩種方法可以獲取該對象达舒。第一種方式,使用類名.class來獲取Class對象叹侄。

        Class<Integer> integerClass = int.class;
        Class<Double> doubleClass = double.class;
        Class<Character> characterClass = char.class;
        .......
        
        Class<Map> mapClass = Map.class;
        
        Class<String> stringClass = String.class;
        Class<Date> dateClass = Date.class;
        ........

無論是基本數(shù)據(jù)類型巩搏,還是一般的class類型,或是接口類型趾代,都是可以通過.class的方式來獲取與之對應(yīng)的Class對象塔猾。第二中獲取Class對象的方法是,通過getClass方法稽坤,當(dāng)然不是所有類都提供了這個(gè)方法的丈甸,例如:Object類就提供這么一個(gè)方法:

public final native Class<?> getClass();

還有我們的數(shù)組類型:

int[] array = new int[23];
Class<?> arrayClass = array.getClass();

?????下面看看有關(guān)我們獲取到的這個(gè)Class對象的一些基本信息糯俗。首先我們可以獲取有關(guān)該Class對象的名稱,包睦擂,父類得湘,父接口等信息。

//獲取該Class對象代表的類名
public String getName()

//獲取該Class對象代表的類名顿仇,不包含包名
public String getSimpleName()

//獲取該Class對象所代表的類的包名
public Package getPackage()

//獲取該類繼承的父類對象
public native Class<? super T> getSuperclass();

//獲取該類實(shí)現(xiàn)的所有接口的集合
public Class<?>[] getInterfaces()

//獲取修飾在該類上的訪問修飾符
public native int getModifiers();

//獲取該類中所有內(nèi)部類的集合
public Class<?>[] getClasses()
............

還有一些有關(guān)注解的信息淘正,由于之前在介紹注解的時(shí)候已經(jīng)說明過,此處不再贅述臼闻。上面的這些方法的使用還是比較簡單鸿吆,此處不再演示。

二述呐、獲取Class對象中字段信息
?????類中的字段包括實(shí)例域和靜態(tài)域惩淳。在Java反射機(jī)制中,使用Field類管理字段信息乓搬。

//獲取該類中所有字段的集合(public修飾符修飾的)
public Field[] getFields()

//返回指定名稱的字段(public修飾符修飾的)
public Field getField(String name)

//獲取所有字段的集合思犁,不包括從父類繼承的(包括非public)
public Field[] getDeclaredFields()

//獲取指定的字段,不包含從父類繼承的(可以是非public)
public Field getDeclaredField(String name)

對以上四種獲取字段Field的方法做一點(diǎn)說明进肯,getFields或者getField方法可以看見該類所有的被public修飾的字段激蹲,包括從父類繼承的,但是不可見非public修飾的字段江掩。getDeclaredFields或者getDeclaredField方法可以看見該類所有的字段学辱,包括非public修飾的,但是不可見父類中的字段环形。接下來策泣,我們看看對獲取到的field字段可以做哪些操作:

//獲取該字段的名稱
public String getName()

//獲取該字段的訪問修飾符
public int getModifiers()

//獲取指定對象中的該字段的值
public Object get(Object obj)

//設(shè)置指定對象中該字段的值
public void set(Object obj, Object value)

//獲取該字段的類型
public Class<?> getType() 

//判斷當(dāng)前程序是否具有訪問該字段的權(quán)限
public boolean isAccessible()

//flag設(shè)為true表示忽略Java的訪問檢查機(jī)制,以允許讀寫非public的字段
public void setAccessible(boolean flag)
.......

對于以上的幾個(gè)基本的方法斟赚,可以說見名知意着降,這里需要說明的是get和set方法,先看個(gè)例子:

   Class<Student> studentClass = Student.class;
   //這里的school是Student類中的一個(gè)屬性
   Field f = studentClass .getDeclaredField("school");
   Student s = new Student();
   f.set(s,"yanSchool");
   System.out.println(f.get(s));
輸出結(jié)果:yanSchool

這里的 f 代表了 school這個(gè)實(shí)例屬性拗军,如果別的類中有相同的屬性任洞,我們是可以通過 f為該屬性賦值的,當(dāng)然也可以從某個(gè)具有該屬性的類中獲取該屬性的值发侵,前提是具備目標(biāo)類中的該屬性的訪問權(quán)限交掏。上述的getModifiers返回的是int值,該值代表了一個(gè)修飾符刃鳄,想要轉(zhuǎn)換成我們能看懂的字符串的形式需要使用 Modifier.toString(int a)方法盅弛,將剛剛返回的int值作為參數(shù)傳入即可。

三、獲取Class對象的方法信息
?????無論是靜態(tài)方法還是實(shí)例方法挪鹏,在Java反射機(jī)制中都是使用Method這個(gè)類來管理的见秽,一個(gè)方法對應(yīng)于一個(gè)Method對象。先看怎么通過Class對象獲取其中的方法讨盒。

//返回所有被public修飾的方法的集合解取,包括父類的
public Method[] getMethods()

//返回指定的被public修飾的方法,包括父類中的
public Method getMethod(String name, Class<?>... parameterTypes)

//返回所有的方法的集合(可以有非public)返顺,不包括父類的
public Method[] getDeclaredMethods()

//返回本類中聲明的指定名稱和參數(shù)類型的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

對于以上四個(gè)方法需要說明一點(diǎn)的是禀苦,getMethod或者getDeclaredMethod在獲取指定方法的時(shí)候,需要傳入該方法的形參類型遂鹊,如果沒有傳入或者傳入錯(cuò)誤就會(huì)拋出NoSuchMethodException異常振乏。因?yàn)榉椒头椒▍?shù)類型(個(gè)數(shù))是唯一確定一個(gè)方法的憑據(jù)。對于獲取到的Method方法有以下一些操作:

//獲取該方法的方法名
public String getName()

public int getModifiers()

//獲取該方法的返回類型
public Class<?> getReturnType()

//獲取所有參數(shù)的類型
public Class<?>[] getParameterTypes()

//獲取方法聲明拋出的異常類型
public Class<?>[] getExceptionTypes()

//將該方法在指定的對象上執(zhí)行秉扑,有形參的需要傳入形參值
public Object invoke(Object obj, Object... args)

....//還有一些有關(guān)注解的操作慧邮,此處不再贅述

上述方法中的invoke方法和之前介紹的字段的get/set方法一樣,需要指定目的對象才能使用邻储,因?yàn)樗麄冎皇谴砹司唧w的一個(gè)字段或者一個(gè)方法赋咽。

public class Student extends People {

    public String school;

    private int sno;

    public void sayHello(String str){
        System.out.println("hello "+str);
    }

    public void sayHello(int s){
        System.out.println("my age is "+s);
    }
}
public static void main(String[] args){

    Class<Student> studentClass = Student.class;
   
    Method method  = studentClass.getMethod("sayHello",String.class);
    
    method.invoke(new Student(),"walker");
    }
輸出結(jié)果:hello walker

四旧噪、獲取構(gòu)造方法和創(chuàng)建實(shí)例對象
?????一旦我們獲取到了Class對象吨娜,我們就可以通過以下的方法進(jìn)一步獲取其中的構(gòu)造方法,并使用它們創(chuàng)建一個(gè)實(shí)例對象淘钟。

//獲取所有被public修飾的構(gòu)造方法宦赠,不包含父類中的
public Constructor<?>[] getConstructors() 

//返回指定的構(gòu)造方法,可以是非public修飾的
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 

//返回所有的構(gòu)造方法米母,可以包含非public修飾的
public Constructor<?>[] getDeclaredConstructors() 

//獲取指定的被pubic修飾的構(gòu)造方法勾扭,不包含父類中的
public Constructor<T> getConstructor(Class<?>... parameterTypes)

以上的方法和之前介紹的字段Field,方法Method很是相似铁瞒。下面我們看看如何使用Constructor做一些事情妙色。

public String getName()

public int getModifiers()

public Class<?>[] getParameterTypes()

//創(chuàng)建該類的實(shí)例對象
public T newInstance(Object ... initargs)

........一些注解有關(guān)的信息,可以查看之前的文章

我們主要看看如何通過 newInstance 創(chuàng)建一個(gè)實(shí)例對象:

public class Student extends People {

    public String school;
    publicint sno;

    public Student(String school,int sno){
        this.school=school;
        this.sno = sno;
    }

    public void sayHello(String str){
        System.out.println("hello "+str);
    }

    public void sayHello(int s){
        System.out.println("my age is "+s);
    }
}
public static void main(String[] args){

        Class<Student> studentClass = Student.class;
        
        Constructor constructor = studentClass.getConstructor(String.class,int.class);

        Student s = (Student) constructor.newInstance("walker", 21);
        System.out.println(s.school+" "+s.sno);
    }
輸出結(jié)果:walker 21

五慧耍、反射機(jī)制的一些細(xì)節(jié)
?????前面我們一直討論的都是具體的類身辨,我們可以從這些類中獲取到字段,方法芍碧,構(gòu)造器煌珊,注解等。java.lang.reflect包中對數(shù)組類型增添了專門的類Array來實(shí)現(xiàn)反射泌豆,這里的Array和數(shù)組中的Arrays是不同的定庵。

//創(chuàng)建指定元素類型、指定長度的數(shù)組,
public static Object newInstance(Class<?> componentType, int length)

//創(chuàng)建多維數(shù)組
public static Object newInstance(Class<?> componentType, int... dimensions)

//獲取數(shù)組array指定的索引位置index處的值
public static native Object get(Object array, int index)

//修改數(shù)組array指定的索引位置index處的值為value
public static native void set(Object array, int index, Object value)

//返回?cái)?shù)組的長度
public static native int getLength(Object array)

我們可以通過Array類在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建數(shù)組和操作數(shù)組中的元素蔬浙,而不必想之前一樣必須在編譯之前完成數(shù)組的創(chuàng)建猪落。需要注意的是此處返回的數(shù)組類型是Object而非Object[],那時(shí)因?yàn)榍罢呖梢赞D(zhuǎn)化成具體類型的數(shù)組畴博,后者則不能许布。

至此,反射的基本內(nèi)容介紹完了绎晃,我們應(yīng)該知道蜜唾,雖然反射很是靈活,可以動(dòng)態(tài)的讀取類的信息庶艾,動(dòng)態(tài)的創(chuàng)建實(shí)例對象和數(shù)組等袁余,但是沒有了編譯器的一層檢查,很容易導(dǎo)致運(yùn)行是異常咱揍。如果能夠用接口來完成的颖榜,盡量使用接口來完成。

理解不到之處煤裙,還望大家不吝賜教掩完!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市硼砰,隨后出現(xiàn)的幾起案子且蓬,更是在濱河造成了極大的恐慌,老刑警劉巖题翰,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恶阴,死亡現(xiàn)場離奇詭異,居然都是意外死亡豹障,警方通過查閱死者的電腦和手機(jī)冯事,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來血公,“玉大人昵仅,你說我怎么就攤上這事±勰В” “怎么了摔笤?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長薛夜。 經(jīng)常有香客問我籍茧,道長,這世上最難降的妖魔是什么梯澜? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任寞冯,我火速辦了婚禮渴析,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吮龄。我一直安慰自己俭茧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布漓帚。 她就那樣靜靜地躺著母债,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尝抖。 梳的紋絲不亂的頭發(fā)上毡们,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音昧辽,去河邊找鬼衙熔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛搅荞,可吹牛的內(nèi)容都是我干的红氯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咕痛,長吁一口氣:“原來是場噩夢啊……” “哼痢甘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茉贡,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤塞栅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后块仆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體构蹬,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡王暗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年悔据,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俗壹。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡科汗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绷雏,到底是詐尸還是另有隱情头滔,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布涎显,位于F島的核電站坤检,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏期吓。R本人自食惡果不足惜早歇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箭跳,春花似錦晨另、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至屉来,卻和暖如春路翻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茄靠。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工帚桩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘹黔。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓账嚎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親儡蔓。 傳聞我的和親對象是個(gè)殘疾皇子郭蕉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)喂江,斷路器召锈,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法获询,內(nèi)部類的語法涨岁,繼承相關(guān)的語法,異常的語法吉嚣,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 詳解Java反射機(jī)制(Reflection) 反射機(jī)制的作用 JAVA反射機(jī)制是在運(yùn)行狀態(tài)中梢薪,對于任意一個(gè)類,都能...
    顏洛濱閱讀 1,019評論 0 2
  • 一:java概述:1尝哆,JDK:Java Development Kit秉撇,java的開發(fā)和運(yùn)行環(huán)境,java的開發(fā)工...
    ZaneInTheSun閱讀 2,649評論 0 11
  • c
    檸檬草清香閱讀 121評論 0 0