1. 程式人生 > >java反射機制解析

java反射機制解析

第1章 反射
1.1 類的載入
當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類進行初始化。
 載入
就是指將class檔案讀入記憶體,併為之建立一個Class物件。
任何類被使用時系統都會建立一個Class物件
 連線
驗證 是否有正確的內部結構,並和其他類協調一致
準備 負責為類的靜態成員分配記憶體,並設定預設初始化值
解析 將類的二進位制資料中的符號引用替換為直接引用
 初始化
就是我們以前講過的初始化步驟
1.2 類的初始化
Java類只要滿足以下任意一條,都講完成類的初始化。

  1. 建立類的例項
  2. 類的靜態變數,或者為靜態變數賦值
  3. 類的靜態方法
  4. 使用反射方式來強制建立某個類或介面對應的java.lang.Class物件
  5. 初始化某個類的子類
  6. 直接使用java.exe命令來執行某個主類

到目前為止我們已經知道把class檔案載入到記憶體做了哪些事情了,那麼, 如果我們僅僅站在這些class檔案的角度,我們如何來使用這些class檔案中的內容呢?那就是我們反射將要學習的內容了。

1.3 反射概述
Java反射機制是在執行狀態中,對指定的類,任意的方法或任意的欄位進行操作,這種動態獲取的資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。
簡而言之,反射就是:在執行時通過程式碼操作類。
想要操作類,就必須先獲得該類的位元組碼物件Class。通過Class提供的方法對類進行進一步的解剖,從而實現各種操作。
在進行具體操作之前,大家需要先掌握類各個組成的專業描述
 Class類
 Constructor 構造
 Method 方法
 Field 欄位
 instance 例項
 invoke 執行

1.4 準備資料

	新增欄位
public class Bean {
   private String id;
   private String className;

	Bean類提供getter和setter方法
public String getId() {
   System.out.println("getId方法執行");
   return id;
}
public void setId(String id) {
   System.out.println("setId方法執行:" + id);
   this.id = id;
}

1.5 Class獲得方式

//1 通過型別獲得
// 語法:類名.class
// 應用場景:確定型別 等
Class clazz1 = Bean.class;

//2 通過例項物件獲得
// 語法:變數.getClass()
// 應用場景:在方法內部通過引數獲得型別 等
Bean bean = new Bean();
Class clazz2 = bean.getClass();

//3 通過字串獲得
// 語法:Class.forName(“全限定類名”)
// 應用場景:通過配置獲得字串 等
Class clazz3 = Class.forName(“com.itheima_00_Bean.Bean”);
1.6 構造方法與例項

1.6.1 新增構造

	修改Bean ,新增構造方法
public Bean() {
   System.out.println("無參構造");
}

public Bean(String id, String className) {
   this.id = id;
   this.className = className;
   System.out.print("有參構造:" + id);
   System.out.println(" ," + className);
}

1.6.2 無參構造

	無參構造,並獲得例項

@Test
public void testDefaultCons() throws Exception{
   //無參構造 , 並例項化
   
   //1獲得Class
   Class beanClass = Bean.class;
   
   //2獲得構造 -- 沒有形參
   Constructor constructor = beanClass.getConstructor();
   
   //3 例項物件,沒有實參
   Object bean = constructor.newInstance();
   System.out.println(bean);
   
   /* 結果:
    * 無參構造
    * [email protected]
    */
}

1.6.3 有參構造
 有參構造,並獲得例項

@Test
public void testParamCons() throws Exception{
   //有參構造 , 並例項化
   
   //1獲得Class
   Class beanClass = Bean.class;
   
   //2獲得構造 -- 兩個字串形參 -- Bean(String id, String className)
   Constructor constructor = beanClass.getConstructor(String.class,String.class);
   
   //3 例項物件,兩個字串實參
   Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList");
   System.out.println(bean);
   
   /* 結果:
    * 有參構造:ArrayListId ,java.util.ArrayList
    * [email protected]
    */
}

1.6.4 無參構造,簡化版獲得例項
 無參構造,簡化版獲得例項

@Test
public void testDefaultSimple() throws Exception{
   //無參構造 , 簡化版
   
   //1獲得Class
   Class beanClass = Bean.class;
   
   //2 直接獲得例項物件,兩個字串實參
   Object bean = beanClass.newInstance();
   System.out.println(bean);
   
   /* 結果:
    * 無參構造
    * [email protected]
    */
}

1.6.5 擴充套件:私有構造(暴力反射)
 修改Bean新增私有構造

private Bean(String id) {
   this.id = id;
   System.out.println("有參構造:" + id);
}
	getConstructor() 使用該方法將無法獲得私有方法,程式執行拋異常

