Java反射機制深度剖析


版權聲明:本文為博主原創文章,轉載請注明出處,歡迎交流學習!

      Java反射機制是Java語言中一種很重要的機制,可能在工作中用到的機會不多,但是在很多框架中都有用到這種機制。我們知道Java是一門靜態語言,在程序編譯時變量的數據類型都已經確定,那么在Java運行時環境中,對於任意一個類,我們能否知道這個類有哪些屬性和方法?對於任意一個對象,能否調用它的任意一個方法?答案是肯定的。這種動態獲取類的信息以及動態調用對象的方法的功能來自於Java的反射機制(Reflection)。

 

      1、Java反射機制提供的功能

      主要提供了以下幾個功能:

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

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

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

      4)在運行時調用任意一個對象的方法。

      反射讓Java具有了動態的特性,這種機制允許程序在運行時透過Reflection API獲取任意一個已知名稱的類的內部信息,包括成員變量(fields)、方法(methods)、實現的接口(interfaces)、Java語言修飾符(modifiers)以及它的父類(superclass)等等,並可在運行時改變成員變量的內容或調用方法。

 

      2、Java Reflection API

      在JDK中,提供了以下類來實現Java反射機制,這些類都位於java.lang.reflect包下:

      Class類:代表一個類(注意:Class類位於java.lang包下);

      Field類:代表類的成員變量;

      Method類:代表類的方法;

      Constructor類:代表類的構造方法;

      Array類:提供了動態創建數組,以及訪問數組的元素的靜態方法。

      通過API提供的這些類里的方法,我們可以動態獲取想要的類的內部信息。

 

      3、獲取類的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 }

 

      4、獲取類的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對象。

 

      5、獲取類的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 }

      輸出結果:

      

 

      6、用反射機制調用對象的方法

      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 }

 

      7、用反射機制調用類的私有方法

      我們知道正常情況下一個類的私有方法只允許這個類本身來調用,但使用反射機制能打破這種訪問限制,讓其他的類也能調用這個類的私有的方法。這種場景在實際開發中很少用到,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標志,它允許程序以某種通常禁止的方式來操作對象。

 

      8、用反射機制操作類的私有變量

      與前面調用類的私有方法類似,通過反射我們還能操作類的私有變量,代碼示例如下:   

 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反射機制在很多框架的底層實現中有用到,還有一種很重要的設計模式也用到了反射,那就是代理模式中的動態代理,了解了動態代理模式的思想對我們研究框架有很大幫助,我會在后面的博客中介紹這些內容,歡迎大家共同探討。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM