你必須瞭解的Java:反射

你必須瞭解的Java:反射

首先,反射是框架設計的靈魂 ,這句話的分量,讀者自行體會。

1、幾個概念:

  • 框架:半成品軟件。可以在框架的基礎上進行軟件開發,簡化編碼;所謂框架,就是能夠適應所有的情況,情況的不同,只需在配置文件中進行修改,而不是在原代碼(這個指的框架)中修改;
  • 反射:將類的各個組成部分封裝爲其他對象,這就是反射機制。
  • 好處:
      1. 可以在程序運行過程中,操作這些對象。idea 就是在程序的運行過程中,運用了反射的機制。 比如:定義了一個字符串,結果字符串的方法就已經可以選取調用了。·因爲在這個過程中,idea是一邊寫,一邊編譯,將字符串編譯爲字節碼文件,並且已經將字節碼文件通過ClassLoader加載進了內存中;
    •         2. **可以解耦,提高程序的可擴展性。**
      

2、java在計算機中經歷的三個階段:

java代碼 ------>javac進行編譯------>字節碼文件.class------>classLoader類加載器------>在堆中創建對象 new someoneClass
source源代碼階段 class類對象階段 RUNTIME運行時階段

3、反射中的對象:

​ 1、成員變量 Field[] field

​ 2、構造方法 Constructor[]

​ 3、成員方法 Method[] method

4、獲取Class對象的方式

  1. Class.forName(“全類名”):將字節碼文件加載進內存,返回Class對象
    多用於配置文件,將類名定義在配置文件中,配置文件中寫的都是全類名。讀取文件,加載類;

  2. 通過類名屬性class獲得:( 多用於參數的傳遞)

//  ClassName.class  不執行靜態塊和不執行動態塊兒
Class<?> class = ClassName.class;
  1. 對象.getClass() 封裝在object中:getClass()方法在Object類中定義着。
    多用於對象的獲取該類的 字節碼文件 的方式

注意: 前兩個是直接根據類名獲取class文件 區別是全類名 或者類名(與絕對路徑和相對路徑相似)第三個是需要先創建類的對象,再有對象獲取類的字節碼文件。

結論: 三種獲取的方式都是同一個字節碼文件(*.class)。在一次程序運行過程中,一個類只會被加載一次,所以不論通過哪一種方式獲取的Class對象都是同一個。

靜態代碼塊與動態代碼塊在 獲取class文件中的應用:

首先定義一個測試類:

 public class ClassTest {
     static{
         // 靜態代碼塊
         System.out.println("靜態代碼塊兒...");
     }
     {
         // 動態代碼塊
         System.out.println("動態構造塊兒...");
     }
     public ClassTest(){
         // 類中的構造方法
         System.out.println("構造方法...");   
     }
 }

使用第1種方法:

public class MainTest {
     public static void main(String[] args) {
         try {
             Class<?> calss = Class.forName("com.souche.lease.admin.mytest.reflect.ClassTest");
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         }
     }
}
/* 打印結果是:
 * 靜態代碼塊兒...
 * 說明Class.forName("類名全路徑")執行靜態塊但是不執行動態塊兒(需要異常處理)
 */

使用第2種方法:

public class MainTest {
    public static void main(String[] args) {
        Class<?> calss = ClassTest.class;
    }
}
/**
 * 打印結果是:
 * 什麼都沒打印
 * 說明ClassName.class不執行靜態塊和不執行動態塊兒
 */

使用第3種方法:

public class MainTest {
     public static void main(String[] args) {
         Class<?> calss = new ClassTest().getClass();
     }
 }
 
 /**
  * 打印結果是:
  * 靜態代碼塊兒...
    動態構造塊兒...
    構造方法...
  * 說明對象.getClass()執行靜態塊也執行動態塊兒
  */

關於獲取class文件方法不同的總結:

第1種方法:類字面常量使得創建Class對象的引用時不會自動地初始化該對象,而是按照之前提到的加載,鏈接,初始化三個步驟,這三個步驟是個懶加載的過程,不使用的時候就不加載。

第2種方法:Class類自帶的方法。

第3種方法:是所有的對象都能夠使用的方法,因爲getClass()方法是Object類的方法,所有的類都繼承了Object,因此所有類的對象也都具有getClass()方法。

建議:使用類名.class,這樣做即簡單又安全,因爲在編譯時就會受到檢查,因此不需要置於try語句塊中,並且它根除了對forName()方法的調用,所以也更高效。

補充注意:靜態塊僅在類加載時執行一次,若類已加載便不再重複執行;而動態構造塊在每次new對象時均會執行。

補充:生成對象四種方式

請參見blog: https://blog.csdn.net/qq_39817135/article/details/101313225

5、Class對象功能( 就是獲取class文件之後,拿它來做什麼。)

1. 獲取 成員變量們 field