	沒有使用setAccessible(true),將拋異常
   		 

@Test
public void testPrivateCons() throws Exception{
   //私有構造 
   
   //1獲得Class
   Class beanClass = Bean.class;
   
   //2獲得構造 -- 兩個字串形參 -- Bean(String id, String className)
   // * getConstructor() 將拋異常  java.lang.NoSuchMethodException
   // * getDeclaredConstructor 可以獲得私有構造
   Constructor constructor = beanClass.getDeclaredConstructor(String.class);
   
   //暴力訪問
   constructor.setAccessible(true);
   
   //3 例項物件,兩個字串實參
   Object bean = constructor.newInstance("userId");
   System.out.println(bean);
   
   /* 結果:
    * 有參構造:userId 
    * [email protected]
    */
}

1.7 方法與執行

1.7.1 public方法
 獲得方法並設定

@Test
public void testMethod() throws Exception{
   
   //1 獲得Class
   Class clazz = Class.forName("com.itheima_00_Bean.Bean");
   
   //2 獲得例項 ,相當於  Object obj = new Bean();
   Object obj = clazz.newInstance(); 
   
   
   //3 操作setId方法
   //3.1 獲得的方法,一個形參
   // * 格式:getMethod(方法名,形成列表)
   Method setMethod = clazz.getMethod("setId", String.class);
   //3.2 執行方法,一個實參
   Object setReturnObj = setMethod.invoke(obj, "我是引數");
   
   System.out.println("set方法返回值:" + setReturnObj);
   
   /* 執行結果:
    * 	無參構造
    * 	setId方法執行:我是引數
    * 	set方法返回值:null
    */
   
   System.out.println("---------------");
   
   //4 操作getId方法 (鞏固)
   // 3.1 獲得方法,沒有形參
   Method getMethod = clazz.getMethod("getId");
   // 3.2 執行方法,沒有實參
   Object getReturnObj = getMethod.invoke(obj);
   
   System.out.println("get方法返回值:" + getReturnObj);
   
   /* 執行結果:
    *	getId方法執行
    *	get方法返回值:我是引數
    */
}

1.7.2 擴充套件:私有方法(暴力反射)
 修改bean,新增方法:

//私有方法
private String show(){
   System.out.println("私有方法執行");
   return "Bean["+id+", "+ className +"]";
}
	getMethod() 使用該方法將無法獲得私有方法,程式執行拋異常

	沒有使用setAccessible(true),將拋異常



@Test
public void testPrivateMethod() throws Exception{
   //1 獲得Class
   Class clazz = Class.forName("com.itheima_00_Bean.Bean");
   
   //2獲得構造 -- 兩個字串形參 -- Bean(String id, String className)
   Constructor constructor = clazz.getConstructor(String.class,String.class);
   
   //3 例項物件,兩個字串實參
   Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList");
   
   //3獲得方法 -- 私有方法 -- private String show()
   // * getMethod(方法名,形成列表) 將拋異常  java.lang.NoSuchMethodException
   // * getDeclaredMethod(方法名,形成列表) 可以獲得私有構造
   Method showMethod = clazz.getDeclaredMethod("show");
   
   //暴力訪問
   showMethod.setAccessible(true);
   
   // 4 執行方法,沒有實參
   Object getReturnObj = showMethod.invoke(bean);
   System.out.println("show方法返回值:" + getReturnObj);
   
   /* 執行結果:
    *	有參構造:ArrayListId ,java.util.ArrayList
    *	私有方法執行
    *	show方法返回值:Bean[ArrayListId, java.util.ArrayList]
    */
}

1.7.3 擴充套件:main方法與執行
 修改Bean,新增main方法

public static void main(String[] args) {
   for (int i = 0; i < args.length; i++) {
   	System.out.print(args[i] +", ");
   }
}

 通過反射,呼叫執行main方法

@Test
public void testMainMethod() throws Exception{
   //呼叫main方法
   
   //1 獲得Class
   Class clazz = Class.forName("com.itheima_00_Bean.Bean");
   
   //2獲得方法 -- main靜態方法 -- public static void main(String[] args)
   Method mainMethod = clazz.getMethod("main", String[].class);
   Object getReturnObj = mainMethod.invoke(null, (Object)new String[]{"aaa","bbb"});
   
   System.out.println();
   System.out.println("main方法返回值:" + getReturnObj);
   
   /*
    * 執行結果:
    * 	aaa, bbb, 
    *	main方法返回值:null
    */
}

1.8 欄位(成員變數)與資料操作(瞭解)
1.8.1 新增public欄位
 Bean類提供成員變數 address

public class Bean {
   private String id;
   private String className;
   public String description;
1.8.2	public欄位的操作
@Test
public void testField() throws Exception{
   /* 獲得例項,為public欄位賦值、獲取值
    * public String description;
    */
   
   //1 獲得Class
   Class clazz = Class.forName("com.itheima_00_Bean.Bean");
   
   //2 獲得例項 ,相當於  Object obj = new Bean();
   Object bean = clazz.newInstance(); 
   
   //3 操作欄位,進行賦值,public String description;
   //3.1 獲得的欄位,一個形參
   // * 格式:getField(欄位名)
   Field descriptionField = clazz.getField("description");
   //3.2 為物件的欄位賦值
   descriptionField.set(bean, "Bean的描述");
   
   //3.3 獲取物件的欄位值
   Object fieldReturnObj = descriptionField.get(bean);
   System.out.println("description欄位返回值:"+fieldReturnObj);
}

1.8.3 擴充套件:私有欄位(暴力反射)
 getField() 使用該方法將無法獲得私有欄位,程式執行拋異常

 沒有使用setAccessible(true),將拋異常

@Test
public void testPrivateField() throws Exception{
   //私有欄位 className
   
   //1獲得Class
   Class clazz = Bean.class;
   
   //2 獲得例項 ,相當於  Object obj = new Bean();
   Object bean = clazz.newInstance(); 
   
   //3獲得欄位 -- 私有欄位 --  className
   // * getField() 將拋異常  java.lang.NoSuchMethodException
   // * getDeclaredField 可以獲得私有欄位
   Field classNameField = clazz.getDeclaredField("className");
   
   //暴力訪問
   classNameField.setAccessible(true);
   
   //3.2 為物件的欄位賦值
   classNameField.set(bean, "Bean的類名稱");
   
   //3.3 獲取物件的欄位值
   Object fieldReturnObj = classNameField.get(bean);
   System.out.println("description欄位返回值:"+fieldReturnObj);
   
   
   /* 結果:
    * 無參構造
    * description欄位返回值:Bean的類名稱
    */
}