1. 程式人生 > >簡單介紹下反射機制[中高]?

簡單介紹下反射機制[中高]?

反射是框架設計的靈魂.
(使用的前提條件:必須先得到代表的位元組碼的Class,Class類用於表示.class檔案(位元組碼))

什麼是反射?
Java反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為Java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的位元組碼檔案物件。而解剖使用的就是Class類中的方法.所以先要獲取到每一個位元組碼檔案對應的Class型別的物件.

Class是反射的基石。
1、Class是一個類,一個描述類的類(也就是描述類本身),封裝了描述方法的Method,描述欄位的Filed,描述構造器的Constructor等屬性,通過反射可以得到類的各種成分。
2、對於每個類而言,JRE 都為其保留一個不變的 Class 型別的物件。一個 Class 物件包含了特定某個類的有關資訊。
3、Class 物件只能由jvm建立
4、一個類在 JVM 中只會有一個Class例項
5、反射相關的類這個包下:java.lang.reflect

獲取Class物件的三種方式要知道。

  1. Object ——> getClass();
  2. 任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性
  3. 通過Class類的靜態方法:Class.forName(String className)(常用,一般都是使用這個)
    參考程式碼: GetClassTest
// 獲取Class物件的三種方式測試
public class GetClassTest {

	public static void main(String[] args) {
		// 第一種方式獲取Class物件
		Student stu1 = new Student();
		// 獲取Class物件
		Class stuClass = stu1.getClass();
		System.out.println(stuClass.getName());

		// 第二種方式獲取Class物件
		Class stuClass2 = Student.class;
		// 判斷第一種方式獲取的Class物件和第二種方式獲取的是否是同一個
		System.out.println("第一種和第二種方式的class是否相等:" + (stuClass == stuClass2));

		// 第三種方式獲取Class物件,一般都是用這種方式,強烈推薦這種方式
		try {
			// 注意此字串必須是真實路徑,就是帶包名的類路徑,包名.類名
			Class stuClass3 = Class.forName("com.cto.edu.basic.Student");
			// 判斷三種方式是否獲取的是同一個Class物件
			System.out.println("第二種和第三種方式的class是否相等:" + (stuClass3 == stuClass2));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

}

專案中使用的很多的例子:
使用jdbc連結資料庫的時,都用的反射,一般都是通過配置檔案書寫連結哪個資料庫,比如mysql、orcle等,以及對應的關鍵資訊。

簡單總結說:
反射就是把java類中的各種成分對映成一個個的Java物件。
例如:一個類有:成員變數、方法、構造方法、包等等資訊,利用反射技術可以對一個類進行解剖,把個個組成部分對映成一個個物件。
核心的幾個類:
Class類:代表一個類
Constructor類:代表類的構造方法
Filed類:代表類的成員變數
Method類:代表類的方法

參考程式碼: ConstructorsTest FieldsTest

/* 
 * 利用反射獲取構造方法測試
 * 通過Class物件可以獲取某個類中的:構造方法、成員變數、成員方法;並訪問成員; 
 *  
 * 1.獲取構造方法: 
 *      1).批量的方法: 
 *          public Constructor[] getConstructors():所有"公有的"構造方法 
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、預設、公有) 
      
 *      2).獲取單個的方法,並呼叫: 
 *          public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法: 
 *          public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、預設、公有; 
 *       
 *          呼叫構造方法: 
 *          Constructor-->newInstance(Object... initargs) 
 */  
public class ConstructorsTest {  
    public static void main(String[] args) throws Exception {  
        //載入Class物件  
        Class cla = Class.forName("com.cto.edu.basic.Student");  
          
        System.out.println("**********************所有公有構造方法*********************************");  
        Constructor[] conArray = cla.getConstructors();  
        for(Constructor c : conArray){  
            System.out.println(c);  
        }  
        System.out.println();
          
          
        System.out.println("************所有的構造方法(包括:私有、受保護、預設、公有)***************");  
        conArray = cla.getDeclaredConstructors();  
        for(Constructor c : conArray){  
            System.out.println(c);  
        }  
        System.out.println();
        System.out.println("*****************獲取公有、無參的構造方法*******************************");  
//      Constructor con = cla.getConstructor();  
        Constructor con = cla.getConstructor(null);  
        //1>、因為是無參的構造方法所以型別是一個null,不寫也可以:這裡需要的是一個引數的型別,切記是型別  
        //2>、返回的是描述這個無參建構函式的類物件。  
        System.out.println("con = " + con);  
        
        //呼叫構造方法  
        Object obj = con.newInstance();  
        
        System.out.println();
        System.out.println("******************獲取私有構造方法,並呼叫*******************************");  
        con = cla.getDeclaredConstructor(int.class);  
        System.out.println(con);  
        //呼叫構造方法  
        con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)  
        obj = con.newInstance(20);  
    }  
      
}  
/* 
 * 利用反射獲取成員變數並呼叫測試: 
 *  
 * 1.批量的 
 *      1).Field[] getFields():獲取所有的"公有欄位" 
 *      2).Field[] getDeclaredFields():獲取所有欄位,包括:私有、受保護、預設、公有; 
 * 2.獲取單個的: 
 *      1).public Field getField(String fieldName):獲取某個"公有的"欄位; 
 *      2).public Field getDeclaredField(String fieldName):獲取某個欄位(可以是私有的) 
 *  
 *   設定欄位的值: 
 *      Field --> public void set(Object obj,Object value): 
 *                  引數說明: 
 *                  1.obj:要設定的欄位所在的物件; 
 *                  2.value:要為欄位設定的值; 
 *  
 */  
public class FieldsTest {  
  
        public static void main(String[] args) throws Exception {  
            //獲取Class物件  
            Class stuClass = Class.forName("com.cto.edu.basic.Student");  
            
            System.out.println("************獲取所有公有的欄位********************");  
            Field[] fieldArray = stuClass.getFields();  
            for(Field f : fieldArray){  
                System.out.println(f);  
            }  
            System.out.println();
            System.out.println("************獲取所有的欄位(包括私有、受保護、預設的)********************");  
            fieldArray = stuClass.getDeclaredFields();  
            for(Field f : fieldArray){  
                System.out.println(f);  
            }  
            System.out.println();
            System.out.println("*************獲取公有欄位**並呼叫***********************************");  
            Field f = stuClass.getField("name");  
            System.out.println(f);  
            //獲取一個物件 --》相當於Student stu = new Student();
            Object obj = stuClass.getConstructor().newInstance();  
            //為欄位賦值  
            f.set(obj, "張三");//為Student物件中的name屬性賦值--》stu.name = "張三"  
            //驗證  
            Student stu = (Student)obj;  
            System.out.println("驗證姓名:" + stu.name);  
              
            System.out.println(); 
            System.out.println("**************獲取私有欄位****並呼叫********************************");  
            f = stuClass.getDeclaredField("tellphone");  
            System.out.println(f);  
            f.setAccessible(true);//暴力反射,解除私有限定  
            f.set(obj, "13512345678");  
            System.out.println("驗證電話:" + stu.getTellphone());  
              
        }  
    }

參考文章