1. 程式人生 > >JAVA:反射(參考書籍:《JAVA程式設計的邏輯》)

JAVA:反射(參考書籍:《JAVA程式設計的邏輯》)

反射

類的載入

當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過 **載入,連線,初始化 **三步來實現對這個類進行初始化。

載入:就是指將class檔案讀入記憶體,併為之建立一個Class物件。任何類被使用時系統都會建立一個Class物件。

連線:驗證,是否有正確的內部結構,並和其他類協調一致。準備,負責為類的靜態成員分配記憶體,並設定預設初始化值。解析,將類的二進位制資料中的符號引用替換為直接引用。

初始化

類初始化時機

  1. 建立類的例項

  2. 類的靜態變數,或者為靜態變數賦值

  3. 類的靜態方法

  4. 使用反射方式來強制建立某個類或介面對應的java.lang.Class物件

  5. 初始化某個類的子類

  6. 直接使用java.exe命令來執行某個主類

反射

反射是在執行時,而非編譯時,動態獲取型別的資訊,比如介面資訊,成員資訊,方法資訊,構造方法資訊,再根據這些動態獲取到的資訊來建立物件,訪問、修改成員,使用方法。 ------- 《JAVA程式設計的邏輯》

Class類

Class沒有公共的構造方法。注意Class類不是class,大小寫要區分。這裡的前兩種獲取Class類物件的方法需要明確Person型別。而第三種需要指定這種型別的字串就行。

//通過Object類中的getClass()方法 , 返回Object類的執行時類
Person p =
new Person(); Class c = p.getClass(); //通過 類名.class 獲取到位元組碼檔案物件 Class c2 = Person.class; //將類名作為字串傳遞給Class類中的靜態方法forName Class c3 = Class.forName("Person");

1、Class是個泛型類,有一個型別引數,getClass()並不知道具體的型別,只能返回 Class<?>

2、介面也有Class物件。

3、**基本型別 **沒有getClass()方法,但都有對應的Class物件。void 作為特殊的返回型別,也有Class物件。

//基本型別。這裡就不列舉完了,還有Double,Character等
Class<Integer> inCls = int.class;
Class<Byte> byteCls = byte.class;
。。。。
//void型別
Class<void> voidCls = void.class;

4、對於陣列,每種型別的陣列都有對應的Class物件,每個 **維度 **都有一個。

獲取構造方法並使用

在反射機制中,把類中的成員(構造方法、成員方法、成員變數)都封裝成了對應的類進行表示。

其中,構造方法使用類 Constructor 表示。

//獲取public修飾, 指定引數型別所對應的構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//獲取指定引數型別所對應的構造方法(包含私有的)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//獲取所有的public 修飾的構造方法
public Constructor<?>[] getConstructors()
//獲取所有的構造方法(包含私有的)
public Constructor<?>[] getDeclaredConstructors()
    
    
    
    
    
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		//獲取Class物件
		Class c = Class.forName("cn.itcast_01_Reflect.Person");//包名.類名
		
		//獲取所有的構造方法
         //這裡不止有一個構造方法,所以運用集合來儲存
		Constructor[] cons = c.getDeclaredConstructors();
		//用迴圈來列印方法
        for (Constructor con : cons) {
			System.out.println(con);
		}
		
		System.out.println("------------------------");
		//獲取一個構造方法,這裡getConstructor的引數就是指定構造方法的引數。
		//public Person() 
		Constructor con1 = c.getConstructor(null);
		System.out.println(con1);
		
		//public Person(String name)
		Constructor con2 = c.getConstructor(String.class);
		System.out.println(con2);
		
		//private Person(String name, int age)
		Constructor con3 = c.getDeclaredConstructor(String.class, int.class);
		System.out.println(con3);
		
		//public Person(String name, int age, String address)
		Constructor con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
		System.out.println(con4);
	}
}

獲取構造方法,建立物件

獲取 **構造方法 **,步驟如下:

  1. 獲取到Class物件。
  2. 獲取指定的構造方法。
  3. 通過構造方法類 Constructor 中的方法,建立物件。

獲取私有構造方法,建立物件

獲取 私有構造方法 ,步驟如下:

  1. 獲取到Class物件。
  2. 獲取指定的構造方法。
  3. 暴力訪問, 通過 setAccessible(boolean flag)方法,這個方法可以忽略JAVA的訪問許可權。
  4. 通過構造方法類 Constructor 中的方法,建立物件。

