Java反射學習:深入學習Java反射機制


一、Java反射的理解(反射是研究框架的基礎之一)

 Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為Java語言的反射機制。

二、逐步分析

參考:https://blog.csdn.net/u012585964/article/details/52011138

 

1、關於Class

  1、Class是一個類,一個描述類的類(也就是描述類本身),封裝了描述方法的Method,描述字段的Filed,描述構造器的Constructor等屬性
    2、對象照鏡子后(反射)可以得到的信息:某個類的數據成員名、方法和構造器、某個類到底實現了哪些接口。
    3、對於每個類而言,JRE 都為其保留一個不變的 Class 類型的對象。 一個 Class 對象包含了特定某個類的有關信息。
    4、Class 對象只能由系統建立對象
    5、一個類在 JVM 中只會有一個Class實例

下面創建一個例子:后面的分析均以此案例為基准。

創建一個空接口:(后面會不斷補充) 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5 }

 創建一個空基類:(后面會不斷補充) 

1 package com.xfwl.reflection;
2 
3 public class Human {
4     
5 }

 創建一個子類:(后面會不斷補充) 

 1 package com.xfwl.reflection;
 2 
 3 public class Person extends Human implements IHuman {
 4     /**
 5      * 默認default修飾
 6      */
 7     String name;
 8     /**
 9      * private修飾
10      */
11     private int age;
12     /**
13      * public修飾
14      */
15     public char sex='M';
16     /**
17      * 無參構造
18      */
19     public Person(){
20         System.out.println("無參構造!!!");
21     }
22     /**
23      * 有參構造
24      */
25     public Person(String name,int age,char sex){
26         System.out.println("有參構造!!!");
27         this.name=name;
28         this.age=age;
29         this.sex=sex;
30     }
31     public String getName() {
32         return name;
33     }
34     public void setName(String name) {
35         this.name = name;
36     }
37     public int getAge() {
38         return age;
39     }
40     public void setAge(int age) {
41         this.age = age;
42     }
43     public char getSex() {
44         return sex;
45     }
46     public void setSex(char sex) {
47         this.sex = sex;
48     }
49     public String toString() {  
50         return "Person{" +  
51                 "name='" + name + '\'' +  
52                 ", age=" + age  +  
53                 ", sex='" + sex + '\'' +  
54                 '}';  
55     }  
56 }

 2、反射獲取類對象的三種方式(通過一個Junit測試來說明) 

 1 package com.xfwl.reflection;
 2 
 3 import org.junit.Test;
 4 /**
 5  * 測試類
 6  * @function  
 7  * @author 小風微涼
 8  * @time  2018-6-3 下午12:28:38
 9  */
10 public class TestAction {
11     /** 
12      * 反射機制獲取類有三種方法 
13      */ 
14     @Test
15     public void testGetClass() throws ClassNotFoundException {  
16          Class clazz = null; 
17          
18         //1 直接通過類名.Class的方式得到  
19         clazz = Person.class;  
20         System.out.println("通過類名: " + clazz);  
21          
22         //2 通過對象的getClass()方法獲取,這個使用的少(一般是傳的是Object,不知道是什么類型的時候才用)  
23         Object obj = new Person();  
24         clazz = obj.getClass();  
25         System.out.println("通過getClass(): " + clazz);   
26          
27         //3 通過全類名獲取,用的比較多,但可能拋出ClassNotFoundException異常  
28         clazz = Class.forName("com.xfwl.reflection.Person");  
29         System.out.println("通過全類名獲取: " + clazz);           
30     }
31 }

 運行結果: 

通過類名: class com.xfwl.reflection.Person
無參構造!!!
通過getClass(): class com.xfwl.reflection.Person
通過全類名獲取: class com.xfwl.reflection.Person

 特別注意:(以下2中方式不會調用構造方法,因為沒有實例化操作)  

//1 直接通過類名.Class的方式得到  
clazz = Person.class; 
//3 通過全類名獲取,用的比較多,但可能拋出ClassNotFoundException異常  
clazz = Class.forName("com.xfwl.reflection.Person");

