版權聲明:本文爲博主原創文章,轉載請註明出處,歡迎交流學習!java
Java反射機制是Java語言中一種很重要的機制,可能在工做中用到的機會很少,可是在不少框架中都有用到這種機制。咱們知道Java是一門靜態語言,在程序編譯時變量的數據類型都已經肯定,那麼在Java運行時環境中,對於任意一個類,咱們可否知道這個類有哪些屬性和方法?對於任意一個對象,可否調用它的任意一個方法?答案是確定的。這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java的反射機制(Reflection)。設計模式
一、Java反射機制提供的功能數組
主要提供瞭如下幾個功能:框架
1)在運行時判斷任意一個對象所屬的類;學習
2)在運行時構造任意一個類的對象;spa
3)在運行時判斷任意一個類所具備的成員變量和方法;設計
4)在運行時調用任意一個對象的方法。代理
反射讓Java具備了動態的特性,這種機制容許程序在運行時透過Reflection API獲取任意一個已知名稱的類的內部信息,包括成員變量(fields)、方法(methods)、實現的接口(interfaces)、Java語言修飾符(modifiers)以及它的父類(superclass)等等,並可在運行時改變成員變量的內容或調用方法。code
二、Java Reflection API對象
在JDK中,提供瞭如下類來實現Java反射機制,這些類都位於java.lang.reflect包下:
Class類:表明一個類(注意:Class類位於java.lang包下);
Field類:表明類的成員變量;
Method類:表明類的方法;
Constructor類:表明類的構造方法;
Array類:提供了動態建立數組,以及訪問數組的元素的靜態方法。
經過API提供的這些類裏的方法,咱們能夠動態獲取想要的類的內部信息。
三、獲取類的Class對象
Class類的實例表示正在運行的Java程序中的類和接口,每個類都有對應的Class對象,無論一個類生成了多少個對象,這些對象都對應內存裏的同一個Class對象。Class類沒有public的構造方法,Class對象是在加載類時由Java虛擬機自動構建的。
有如下幾種方式來獲取一個類的Class對象:
1)Class類提供的靜態方法:forName(String className),參數className表示所需類的徹底限定名。
1 public class GetClassObject { 2 3 public static void main(String[] args) throws Exception { 4 5 Class<?> classType = Class.forName("java.lang.String"); 6 7 System.out.println(classType);//輸出:class java.lang.String 8 } 9 10 }
2)運用.class語法
1 public class GetClassObject { 2 3 public static void main(String[] args) throws Exception { 4 5 Class<?> classType = String.class; 6 7 System.out.println(classType);//輸出:class java.lang.String 8 } 9 10 }
3)Object類提供的方法:getClass()
1 public class GetClassObject { 2 3 public static void main(String[] args) throws Exception { 4 5 Map map = new HashMap(); 6 Class<?> classType = map.getClass(); 7 8 System.out.println(classType);//輸出:class java.util.HashMap 9 } 10 11 }
四、獲取類的Field(成員變量)對象
類的每個成員變量都對應一個Field對象,Class類提供瞭如下方法來獲取類的成員變量對應的Field對象:
1)Field getDeclaredField(String name):根據傳入的變量名稱返回此Class對象所表示的類或接口中聲明的變量對應的Field對象。
2)Field[] getDeclaredFields():返回一個Field類型的數組,包含此Class對象所表示的類或接口中聲明的全部變量的Field對象。
3)Field getField(String name):根據傳入的變量名返回一個Field對象,注意與getDeclaredField(String name)不一樣的是,此方法返回的是public變量對應的Field對象。
4)Field[] getFields():返回一個Field類型的數組,注意與Field[] getDeclaredFields()方法不一樣的是,此方法返回的是全部public變量對應的Field對象。
代碼示例:
1 public class GetFieldObject { 2 3 public static void main(String[] args) throws Exception { 4 5 //首先,得到String類的Class對象 6 Class<?> classType = Class.forName("java.lang.String"); 7 8 //得到String類中聲明的全部成員變量的Field對象的數組 9 Field[] fields = classType.getDeclaredFields(); 10 for(Field field : fields){ 11 System.out.println(field); 12 } 13 14 System.out.println("---------------------------------------------------------------------"); 15 16 //得到String類中聲明的public成員變量的Field對象的數組 17 Field[] publicFields = classType.getFields(); 18 for(Field field : publicFields){ 19 System.out.println(field); 20 } 21 22 } 23 24 }
輸出結果:
從結果輸出能夠看出getDeclaredFields()與getFields()的區別:getDeclaredFields()返回的是全部屬性的Field對象;而getFields()返回的是聲明爲public的屬性的Field對象。
五、獲取類的Method對象
類中的每個方法都對應一個Method對象,Class類提供瞭如下方法來獲取類中的方法對應的Method對象:
1)Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回一個Method對象,參數name表示方法名,可變參數parameterTypes是一個Class對象的數組,表明方法的參數的Class類型;
2)Method[] getDeclaredMethods():返回Method對象的一個數組,這些對象反映此Class對象所表示的類或接口聲明的全部方法,包括公共、保護、默認訪問和私有方法,但不包括繼承的方法;
3)Method getMethod(String name, Class<?>... parameterTypes):返回一個Method對象,注意和此Method對象對應的方法是公共(public)方法;
4)Method[] getMethods():返回一個Method數組,這些對象反映此Class對象所表示的類或接口中聲明的公共(public)方法(也包括父類或父接口中聲明的public方法)。
代碼示例:
1 public class GetMethodObject { 2 3 public static void main(String[] args) throws Exception { 4 5 //首先,得到類的Class對象 6 Class<?> classType = Class.forName("java.lang.reflect.Proxy"); 7 8 //得到類中聲明的全部方法的Method對象的數組,不包括繼承的父類的方法 9 Method[] methods = classType.getDeclaredMethods(); 10 for(Method method : methods){ 11 System.out.println(method); 12 } 13 14 System.out.println("----------------------------------------------------------------------"); 15 16 //得到類中的public方法的Method對象的數組,也包括繼承的父類的public方法 17 Method[] publicMethods = classType.getMethods(); 18 for(Method method : publicMethods){ 19 System.out.println(method); 20 } 21 22 } 23 24 }
輸出結果:
六、用反射機制調用對象的方法
Java反射機制能夠在運行時動態調用類中的方法,Java Reflection API提供了咱們所需的方法來完成動態調用。要想調用類中的方法首先要建立一個對象,咱們經過類的Class對象來建立它所表明的類的實例,經過Class對象咱們還能得到類中聲明的方法的Method對象,Method類提供了Invoke方法來調用此Method對象所表示的方法。反射機制調用方法代碼示例以下:
1 public class InvokeTester { 2 3 public static int add(int a, int b){ 4 return a + b; 5 } 6 7 public static String echo(String str){ 8 return "hello "+str; 9 } 10 11 12 public static void main(String[] args) throws Exception { 13 // InvokeTester invoke = new InvokeTester(); 14 // System.out.println(invoke.add(1, 2)); 15 // System.out.println(invoke.echo("tom")); 16 17 18 //用反射機制調用,首先得到類的Class對象 19 Class<?> classType = InvokeTester.class; 20 21 //經過Class對象得到一個InvokeTester類的實例 22 Object invoke = classType.newInstance(); 23 24 //得到add(int a, int b)方法的Method對象,getMethod方法的參數爲方法名和方法參數類型的Class對象的數組 25 Method addMethod = classType.getMethod("add", int.class, int.class); 26 27 //經過Method類的invoke方法,調用invoke對象的add方法 28 Object result = addMethod.invoke(invoke, 1, 2); 29 30 System.out.println(result); 31 32 Method echoMethod = classType.getMethod("echo", String.class); 33 34 Object result2 = echoMethod.invoke(invoke, "Tom"); 35 36 System.out.println(result2); 37 38 } 39 }
七、用反射機制調用類的私有方法
咱們知道正常狀況下一個類的私有方法只容許這個類自己來調用,但使用反射機制能打破這種訪問限制,讓其餘的類也能調用這個類的私有的方法。這種場景在實際開發中不多用到,Java也不提倡這種用法。代碼示例以下:
public class Private { //定義一個私有方法 private String sayHello(String name){ return "hello, "+name; } } public class PrivateTest { public static void main(String[] args) throws Exception { //調用Private類的私有方法 Private p = new Private(); Class<?> classType = p.getClass(); Method method = classType.getDeclaredMethod("sayHello", String.class); method.setAccessible(true);//取消Java訪問檢查,若是不設置此項則會報錯 String str = (String)method.invoke(p, "Tracy"); System.out.println(str);//輸出:hello, Tracy } }
Method、Field、Constructor類有一個共同的父類AccessibleObject類,它提供了將反射的對象標記爲在使用時取消默認Java語言訪問控制檢查的能力。在上面的代碼中,咱們在反射對象Method中設置accessible標誌,它容許程序以某種一般禁止的方式來操做對象。
八、用反射機制操做類的私有變量
與前面調用類的私有方法相似,經過反射咱們還能操做類的私有變量,代碼示例以下:
1 public class Private2 { 2 //定義私有變量 3 private String name = "zhangsan"; 4 5 public String getName(){ 6 return name; 7 } 8 } 9 10 11 public class PrivateTest2 { 12 13 public static void main(String[] args) throws Exception { 14 //改變Private2類的私有變量的值 15 Private2 p = new Private2(); 16 17 Class<?> classType = p.getClass(); 18 19 Field field = classType.getDeclaredField("name"); 20 21 field.setAccessible(true);//取消默認java訪問控制檢查,Field類的父類AccessibleObject類提供的方法 22 23 field.set(p, "lisi");//Field類的set(Object obj, Object value)方法將指定對象上此Field對象表示的字段設置爲指定的新值 24 25 System.out.println(p.getName());//輸出:lisi 26 27 } 28 29 }
以上這些內容,我介紹了Java反射機制的中涉及的主要的幾個類以及這些類的基本用法,這些類中還有不少的方法,你們能夠經過查看API進行了解,用法都很簡單。Java反射機制在不少框架的底層實現中有用到,還有一種很重要的設計模式也用到了反射,那就是代理模式中的動態代理,瞭解了動態代理模式的思想對咱們研究框架有很大幫助,我會在後面的博客中介紹這些內容,歡迎你們共同探討。