AccessibleObject 類是 Field、Method 和 Constructor 物件的 父類。它提供了將反射的物件標記為在使用時取消預設 Java 語言訪問控制檢查的能力,也就是可以打破JAVA的訪問許可權。

//引數值為 true 則指示反射的物件在使用時應該取消 Java 語言訪問檢查。引數值為 false 則指示反射的物件應該實施 Java 語言訪問檢查。
public void setAccessible(boolean flag) throws SecurityException

獲取成員變數並使用

在反射機制中,把類中的成員變數使用類 Field 表示。

//獲取指定的 public修飾的變數
public Field getField(String name)
//獲取指定的任意變數
public Field getDeclaredField(String name)
//獲取所有public 修飾的變數
public Field[] getFields()
//獲取所有的 變數 (包含私有)
public Field[] getDeclaredFields()
    
    
    
    
public class FieldDemo {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		//獲取Class物件
		Class c = Class.forName("cn.itcast_01_Reflect.Person");
		
		//獲取成員變數
		//多個變數,運用陣列來儲存
		Field[] fields =  c.getDeclaredFields();
         //遍歷陣列,訪問陣列
		for (Field field : fields) {
			System.out.println(field);
		}
		System.out.println("-----------------");
		//一個變數
		//public int age;
		Field ageField = c.getField("age");
		System.out.println(ageField);
		
		//private String address,這裡是私有變數,也能被獲取。
		Field addressField = c.getDeclaredField("address");
		System.out.println(addressField);
	}
}

成員變數進行賦值與獲取值操作

獲取成員變數,步驟如下:

  1. 獲取Class物件

  2. 獲取構造方法

  3. 通過構造方法,建立物件

  4. 獲取指定的成員變數(私有成員變數,通過setAccessible(boolean flag)方法暴力訪問)

//在指定物件obj中,將此 Field 物件表示的成員變數設定為指定的新值
public void set(Object obj, Object value)
//返回指定物件obj中,獲取物件的值
public Object get(Object obj)
    
    
  	     //1,獲取Class物件
		Class c = Class.forName("cn.itcast_01_Reflect.Person");
		//2,獲取構造方法
		//public Person(String name, int age, String address)
		//這裡要傳入對應構造方法的引數型別的class物件!!!!!!!!!
		Constructor con = c.getConstructor(String.class, int.class, String.class);
		//3,通過構造方法,建立物件
		Object obj = con.newInstance("小明", 23, "哈爾濱");
		//4,獲取指定的方法
		//public void method1()  沒有返回值沒有引數的方法
		//Method m1 = c.getMethod("method1", null);

		//public String method4(String name)
		Method m4 = c.getMethod("method4", String.class);
		
		//5,執行找到的方法
		//m1.invoke(obj, null);
		
		Object result = m4.invoke(obj, "itcast");

獲取成員方法並使用

在反射機制中,把類中的成員方法使用類 Method 表示。

//獲取 public 修飾的方法
public Method getMethod(String name, Class<?>... parameterTypes)
//獲取任意的方法,包含私有的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//引數1: name 要查詢的方法名稱; 引數2: parameterTypes 該方法的引數型別
   
    
//這裡因為返回不只一個方法,因此運用Method的陣列來存放。類似前面的。
//獲取本類與父類中所有public 修飾的方法
public Method[] getMethods()
//獲取本類中所有的方法(包含私有的)
public Method[] getDeclaredMethods()

建立物件呼叫指定的方法

//執行指定物件obj中,當前Method物件所代表的方法,方法要傳入的引數通過args指定
public Object invoke(Object obj,  Object... args)

建立物件呼叫指定的 private 方法

獲取私有成員方法,步驟如下:

  1. 獲取Class物件
  2. 獲取構造方法
  3. 通過構造方法,建立物件
  4. 獲取指定的方法
  5. 開啟暴力訪問
  6. 執行找到的方法

也就是運用 setAccessible(true); 開啟暴力訪問。

總結

1、獲取.Class檔案物件的方法三種。

2、獲取構造方法。

3、獲取成員變數。

4、獲取成員方法。

5、暴力破除JAVA語言侷限,setAccessible (true)。