3、利用newInstance創建對象:調用的類必須有無參的構造器

 1 /**
 2      * Class類的newInstance()方法,創建類的一個對象。
 3      * @throws ClassNotFoundException
 4      * @throws IllegalAccessException
 5      * @throws InstantiationException
 6      */
 7     @Test
 8     public void testNewInstance()  
 9             throws ClassNotFoundException, IllegalAccessException, InstantiationException {  
10       
11         Class clazz = Class.forName("com.xfwl.reflection.Person");  
12       
13         //使用Class類的newInstance()方法創建類的一個對象  
14         //實際調用的類的那個 無參數的構造器(這就是為什么寫的類的時候,要寫一個無參數的構造器,就是給反射用的)  
15         //一般的,一個類若聲明了帶參數的構造器,也要聲明一個無參數的構造器  
16         Object obj = clazz.newInstance();  
17         System.out.println(obj);  
18     }  

 測試結果:

那么,如果刪除Person.java中的無參構造,繼續測試,結果如下:

4、ClassLoader類加載器


類加載器詳解:
http://blog.csdn.net/ochangwen/article/details/51473120
 
 1     /** 
 2      * ClassLoader類裝載器 
 3      */  
 4     @Test  
 5     public void testClassLoader1() throws ClassNotFoundException, IOException {  
 6         //1、獲取一個系統的類加載器  
 7         ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
 8         System.out.println("系統的類加載器-->" + classLoader);  
 9       
10         //2、獲取系統類加載器的父類加載器(擴展類加載器(extensions classLoader))  
11         classLoader = classLoader.getParent();  
12         System.out.println("擴展類加載器-->" + classLoader);  
13       
14         //3、獲取擴展類加載器的父類加載器  
15         //輸出為Null,無法被Java程序直接引用  
16         classLoader = classLoader.getParent();  
17         System.out.println("啟動類加載器-->" + classLoader);  
18       
19         //4、測試當前類由哪個類加載器進行加載 ,結果就是系統的類加載器  
20         classLoader = Class.forName("com.xfwl.reflection.Person").getClassLoader();  
21         System.out.println("當前類由哪個類加載器進行加載-->"+classLoader);  
22       
23         //5、測試JDK提供的Object類由哪個類加載器負責加載的  
24         //輸出為Null,無法被Java程序直接引用
25         classLoader = Class.forName("java.lang.Object").getClassLoader();  
26         System.out.println("JDK提供的Object類由哪個類加載器加載-->" + classLoader);  
27     } 

 測試結果: 

系統的類加載器-->sun.misc.Launcher$AppClassLoader@18b4aac2
擴展類加載器-->sun.misc.Launcher$ExtClassLoader@614c5515
啟動類加載器-->null
當前類由哪個類加載器進行加載-->sun.misc.Launcher$AppClassLoader@18b4aac2
JDK提供的Object類由哪個類加載器加載-->null

 5、反射機制通過加載器獲取流對象:getResourceAsStream方法 

 1 /**
 2      * 反射機制通過加載器獲取流對象:getResourceAsStream方法
 3      * @throws ClassNotFoundException
 4      * @throws IOException
 5      */
 6      @Test  
 7     public void testGetResourceAsStream() throws ClassNotFoundException, IOException {  
 8         //調用getResourceAsStream 獲取類路徑下的文件對應的輸入流 
 9          /**
10           * 特別說明:
11           * getResourceAsStream("path"),path的路徑和new Person()的位置有關
12           */
13          
14         InputStream in = new Person().getClass().getClassLoader()  
15                 .getResourceAsStream("com/xfwl/reflection/test.properties");  
16         System.out.println("in: " +in);  
17   
18         Properties properties = new Properties(); 
19         properties.load(in);  
20         System.out.println("文件內容:"+properties);
21         System.out.println("name: "+properties.getProperty("name"));  
22         System.out.println("age: " + properties.getProperty("age"));
23         System.out.println("sex: "+properties.getProperty("sex"));  
24         System.out.println("desc: " + properties.getProperty("desc")); 
25     }  

 test.properties文件內容如下:文件編碼格式:ISO-8859-1 

name=\u5C0F\u98CE\u5FAE\u51C9\u0087\u0089
age=23
sex=M
desc=\u53CD\u5C04\u673A\u5236\u5B66\u4E60

 運行結果:(直接解析會出現亂碼問題,這個可以通過new String(亂碼格式處理參數)來處理)