獲取了成員變量之後,就可以將獲取的成員變量進行設置、獲取(也就是get和set)原類中成員變量的值。從而讓其達到不創建對象就可以操作類中成員變量Field,構造方法Constructor,成員方法Method三個大的類對象;

    Field[] getFields()// 獲取所有 public 修飾的成員變量  
			
	Field getField(String name)      // 獲取的指定的成員變量  獲取指定名稱的 public修飾的成員變量
			
	Field[] getDeclaredFields()      // 獲取所有的成員變量,不考慮修飾符
			
	Field getDeclaredField(String name)   //獲取的指定的成員變量
			
	Field[] getDeclaredFields();	//獲取所有的成員變量,不考慮修飾符

2. 舉例說明 成員變量類對象 的獲取,並設置、獲取 成員變量類對象中的值

   // e.g. 成員變量類對象 的獲取,並設置、獲取 成員變量類對象中的值
    Field[] declaredFields = personClass.getDeclaredFields(); 
    
	// 獲取了所有的成員變量,並且將其放在了數組中;並可以將其遍歷:
    for (Field declaredField : declaredFields) {
        System.out.println(declaredField);
    }
    
	// 獲取指定的成員變量,如果成員變量是私有的,那麼需要將獲取權限  即暴力反射獲得權限
    Field getDeclaredField(String name)
    Field d = personClass.getDeclaredField("d");

	// 成員變量類對象d 就可以進行d.get();d.set();
    // 忽略訪問權限修飾符的安全檢查
    d.setAccessible(true);     //暴力反射
    
	d.set(oo, "wangwu")
    Object value2 = d.get(p);
    System.out.println(value2);

3、獲取構造方法們 :之後就創建對象

Constructor<?>[]  getConstructors()  	   //獲取的是空參構造

Constructor<T>  getConstructor(<?>... parameterTypes) 
    
 //  獲取的是有參構造
	Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  
	Constructor<?>[] getDeclaredConstructors()  
		
//Constructor<T> getConstructor(類<?>... parameterTypes)
 Constructor constructor = personClass.getConstructor(String.class, int.class);
 //這裏返回得到的是  構造器		
 //這裏傳遞的參數 
 //String.class,int.class是原構造方法中參數類型的class對象
 System.out.println(constructor);
    /**創建對象****重要:constructor.newInstance("張三", 23)    
	  **當然也可以利用空參構造,創建對象:但是提供了比較簡單的方法:
	  **直接跳過獲取構造器,直接使用類名就可以利用空參構造的創建對象:
	  **	Class對象的newInstance方法
	  **	personClass.newInstance()*/
    Object person = constructor.newInstance("張三", 23);
    System.out.println(person);

4、 獲取成員方法們:之後就執行方法

Method[] getMethods()  
Method getMethod(String name, 類<?>... parameterTypes)  
Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, 類<?>... parameterTypes)  
    	//獲取指定名稱的方法 其實是總的方法類對象
	    Method eat_method = personClass.getMethod("eat"); 
		// 這裏是無參方法,可以不用傳遞參數類型的對象
	     Person p = new Person();

 //獲取到成員方法之後,需要執行方法,關鍵詞:.invoke(p)  
 //獲取的法類對象來執行 需要傳遞的是一個對象,以及該方法執行所需的參數類對象
   eat_method.invoke(p);	  // 這裏是無參方法,可以不用傳遞參數類型的對象
 	// 有參方法舉例並說明:
       Method eat_method = personClass.getMethod("eat",String.class,int.class,...); 
       eat_method.invoke(p,"abcdef",6);
			
	 //獲取所有public修飾的方法
    Method[] methods = personClass.getMethods();
    for (Method method : methods) {
        System.out.println(method);
        String name = method.getName();
		//獲取所有的方法名,
		//這裏獲取的方法名不僅僅是方法中的public修飾的方法名,
		//還有其繼承Object中的方法
        System.out.println(name);
        //method.setAccessible(true);
    }
   }		
		/* Field:成員變量
	獲取了成員變量之後,就可以用將獲取的成員變量進行設置、獲取原類中成員變量的值。
	這樣就可以 不通過構造方法,對目標類(最開始的類)進行獲取或者設置成員變量的值。*/
	// 操作:
		//1. 設置值
			* void set(Object obj, Object value)  
		//2. 獲取值
			* get(Object obj) 
		//3. 忽略訪問權限修飾符的安全檢查
			* setAccessible(true):   暴力反射(私有的成員變量都可以訪問)
			
// Constructor:構造方法
	// 創建對象:
		 T newInstance(Object... initargs)  
		// 如果使用空參數構造方法創建對象,操作可以簡化:
			Class對象的newInstance方法

// Method:方法對象
	// 執行方法:
		 Object invoke(Object obj, Object... args)  
	// 獲取方法名稱:
		 String getName:獲取方法名

面試題:

需求

寫一個"框架",不能改變該類的任何代碼的前提下,
可以幫我們創建任意類的對象,並且執行其中任意方法。

回答思路

實現

  1. 配置文件 properties

  2. 反射(如果配置文件中 出現了全類名,絕大部分情況就是運用了反射)

    步驟

    1. 將需要創建的對象的全類名和需要執行的方法定義在配置文件中
    2. 在程序中加載讀取配置文件
    3. 使用反射技術來加載類文件進內存
    4. 創建對象
    5. 執行方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章