1. 程式人生 > >Java反射篇學習筆記

Java反射篇學習筆記

nsh boolean 案例 即使 框架 ade 原理 劉德華 種類型

今天重新學習了java中的反射,寫一篇學習筆記總結一下。代碼基本都是照著兩篇博客敲的:

參考一: https://blog.csdn.net/sinat_38259539/article/details/71799078

參考二: https://blog.csdn.net/liujiahan629629/article/details/18013523

其中,第一篇比較詳細點。作為初學者,我覺得不能根據晦澀的概念強行去理解一個知識點,而應該結合具體的應用實例。

先初步理解一下反射的作用:

(1)反編譯,將 .class文件轉化為 .java文件

(2)通過class對象來訪問其對應類的對象的屬性、方法、構造方法。

1、Class類

先來理解一下Class這個特殊的的類,在平時敲代碼的時候,偶爾會寫到,this.getClass( )這樣的方法,但是卻不知道是什麽意思。查一下API可知,Class類也同樣繼承了Object類,Class類的實例用來表示正在運行的java應用程序中的類和接口,也就是說jvm中已經存在對象的類、已經被加載進去的類,都會有對應的Class實例(這也包括基本的數據結構),詳細圖解見第一篇推薦博客。

註意點:

(1)Class類保存的是對應類的信息,比如現在有一個Student類,現在通過new 方法 產生了一個stu1的實例,那麽jvm會把Stduent類加載進內存,通過類加載器中等defineClass方法構造 一個對應的Class對象,來保存Student的一些信息,包括其中的一些方法、屬性、構造方法等。

(2)同一種類型的對象或者數據類型,共享一個class實例。比如說,Student方法繼續產生stu2、stu3.。。。等等的對象,即使再多,也只有一個對應的Class實例來保存他們的信息。

(3)通過Class實例,可以反向地獲取對應類的信息,如果指定對象(例如 stu1), 可以反向獲得他的屬性、方法等信息。

2、反射的使用

下面舉的例子都是來自上面推薦的第一篇博客,我自己也敲了一遍,學習一下。