無參構造!!!
in: java.io.BufferedInputStream@215be6bb
文件內容:{age=23, name=小風微涼??, sex=M, desc=反射機制學習}
name: 小風微涼??
age: 23
sex: M
desc: 反射機制學習

 6、反射機制獲取類中的方法:Method: 對應類中的方法 

現在給Person類添加一個private 方法、一個public 方法、一個defaut 方法、一個protected方法 

 1 /**
 2      * Java權限有四個,分別為public,protected,默認,private,其開放程度依次降低
 3      *    public可供所有類訪問
 4      *    protected繼承可見
 5      *    private只能類本身內部的方法可以訪問
 6      */    
 7     public void method_public(){
 8         System.out.println("method_public");
 9     }
10     public void method_public_2(String name,int age,char sex){//public  帶參數
11         System.out.println("method_public_2");
12         String info="Person{" +  
13         "name='" + name + '\'' +  
14         ", age=" + age  +  
15         ", sex='" + sex + '\'' +  
16         '}';
17         System.out.println(info);
18     }
19     protected void method_protected(){
20         System.out.println("method_protected");
21     }
22     protected void method_protected_2(String info){//protected  帶參數
23         System.out.println("method_protected_2:"+info);
24     }
25     void method_default(){
26         System.out.println("method_default");
27     }
28     void method_default_2(String info){//默認修飾符   帶參數
29         System.out.println("method_default_2:"+info);
30     }
31     private void method_private(){
32         System.out.println("method_private");
33     }
34     private void method_private_2(String info){//private  帶參數
35         System.out.println("method_private_2:"+info);
36     }

 開始測試如何通過反射機制使用這些方法 

 1 /**
 2      * 如何通過反射機制使用這些方法
 3      * @throws ClassNotFoundException
 4      * @throws NoSuchMethodException
 5      * @throws IllegalAccessException
 6      * @throws InstantiationException
 7      * @throws InvocationTargetException
 8      */
 9     @Test  
10     public void testMethod() throws ClassNotFoundException, NoSuchMethodException,   
11             IllegalAccessException, InstantiationException, InvocationTargetException {  
12         Class clazz = Class.forName("com.xfwl.reflection.Person");  
13       
14         //1、得到clazz 對應的類中有哪些方法,不能獲取private方法  
15         Method[] methods =clazz.getMethods();  
16         System.out.println("通過反射機制可以拿到的方法:clazz.getMethods()");  
17         for (Method method : methods){  
18             System.out.println(method.getName());  
19         } 
20         System.out.println("<-------------------------->"); 
21       
22         //2、獲取所有的方法(且只獲取當着類聲明的方法,包括private方法)  
23         Method[] methods2 = clazz.getDeclaredMethods(); 
24         System.out.println("通過反射機制可以拿到的方法:clazz.getDeclaredMethods()");  
25         for (Method method : methods2){  
26             System.out.println(method.getName());  
27         }  
28         System.out.println("<-------------------------->"); 
29         System.out.println("通過反射機制可以拿到指定的方法:clazz.getDeclaredMethod()"); 
30         //3、獲取指定的方法  
31         Method  method1= clazz.getDeclaredMethod("method_private");
32         System.out.println("private 無參:"+method1);  
33       
34         Method method2 = clazz.getDeclaredMethod("method_private_2",String.class);//第一個參數是方法名,后面的是方法里的參數  
35         System.out.println("private 有參:"+method2);  
36       
37         Method method3 = clazz.getDeclaredMethod("method_public_2",String.class,int.class,char.class);//第一個參數是方法名,后面的是方法里的參數  
38         System.out.println("public 有參:"+method2);  
39       
40         //4、執行方法!  
41         Object obj = clazz.newInstance();  
42         method3.invoke(obj, "小風微涼", 23,'M');  //執行方法:invoke(類對象)
43     }  

 測試結果: 

通過反射機制可以拿到的方法:clazz.getMethods():不能獲取private/protected/default方法 
toString
getName
setName
method_public_2
setAge
method_public
getSex
getAge
setSex
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
<-------------------------->
通過反射機制可以拿到的方法:clazz.getDeclaredMethods():獲取所有修飾權限的方法
toString
getName
setName
method_private_2
method_private
method_public_2
setAge
method_public
method_default
method_default_2
getSex
getAge
setSex
method_protected
method_protected_2
<-------------------------->
通過反射機制可以拿到指定的方法:clazz.getDeclaredMethod()
private 無參:private void com.xfwl.reflection.Person.method_private()
private 有參:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
public 有參:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
無參構造!!!
method_public_2
Person{name='小風微涼', age=23, sex='M'}

 繼續分析一下:

JDK中的獲取方法

獲取方法:默認只能獲取public修飾的方法 

1 @CallerSensitive
2     public Method[] getMethods() throws SecurityException {
3         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
4         return copyMethods(privateGetPublicMethods());
5     }

 獲取方法:所有修飾權限的方法都可以獲得 

@CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

 獲取方法:獲取指定的方法(所有修飾權限) 

 1     /**
 2      * @jls 8.2 Class Members
 3      * @jls 8.4 Method Declarations
 4      * @since JDK1.1
 5      */
 6     @CallerSensitive
 7     public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 8         throws NoSuchMethodException, SecurityException {
 9         checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
10         Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
11         if (method == null) {
12             throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
13         }
14         return method;
15     }

   分析一下上面這個方法:

    String name:方法的名稱

       Class<?>... parameterTypes:一個或多個方法參數的類型,注意要一一對應,否則會報錯的哦

執行方法:invoke(方法對象,方法實際參數) 

 1 @CallerSensitive
 2     public Object invoke(Object obj, Object... args)
 3         throws IllegalAccessException, IllegalArgumentException,
 4            InvocationTargetException
 5     {
 6         if (!override) {
 7             if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
 8                 Class<?> caller = Reflection.getCallerClass();
 9                 checkAccess(caller, clazz, obj, modifiers);
10             }
11         }
12         MethodAccessor ma = methodAccessor;             // read volatile
13         if (ma == null) {
14             ma = acquireMethodAccessor();
15         }
16         return ma.invoke(obj, args);
17     }

 7、反射機制獲取類中的方法:Method: 對應基類或接口中的方法

上面分析了,如何通過反射拿到當前本類里面的各個修飾權限的方法,下面來繼續分析一下,如何讀取父類或實現的接口中的方法:

現在,給父類添加一些方法,在接口中定義一些方法:

接口中的方法聲明: 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5     void eat();
6     void eat(String info);
7 }

 Person.java實現接口方法

1     public void eat() {
2         System.out.println("實現接口的方法:eat()無參:");
3     }
4     public void eat(String info) {
5         System.out.println("實現接口的方法:eat()有參:"+info);        
6     }

 父類中的方法定義: 

 1 package com.xfwl.reflection;
 2 
 3 public class Human {
 4     public void play_public(){
 5         System.out.println("public無參:play_public");
 6     }
 7     public void play_public_2(String info){
 8         System.out.println("public有參:play_public2:"+info);
 9     }
10     protected void play_protected(){
11         System.out.println("protected無參:play_protected");
12     }
13     protected void play_protected_2(String info){
14         System.out.println("protected有參:play_protected_2:"+info);
15     }
16     void play_default(){
17         System.out.println("默認修飾符無參:play_default");
18     }
19     void play_default_2(String info){//默認修飾符   帶參數
20         System.out.println("默認修飾符有參:play_default_2:"+info);
21     }
22     private void play_private(){
23         System.out.println("private無參:play_private");
24     }
25     private void play_private_2(String info){
26         System.out.println("private有參:play_private_2:"+info);
27     }
28 }

 開始測試:

1、拿到當前Person類反射對象,能否得到接口中的方法 

 1 /**
 2      * 反射機制獲取類中的方法:Method: 對應基類或接口中的方法
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchMethodException 
 6      * @throws InstantiationException 
 7      * @throws InvocationTargetException 
 8      * @throws IllegalArgumentException 
 9      * @throws IllegalAccessException 
10      */
11     @Test
12     public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
13          Class clazz = Class.forName("com.xfwl.reflection.Person");
14          //拿到當前Person類反射對象,能否得到接口中的方法     
15          for (Method method : clazz.getMethods()){  
16              System.out.println(method.getName());  
17          }
18         //獲取當前類實現的接口中的方法  
19          Method  method1= clazz.getDeclaredMethod("eat");
20          Method  method2= clazz.getDeclaredMethod("eat",String.class);
21          //執行
22          method1.invoke(clazz.newInstance());         
23          method2.invoke(clazz.newInstance(),"eat有參數");  
24     }

 測試結果:(可以拿到實現的接口中的方法並執行) 

