反射是程序在運(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對(duì)象

  • 從Class對(duì)象中讀取字段信息

  • 從Class對(duì)象中讀取方法信息

  • 獲取Class對(duì)象中的所有構(gòu)造方法并使用它們動(dòng)態(tài)創(chuàng)建類對(duì)象

  • 反射的一些其他細(xì)節(jié)

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

        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的方式來獲取與之對(duì)應(yīng)的Class對(duì)象。第二中獲取Class對(duì)象的方法是,通過getClass方法,當(dāng)然不是所有類都提供了這個(gè)方法的,例如:Object類就提供這么一個(gè)方法:

public final native Class<?> getClass();

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

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

對(duì)于我們的基本數(shù)據(jù)類型來說,它們從根本上說并不屬于任何一個(gè)類,所以它們只能通過第一種方式獲取對(duì)應(yīng)的Class對(duì)象。

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

//獲取該Class對(duì)象代表的類名public String getName()//獲取該Class對(duì)象代表的類名,不包含包名public String getSimpleName()//獲取該Class對(duì)象所代表的類的包名public Package getPackage()//獲取該類繼承的父類對(duì)象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)說明過,此處不再贅述。上面的這些方法的使用還是比較簡(jiǎn)單,此處不再演示。

二、獲取Class對(duì)象中字段信息
     類中的字段包括實(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)

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

//獲取該字段的名稱public String getName()//獲取該字段的訪問修飾符public int getModifiers()//獲取指定對(duì)象中的該字段的值public Object get(Object obj)//設(shè)置指定對(duì)象中該字段的值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)
.......

對(duì)于以上的幾個(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對(duì)象的方法信息
     無論是靜態(tài)方法還是實(shí)例方法,在Java反射機(jī)制中都是使用Method這個(gè)類來管理的,一個(gè)方法對(duì)應(yīng)于一個(gè)Method對(duì)象。先看怎么通過Class對(duì)象獲取其中的方法。

//返回所有被public修飾的方法的集合,包括父類的public Method[] getMethods()//返回指定的被public修飾的方法,包括父類中的public Method getMethod(String name, Class<?>... parameterTypes)//返回所有的方法的集合(可以有非public),不包括父類的public Method[] getDeclaredMethods()//返回本類中聲明的指定名稱和參數(shù)類型的方法public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

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

//獲取該方法的方法名public String getName()public int getModifiers()//獲取該方法的返回類型public Class<?> getReturnType()//獲取所有參數(shù)的類型public Class<?>[] getParameterTypes()//獲取方法聲明拋出的異常類型public Class<?>[] getExceptionTypes()//將該方法在指定的對(duì)象上執(zhí)行,有形參的需要傳入形參值public Object invoke(Object obj, Object... args)

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

上述方法中的invoke方法和之前介紹的字段的get/set方法一樣,需要指定目的對(duì)象才能使用,因?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í)例對(duì)象
     一旦我們獲取到了Class對(duì)象,我們就可以通過以下的方法進(jìn)一步獲取其中的構(gòu)造方法,并使用它們創(chuàng)建一個(gè)實(shí)例對(duì)象。

//獲取所有被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í)例對(duì)象public T newInstance(Object ... initargs)

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

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

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包中對(duì)數(shù)組類型增添了專門的類Array來實(shí)現(xiàn)反射,這里的Array和數(shù)組中的Arrays是不同的。

//創(chuàng)建指定元素類型、指定長(zhǎ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處的值為valuepublic static native void set(Object array, int index, Object value)//返回?cái)?shù)組的長(zhǎng)度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í)例對(duì)象和數(shù)組等,但是沒有了編譯器的一層檢查,很容易導(dǎo)致運(yùn)行是異常。如果能夠用接口來完成的,盡量使用接口來完成。

理解不到之處,還望大家不吝賜教!

http://www.cnblogs.com/yangming1996/p/6790478.html