(1)獲取Class對象的方法

 1 package fanshe;
 2 /**
 3  * 
 4  * 獲取class對象的三種方式
 5  * 1、object ——> getClass();
6 * 2、任何數據類型(包括基本數據類型)都有一個靜態的class屬性。 7 * 3、通過class類的靜態方法:forName(String className)(常用) 8 * 9 */ 10 public class Demo_1 { 11 12 public static void main(String[] args) { 13 //第一種獲得class對象的方法 14 Student stu1 = new Student();//這一個new產生一個Student對象,一個Class對象 15 Class stuClass = stu1.getClass();//獲取class對象 16 System.out.println(stuClass.getName()); 17 18 //第二種方式獲取class對象 19 Class stuClass2 = Student.class; 20 System.out.println(stuClass == stuClass2); 21 22 //第三種方式獲取class對象 23 try { 24 Class stuClass3 = Class.forName("fanshe.Student");//帶包名 25 System.out.println(stuClass3 == stuClass2); 26 } catch (ClassNotFoundException e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } 30 31 } 32 }

Student:

class Student{
    Student(){}
}

結果:

fanshe.Student
true
true

總結:第三種方法比較常見,不用導包,也不用知道對象。上面的測試也說明了,同一個對象共享一個Class實例,一個類只產生一個class實例。

2)通過反射獲取構造函數

Student類:

package fanshe;

public class Student {

    //默認構造方法
    Student(String str){
        System.out.println("(默認)的默認構造方法 s = " + str);
    }
    
    //無參構造方法
    public Student() {
        System.out.println("調用了共有的、無參構造方法執行");
    }
    
    //有一個參數的構造方法
    public Student(char name) {
        System.out.println("姓名: " + name);
    }
    
    //有多個參數的構造方法
    public Student(String name, int age) {
        System.out.println("姓名: " + name+ "年齡:" +age);//這的執行效率有問題
    }
    
    //受保護的構造方法
    protected Student(boolean n) {
        System.out.println("受保護的構造方法 n= " + n);
    }
    
    //私有構造方法
    private Student(int age) {
        System.out.println("私有構造方法  年齡:" + age);
    }
}

測試類:

 1 package fanshe;
 2 import java.lang.reflect.Constructor;
 3 /**
 4  * 
 5  * 通過Class對象可以獲取某個類中的構造方法、成員變量、成員方法:並訪問成員;
 6  * 1、獲取構造方法:
 7  *        (1)批量的方法
 8  *             public Constructor[] getConstructor(); 所有“公有的”構造方法
 9  *             public Constructor[] getDeclaredConstructors(); 獲取所有的構造方法
10  *             
11  *        (2)獲取單個的方法,並調用:
12  *             public Constructor getConstructor(Class parameterTypes):獲取單個公有的構造方法
13  *             public Constructor getDeclaredConstructor(Class parameterTypes);獲取某個構造方法可以是私有的,或者是受保護的
14  *             、默認的。公有的:
15  *             調用構造方法
16  *             Construtor -->newInstance(Object、、、initargs)
17  *             
18  *
19  */
20 public class Test_1 {
21 
22     public static void main(String[] args)throws Exception{
23         //1、加載class對象
24         Class clazz = Class.forName("fanshe.Student");
25         
26         //2、獲取所有公有構造方法
27         System.out.println("*****************************所有公有構造方法*************");
28         Constructor[] conArray = clazz.getConstructors();
29         for(Constructor c : conArray) {
30             System.out.println(c);
31         }
32         
33         System.out.println("********所有的構造方法(包括私有、受保護、默認、公有)********");
34         conArray = clazz.getDeclaredConstructors();
35         for(Constructor c : conArray) {
36             System.out.println(c);
37         }
38         
39         System.out.println("********獲取公有的 、無參的構造方法");
40         Constructor con = clazz.getConstructor(null);
41          //1>、因為是無參的構造方法所以類型是一個null,不寫也可以:這裏需要的是一個參數的類型,切記是類型  
42          //2>、返回的是描述這個無參構造函數的類對象。  
43         
44         System.out.println("con = "+con);
45         
46         //調用構造方法
47         Object obj = con.newInstance();
48         
49         
50         System.out.println("******************獲取私有構造方法,並調用*******************************"); 
51         con = clazz.getDeclaredConstructor(char.class);
52         System.out.println(con);
53         
54         //調用構造函數
55         con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
56         obj = con.newInstance(‘男‘);        
57     }    
58 }

結果:

*****************************所有公有構造方法*************
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
********所有的構造方法(包括私有、受保護、默認、公有)********
private fanshe.Student(int)
protected fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
fanshe.Student(java.lang.String)
********獲取公有的 、無參的構造方法
con = public fanshe.Student()
調用了共有的、無參構造方法執行
******************獲取私有構造方法,並調用*******************************
public fanshe.Student(char)
姓名: 男

(3)獲取成員變量並調用

Stduent類:

package fanshe;

public class Student_1 {

    public Student_1() {
        
    }
    //******字段******//
    public String name;
    protected int age;
    char sex;
    private String phoneNum;
    
    public String toString() {
        return "Student [name="+name+",age="+age+",sex="+sex+", phoneNum="+phoneNum+"]";
    }
}

測試類:

 1 /**
 2  * 
 3  * 獲取成員變量並調用: 
 4  *  
 5  * 1.批量的 
 6  *      1).Field[] getFields():獲取所有的"公有字段" 
 7  *      2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有; 
 8  * 2.獲取單個的: 
 9  *      1).public Field getField(String fieldName):獲取某個"公有的"字段; 
10  *      2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的) 
11  *  
12  *   設置字段的值: 
13  *      Field --> public void set(Object obj,Object value): 
14  *                  參數說明: 
15  *                  1.obj:要設置的字段所在的對象; 
16  *                  2.value:要為字段設置的值; 
17  */
18 public class Test_2 {
19 
20     public static void main(String[] args) throws Exception {
21         //1.獲取Class對象  
22         Class stuClass = Class.forName("fanshe.Student_1");  
23         //2.獲取字段  
24         System.out.println("************獲取所有公有的字段********************");  
25         Field[] fieldArray = stuClass.getFields();  
26         for(Field f : fieldArray){  
27             System.out.println(f);  
28         }  
29         System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");  
30         fieldArray = stuClass.getDeclaredFields();  
31         for(Field f : fieldArray){  
32             System.out.println(f);  
33         }  
34         System.out.println("*************獲取公有字段**並調用***********************************");  
35         Field f = stuClass.getField("name");  
36         System.out.println(f);  
37         //獲取一個對象  
38         Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();  
39         //為字段設置值  
40         f.set(obj, "劉德華");//為Student對象中的name屬性賦值--》stu.name = "劉德華"  
41         //驗證  
42         Student_1 stu = (Student_1)obj;  
43         System.out.println("驗證姓名:" + stu.name);  
44           
45           
46         System.out.println("**************獲取私有字段****並調用********************************");  
47         f = stuClass.getDeclaredField("phoneNum");  
48         System.out.println(f);  
49         f.setAccessible(true);//暴力反射,解除私有限定  
50         f.set(obj, "18888889999");  
51         System.out.println("驗證電話:" + stu); 
52     }
53 }

結果:

************獲取所有公有的字段********************
public java.lang.String fanshe.Student_1.name
************獲取所有的字段(包括私有、受保護、默認的)********************
public java.lang.String fanshe.Student_1.name
protected int fanshe.Student_1.age
char fanshe.Student_1.sex
private java.lang.String fanshe.Student_1.phoneNum
*************獲取公有字段**並調用***********************************
public java.lang.String fanshe.Student_1.name
驗證姓名:劉德華
**************獲取私有字段****並調用********************************
private java.lang.String fanshe.Student_1.phoneNum
驗證電話:Student [name=劉德華,age=0,sex=

(4)獲取成員方法並調用

Stduent類

 1 package fanshe;
 2 
 3 public class Student_3 {
 4 
 5     //**************成員方法***************//  
 6     public void show1(String s){  
 7         System.out.println("調用了:公有的,String參數的show1(): s = " + s);  
 8     }  
 9     protected void show2(){  
10         System.out.println("調用了:受保護的,無參的show2()");  
11     }  
12     void show3(){  
13         System.out.println("調用了:默認的,無參的show3()");  
14     }  
15     private String show4(int age){  
16         System.out.println("調用了,私有的,並且有返回值的,int參數的show4(): age = " + age);  
17         return "abcd";  
18     }  
19 }

測試類:

 1 package fanshe;
 2 import java.lang.reflect.Method;
 3 
 4 /**
 5 * 獲取成員方法並調用: 
 6  *  
 7  * 1.批量的: 
 8  *      public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類) 
 9  *      public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的) 
10  * 2.獲取單個的: 
11  *      public Method getMethod(String name,Class<?>... parameterTypes): 
12  *                  參數: 
13  *                      name : 方法名; 
14  *                      Class ... : 形參的Class類型對象 
15  *      public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 
16  *  
17  *   調用方法: 
18  *      Method --> public Object invoke(Object obj,Object... args): 
19  *                  參數說明: 
20  *                  obj : 要調用方法的對象; 
21  *                  args:調用方式時所傳遞的實參; 
22  *
23  */
24 
25 public class Test_3 {
26 
27     public static void main(String[]args) throws Exception{
28          //1.獲取Class對象  
29         Class stuClass = Class.forName("fanshe.Student_3");  
30         //2.獲取所有公有方法  
31         System.out.println("***************獲取所有的”公有“方法*******************");  
32         stuClass.getMethods();  
33         Method[] methodArray = stuClass.getMethods();  
34         for(Method m : methodArray){  
35             System.out.println(m);  
36         }  
37         System.out.println("***************獲取所有的方法,包括私有的*******************");  
38         methodArray = stuClass.getDeclaredMethods();  
39         for(Method m : methodArray){  
40             System.out.println(m);  
41         }  
42         System.out.println("***************獲取公有的show1()方法*******************");  
43         Method m = stuClass.getMethod("show1", String.class);  
44         System.out.println(m);  
45         //實例化一個Student對象  
46         Object obj = stuClass.getConstructor().newInstance();  
47         m.invoke(obj, "劉德華");  
48           
49         System.out.println("***************獲取私有的show4()方法******************");  
50         m = stuClass.getDeclaredMethod("show4", int.class);  
51         System.out.println(m);  
52         m.setAccessible(true);//解除私有限定  
53         Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參  
54         System.out.println("返回值:" + result);  
55     }
56 }

結果:

***************獲取所有的”公有“方法*******************
public void fanshe.Student_3.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************獲取所有的方法,包括私有的*******************
protected void fanshe.Student_3.show2()
private java.lang.String fanshe.Student_3.show4(int)
void fanshe.Student_3.show3()
public void fanshe.Student_3.show1(java.lang.String)
***************獲取公有的show1()方法*******************
public void fanshe.Student_3.show1(java.lang.String)
調用了:公有的,String參數的show1(): s = 劉德華
***************獲取私有的show4()方法******************
private java.lang.String fanshe.Student_3.show4(int)
調用了,私有的,並且有返回值的,int參數的show4(): age = 20
返回值:abcd

(5)反射main方法

student類:

package fanshe;

public class Student_4 {

    public static void main(String[]args) {
        System.out.println("main 方法執行了。。。");
    }
}

測試類:

 1 package fanshe;
 2 import java.lang.reflect.Method;
 3 /** 
 4  * 獲取Student類的main方法、不要與當前的main方法搞混了 
 5  */  
 6 public class Test_4 {
 7 
 8     public static void main(String[]args) {
 9         try {  
10             //1、獲取Student對象的字節碼  
11             Class clazz = Class.forName("fanshe.Student_4");  
12               
13             //2、獲取main方法  
14              Method methodMain = clazz.getMethod("main", String[].class);//第一個參數:方法名稱,第二個參數:方法形參的類型,  
15             //3、調用main方法  
16             // methodMain.invoke(null, new String[]{"a","b","c"});  
17              //第一個參數,對象類型,因為方法是static靜態的,所以為null可以,第二個參數是String數組,這裏要註意在jdk1.4時是數組,jdk1.5之後是可變參數  
18              //這裏拆的時候將  new String[]{"a","b","c"} 拆成3個對象。。。所以需要將它強轉。  
19              methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一  
20             // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二  
21               
22         } catch (Exception e) {  
23             e.printStackTrace();  
24         }       
25     }
26 }

結果:

main 方法執行了。。。

(6)通過反射運行配置文件

student類:

1 public class Student {  
2     public void show(){  
3         System.out.println("is show()");  
4     }  
5 } 

配置文件,以txt為例(pro.txt)

1 className = cn.fanshe.Student  
2 methodName = show 

測試類:

import java.io.FileNotFoundException;  
import java.io.FileReader;  
import java.io.IOException;  
import java.lang.reflect.Method;  
import java.util.Properties;  
  
/* 
 * 我們利用反射和配置文件,可以使:應用程序更新時,對源碼無需進行任何修改 
 * 我們只需要將新類發送給客戶端,並修改配置文件即可 
 */  
public class Demo {  
    public static void main(String[] args) throws Exception {  
        //通過反射獲取Class對象  
        Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"  
        //2獲取show()方法  
        Method m = stuClass.getMethod(getValue("methodName"));//show  
        //3.調用show()方法  
        m.invoke(stuClass.getConstructor().newInstance());  
          
    }  
      
    //此方法接收一個key,在配置文件中獲取相應的value  
    public static String getValue(String key) throws IOException{  
        Properties pro = new Properties();//獲取配置文件的對象  
        FileReader in = new FileReader("pro.txt");//獲取輸入流  
        pro.load(in);//將流加載到配置文件對象中  
        in.close();  
        return pro.getProperty(key);//返回根據key獲取的value值  
    }  
}  

結果:

is show()

需求:更新系統時,只要再寫一個Student2類,更改一下配置文件,而不需要動原來的類。

Student2類:

1 public class Student2 {  
2     public void show2(){  
3         System.out.println("is show2()");  
4     }  
5 }  

配置文件:

className = cn.fanshe.Student2  
methodName = show2 

(7)通過反射越過泛型檢查

測試類:

 1 import java.lang.reflect.Method;  
 2 import java.util.ArrayList;  
 3   
 4 /* 
 5  * 通過反射越過泛型檢查 
 6  *  
 7  * 例如:有一個String泛型的集合,怎樣能向這個集合中添加一個Integer類型的值? 
 8  */  
 9 public class Demo {  
10     public static void main(String[] args) throws Exception{  
11         ArrayList<String> strList = new ArrayList<>();  
12         strList.add("aaa");  
13         strList.add("bbb");  
14           
15     //  strList.add(100);  
16         //獲取ArrayList的Class對象,反向的調用add()方法,添加數據  
17         Class listClass = strList.getClass(); //得到 strList 對象的字節碼 對象  
18         //獲取add()方法  
19         Method m = listClass.getMethod("add", Object.class);  
20         //調用add()方法  
21         m.invoke(strList, 100);  
22           
23         //遍歷集合  
24         for(Object obj : strList){  
25             System.out.println(obj);  
26         }  
27     }  
28 }  

輸出:

aaa
bbb
100

總結:再次感謝這篇博客的博主:https://blog.csdn.net/sinat_38259539/article/details/71799078

其清晰的表述,具體的案例,讓我進一步了解了反射機制的一些應用,等以後學習框架或者工作的時候,再根據具體情況深入地學習反射的原理。通過反射機制的初步學習,對面向對象這個概念也有了更進一步的體會,原來一個類中的方法,屬性,構造方法,也可以抽象並封裝為一個類(Field,Method,Constructor),果然面向對象,一切皆對象。

如有錯誤,歡迎批評指正。

Java反射篇學習筆記