toString
getName
setName
eat
eat
method_public_2
setAge
getSex
setSex
getAge
method_public
play_public_2
play_public
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
無參構造!!!
實現接口的方法:eat()無參:
無參構造!!!
實現接口的方法:eat()有參:eat有參數

 2、拿到當前Person類反射對象,能否獲取父類中的方法

通過當前反射對象,拿到父類反射對象 

1 Class clazz = Class.forName("com.xfwl.reflection.Person");
2 Class superClazz = clazz.getSuperclass();  

 

 1 /**
 2      * 反射機制獲取類中的方法:Method: 對應基類或接口中的方法
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchMethodException 
 6      * @throws InstantiationException 
 7      * @throws InvocationTargetException 
 8      * @throws IllegalArgumentException 
 9      * @throws IllegalAccessException 
10      */
11     @Test
12     public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
13          Class clazz = Class.forName("com.xfwl.reflection.Person");
14          System.out.println("<------------父類中能夠使用的公共權限的方法------------------------>");
15          //拿到當前Person類反射對象,能否獲取父類中的方法
16          Class superClazz = clazz.getSuperclass();  
17          for (Method method : superClazz.getMethods()){  
18              System.out.println(method.getName());  
19          }
20          System.out.println("<----------------父類中所有的方法:僅僅父類中的方法-------------------->");
21          //拿到父類中的所有權限修飾符修飾的方法
22          for (Method method : superClazz.getDeclaredMethods()){  
23              System.out.println(method.getName());  
24          }       
25     }

 運行結果: 

<------------父類中能夠使用的公共權限的方法------------------------>
play_public
play_public_2
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
<----------------父類中所有的方法:僅僅父類中的方法-------------------->
play_private_2
play_public
play_public_2
play_private
play_protected
play_default
play_protected_2
play_default_2

 那么可以執行父類中的方法嗎? 

1 //是否可以通過子類對象拿到父類中的方法
2          Method  method3= clazz.getDeclaredMethod("play_public");
3          Method  method4= clazz.getDeclaredMethod("play_public_2",String.class);
4          Method  method5= clazz.getDeclaredMethod("play_private");
5          Method  method6= clazz.getDeclaredMethod("play_private_2",String.class);

 上面代碼報錯,說明不可以,public修飾的方法也拿不到 

1       Method  method7= superClazz.getDeclaredMethod("play_public");
2          Method  method8= superClazz.getDeclaredMethod("play_public_2",String.class);
3          Method  method9= superClazz.getDeclaredMethod("play_private");
4          Method  method10= superClazz.getDeclaredMethod("play_private_2",String.class);

  上面代碼正常執行,說明父類的反射對象可以拿到自己的public或private方法 

1  //使用子類的反射對象執行方法
2          method7.invoke(clazz.newInstance());
3          method8.invoke(clazz.newInstance(), "play_public_2有參數");
4          method9.invoke(clazz.newInstance());                       //無法執行,Junit報錯
5          method10.invoke(clazz.newInstance(), "play_private_2有參數");//無法執行,Junit報錯

  

1 //使用父類的反射對象執行方法
2          method7.invoke(superClazz.newInstance());
3          method8.invoke(superClazz.newInstance(), "play_public_2有參數");
4          method9.invoke(superClazz.newInstance());                       //無法執行,Junit報錯
5          method10.invoke(superClazz.newInstance(), "play_private_2有參數");//無法執行,Junit報錯

 上面代碼執行部分報錯,說明通過子類的反射對象和拿到的父類反射對象,也僅僅只能執行public和protected和default默認修飾的方法,不能執行private修飾的方法

7、反射機制獲取類中的字段屬性:Field字段 

 1    /**
 2      * 默認default修飾
 3      */
 4     String name;
 5     /**
 6      * private修飾
 7      */
 8     private int age;
 9     /**
10      * public修飾
11      */
12     public char sex='M';
13     /**
14      * protected修飾
15      */
16     protected boolean isBeauty=true;

 開始測試:如何獲取 

 1  /**
 2      * 反射機制獲取類中的字段屬性:Field字段
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchFieldException 
 6      * @throws IllegalAccessException 
 7      * @throws IllegalArgumentException 
 8      */
 9     @Test
