?????反射是程序在運(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)行是異常咱揍。如果能夠用接口來完成的颖榜,盡量使用接口來完成。
理解不到之處煤裙,還望大家不吝賜教掩完!