10     public void testFiled() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
11          //拿到反射Class對象
12          Class clazz = Class.forName("com.xfwl.reflection.Person"); 
13          //獲取Field的數組,私有字段也能獲取
14          Field[] fields = clazz.getDeclaredFields(); 
15          System.out.println("<----遍歷拿到字段:開始--------------------->");
16          for (Field field: fields) {  
17                 System.out.println(field.getName());  
18          }
19          System.out.println("<------------獲取指定名字的Field以及類型--------------------------->");
20          //獲取指定名字的Field(如果是私有的,見下面的4)  
21          Field field1 = clazz.getDeclaredField("name");  
22          System.out.println("獲取指定Field名=: " + field1.getName()+",類型:"+field1.getType()); 
23          Field field2 = clazz.getDeclaredField("age");  
24          System.out.println("獲取指定Field名=: " + field2.getName()+",類型:"+field2.getType()); 
25          Field field3 = clazz.getDeclaredField("sex");  
26          System.out.println("獲取指定Field名=: " + field3.getName()+",類型:"+field3.getType()); 
27          Field field4 = clazz.getDeclaredField("isBeauty");  
28          System.out.println("獲取指定Field名=: " + field4.getName()+",類型:"+field4.getType()); 
29          
30          System.out.println("<----------獲取指定對象的Field的值 ----------------------------->");
31          Person person = new Person("小風微涼", 12,'M');  
32          //獲取指定對象的Field的值  
33          Object val = field1.get(person);  
34          System.out.println("獲取指定對象字段'name'的Field的值=: " + val);  
35         
36          System.out.println("<----------設置指定對象的Field的值----------------------------->");
37         //設置指定對象的Field的值  
38         field1.set(person, "反射學習A");  
39         System.out.println("設置指定對象字段'name'的Field的值=: " + person.name);  
40          
41         System.out.println("<----------若該字段是私有的,需要調用setAccessible(true)方法----------------------------->");
42         //若該字段是私有的,需要調用setAccessible(true)方法  
43         field2 = clazz.getDeclaredField("age");  
44         field2.setAccessible(true);  
45         System.out.println("獲取指定私有字段名=: " + field2.getName());  
46     }

 測試結果: 

<----遍歷拿到字段:開始--------------------->
name
age
sex
isBeauty
<------------獲取指定名字的Field以及類型--------------------------->
獲取指定Field名=: name,類型:class java.lang.String
獲取指定Field名=: age,類型:int
獲取指定Field名=: sex,類型:char
獲取指定Field名=: isBeauty,類型:boolean
<----------獲取指定對象的Field的值 ----------------------------->
有參構造!!!
獲取指定對象字段'name'的Field的值=: 小風微涼
<----------設置指定對象的Field的值----------------------------->
設置指定對象字段'name'的Field的值=: 反射學習A
<----------若該字段是私有的,需要調用setAccessible(true)方法----------------------------->
獲取指定私有字段名=: age

 8、反射機制獲取類中的構造器:構造器(Constructor)

Person的構造器

 1 /**
 2      * 無參構造
 3      */
 4     public Person(){
 5         System.out.println("無參構造!!!");
 6     }
 7     /**
 8      * 有參構造
 9      */
10     public Person(String name,int age,char sex){
11         System.out.println("有參構造!!!");
12         this.name=name;
13         this.age=age;
14         this.sex=sex;
15     }

 @Test測試 

 1 /** 
 2      * 構造器:開發用的比較少 
 3      */  
 4     @Test  
 5     public void testConstructor() throws ClassNotFoundException, NoSuchMethodException,  
 6             IllegalAccessException, InvocationTargetException, InstantiationException {  
 7         String className = "com.xfwl.reflection.Person";  
 8         Class<Person> clazz = (Class<Person>) Class.forName(className);  
 9       
10         //1.獲取Constructor對象  
11         Constructor<Person>[] constructors =  
12                 (Constructor<Person>[]) Class.forName(className).getConstructors();  
13 
14         System.out.println("<-------------打印所有的構造器---------------------->");
15         for (Constructor<Person> constructor: constructors) {  
16             System.out.println(constructor);  
17         }  
18         System.out.println("<------------------------------------------------>");
19         Constructor<Person> constructor = clazz.getConstructor(String.class, int.class,char.class);  
20         System.out.println("拿到指定的-->" + constructor);  
21       
22         //2.調用構造器的newInstance()方法創建對象  
23         Object obj= constructor.newInstance("changwen", 11,'M');  
24     }

  運行結果: 

<-------------打印所有的構造器---------------------->
public com.xfwl.reflection.Person()
public com.xfwl.reflection.Person(java.lang.String,int,char)
<------------------------------------------------>
拿到指定的-->public com.xfwl.reflection.Person(java.lang.String,int,char)
有參構造!!!

 9、反射機制獲取類中的注解:注解(Annotation) 

 •從 JDK5.0 開始,Java 增加了對元數據(MetaData)的支持,也就是Annotation(注釋) 
    •Annotation其實就是代碼里的特殊標記,這些標記可以在編譯,類加載, 運行時被讀取,並執行相應的處理.通過使用Annotation,程序員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補充信息. 
    •Annotation 可以像修飾符一樣被使用,可用於修飾包,類,構造器, 方法,成員變量, 參數,局部變量的聲明,這些信息被保存在Annotation的 “name=value”對中. 
    •Annotation能被用來為程序元素(類,方法,成員變量等)設置元數據 

基本的 Annotation 

•使用 Annotation時要在其前面增加@符號,並把該Annotation 當成一個修飾符使用.用於修飾它支持的程序元素 
•三個基本的Annotation: 
    –@Override:限定重寫父類方法,該注釋只能用於方法 
    –@Deprecated:用於表示某個程序元素(類,方法等)已過時 
    –@SuppressWarnings:抑制編譯器警告. 

自定義 Annotation 

    •定義新的 Annotation類型使用@interface關鍵字 
    •Annotation 的成員變量在Annotation 定義中以無參數方法的形式來聲明.其方法名和返回值定義了該成員的名字和類型. 
    •可以在定義Annotation的成員變量時為其指定初始值,指定成員變量的初始值可使用default關鍵字 
    •沒有成員定義的Annotation稱為標記;包含成員變量的Annotation稱為元數據Annotation  
 1 package com.xfwl.reflection;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 public class Person extends Human implements IHuman {
 9 
10     @Retention(RetentionPolicy.RUNTIME) //運行時檢驗  
11     @Target(value = {ElementType.METHOD})  //作用在方法上  
12     public @interface AgeValidator {  
13       
14         int min();  
15         int max();  
16     }
17     //......其余部分省略  
18 }

  

 1 /**
 2      * 自定義一個注解:檢查年齡范圍
 3      * @function  
 4      * @author 小風微涼
 5      * @time  2018-6-3 下午3:56:03
 6      */
 7     @Retention(RetentionPolicy.RUNTIME) //運行時檢驗  
 8     @Target(value = {ElementType.METHOD})  //作用在方法上  
 9     public @interface AgeValidator { 
10         int min();  
11         int max();  
12     } 

 @Test測試 

 1  /** 
 2      * 通過反射才能獲取注解 
 3      */  
 4     @Test  
 5     public void testAnnotation() throws Exception {  
 6         //這樣的方式不能使用注解  
 7         /*Person person3 = new Person();  
 8           person3.setAge(10);*/
 9         
10         //拿到反射Class對象
11         String className = "com.xfwl.reflection.Person";  
12         Class clazz = Class.forName(className);  
13         Object obj = clazz.newInstance();  
14         //拿到指定方法
15         Method method = clazz.getDeclaredMethod("setAge",int.class);  
16         int val =40;  
17       
18         //獲取注解  
19         Annotation annotation = method.getAnnotation(AgeValidator.class);  
20         if (annotation != null){  
21             if (annotation instanceof AgeValidator){  
22                 AgeValidator ageValidator = (AgeValidator) annotation;  
23       
24                 if (val< ageValidator.min() || val>ageValidator.max()){  
25                     throw new RuntimeException("數值超出范圍");  
26                 }  
27             }  
28         }  
29         //執行方法
30         method.invoke(obj, val);  
31         System.out.println(obj);  
32     }

 運行結果:

無參構造!!!
Person{name='null', age=40, sex='M'}

獲取指定注解:

 //獲取注解  
        Annotation annotation = method.getAnnotation(AgeValidator.class);  

 獲取所有注解:

1  //獲取所有注解
2         Annotation[] arr=clazz.getDeclaredAnnotations();

 提取 Annotation信息

•JDK5.0 在 java.lang.reflect包下新增了 AnnotatedElement接口,該接口代表程序中可以接受注釋的程序元素
•當一個 Annotation類型被定義為運行時Annotation后,該注釋才是運行時可見,當 class文件被載入時保存在 class文件中的 Annotation才會被虛擬機讀取
•程序可以調用AnnotationElement對象的如下方法來訪問 Annotation信息
–獲取 Annotation實例:
getAnnotation( Class<T> annotationClass)

JDK 的元Annotation

•JDK 的元Annotation 用於修飾其他Annotation 定義
•@Retention:只能用於修飾一個 Annotation定義,用於指定該 Annotation可以保留多長時間,@Rentention包含一個RetentionPolicy類型的成員變量,使用 @Rentention時必須為該 value成員變量指定值:
    –RetentionPolicy.CLASS:編譯器將把注釋記錄在 class文件中.當運行 Java程序時,JVM 不會保留注釋.這是默認值
    –RetentionPolicy.RUNTIME:編譯器將把注釋記錄在class文件中. 當運行 Java 程序時, JVM 會保留注釋. 程序可以通過反射獲取該注釋
    –RetentionPolicy.SOURCE:編譯器直接丟棄這種策略的注釋
•@Target: 用於修飾Annotation 定義,用於指定被修飾的 Annotation能用於修飾哪些程序元素.@Target 也包含一個名為 value的成員變量.
•@Documented:用於指定被該元 Annotation修飾的 Annotation類將被 javadoc工具提取成文檔.
•@Inherited:被它修飾的 Annotation將具有繼承性.如果某個類使用了被@Inherited 修飾的Annotation, 則其子類將自動具有該注釋 
 
后續補充整理:
(1)通過getDeclaredMethod拿到的方法,可以獲取個中修飾符修飾的方法名稱和對象,但是private的方法無法invoke執行
UserBean.java
 1 package com.xfwl.reflect;
 2 
 3 public class UserBean {
 4 
 5     private String uname;
 6     private String upwd;
 7     public UserBean(){
 8         this.setUname("xfwl");
 9         this.setUpwd("123456");
10     }
11     public UserBean(String uname,String upwd){
12         this.setUname(uname);
13         this.setUpwd(uname);
14     }
15     public void logIn(UserBean user){
16         System.out.println("用戶登錄:uname="+this.getUname()+",upwd="+this.getUpwd());
17     }
18     public String getUname() {
19         return uname;
20     }
21     private void logOut(UserBean user){
22         System.out.println("用戶退出:uname="+this.getUname()+",upwd="+this.getUpwd());
23     }
24     public void setUname(String uname) {
25         this.uname = uname;
26     }
27 
28     public String getUpwd() {
29         return upwd;
30     }
31 
32     public void setUpwd(String upwd) {
33         this.upwd = upwd;
34     }    
35 }
View Code

 測試類:ReflectAction.java

 1 package com.xfwl.reflect;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 
 6 public class ReflectAction {
 7     /**
 8      * @param args
 9      */
10     public static void main(String[] args) {
11         UserBean jack=null;
12         UserBean tom=null;
13         Class tomC=null;
14         try{
15             jack=(UserBean)Class.forName("com.xfwl.reflect.UserBean").newInstance();
16             //jack.logIn();
17             
18             tomC=Class.forName("com.xfwl.reflect.UserBean");
19             tom=(UserBean) tomC.newInstance();
20             Method[] methods = tomC.getDeclaredMethods(); 
21             for (Method method : methods){  
22                if("logOut".equals(method.getName())){
23                    method=tomC.getDeclaredMethod(method.getName(),UserBean.class);
24                    System.out.println(method);
25                    method.invoke(tomC.newInstance(),tom);
26                }
27             }             
28         }catch(Exception e){
29             
30             
31         }
32 
33     }
34 
35 }
View Code

 運行結果:沒有執行:logOut()

修改:

1 public void logOut(UserBean user){
2         System.out.println("用戶退出:uname="+this.getUname()+",upwd="+this.getUpwd());
3     }

  即可執行:logOut()


免責聲明!

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



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