1. 程式人生 > >帶你走進java.第二十四章.類的載入,反射

帶你走進java.第二十四章.類的載入,反射

類的載入

1.類是如何載入的?
一. 載入 (將類的.class檔案 載入到方法區中)
      建立一個class物件(代表.class檔案,相當於檔案物件)
二.連線
    1.驗證:驗證一下類的內部結構(成員變數 和方法[成員方法,構造方法] )
    2.準備: 為靜態變數開闢一塊空間 , 賦初值
    3.解析: 將區域性變數進行替換, 替換成對應的值
         如 int a = 10; 下面會將所有的a 替換成 10
三.初始化
     建立物件 (new一個物件,在堆記憶體開闢空間)

2.類是什麼時候被載入的? (載入時機)
  當你使用類的時候 該類一定會被載入
  載入該類時 會先載入該類的父類(保證繼承完整性)      

3.類是用什麼載入的? (使用 類載入器 載入類)
    1.根載入器[載入jdk的lib資料夾 jar檔案]
    2.拓展載入器[載入jdk的ext資料夾下 的檔案]
    3.系統載入器[自己自定義的類或者匯入的jar] 

反射

1.什麼是反射?
  反射可以將一個正在執行的類(載入完成的類) 
  通過這個.class檔案的class物件  
  可以直接獲取類內部的 所有成員變數和方法(包括私有的)

獲取構造方法

// 第一步. 獲取.class檔案的class物件
/*
 *  獲取class檔案物件
 *  1.通過物件獲取
 *  2.通過類獲取
 *  3.通過class中的靜態方法獲取(Class.ForName()   常用)
 */
// 1.通過物件獲取
Person p = new Person(); 
Class<? extends Person> c1 = p.getClass();// 獲取Person類的 class檔案物件
System.out.println(c1);
// 2.通過類獲取
Class<Person> c2 = Person.class;
System.out.println(c2);
// 3.常用(只需要一個字串,就能獲取該類的class物件)
// 注意:引數 要填全類名(包名+類名)
Class<?> c3 = Class.forName("com.reflect.Person");
System.out.println(c3);
// 獲取公開的構造方法         getConstructors()

// 通過class檔案物件 獲取類中構造方法
Class<?> c = Class.forName("com.reflect.Person");
// 獲取所有public公開的構造方法
Constructor<?>[] constructors = c.getConstructors();
// 遍歷
for (Constructor<?> constructor : constructors) {
	System.out.println(constructor);
}
// 獲取單個構造方法
Constructor<?> constructor = c.getConstructor();
// 利用該構造方法建立物件
Object object = constructor.newInstance();
System.out.println(object);
// 呼叫   有參的構造方法,建立物件
// **獲取有參構造方法的引數是  有參構造方法的引數類的檔案型別**
Constructor<?> constructor2 = c.getConstructor(String.class,int.class);
// 建立物件
Object object2 = constructor2.newInstance("張三",30);
System.out.println(object2);
// 通過class檔案物件,快速建立物件   newInstance()
/*
 * 通過Class檔案物件 快速建立物件
 * 前提: 
 * 1.類必須用public修飾
 * 2.必須提供無參構造方法
 * 3.該無參的構造方法必須是public修飾
 */
Class<?> c = Class.forName("com.reflect.Person");
// 快速建立物件(直接使用類中的無參構造方法建立)
Object object = c.newInstance();
System.out.println(object);

// 獲取所有的構造方法,包括私有構造方法    getDeclaredConstructors()
// 特殊: 要開啟私有方法的使用許可權     setAccessible(true)

Class<?> c = Class.forName("com.reflect.Person");
//獲取類中所有的構造方法(包括私有)
Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
	System.out.println(constructor);
}
// 獲取私有的構造方法
Constructor<?> declaredConstructor = c.getDeclaredConstructor(int.class,String.class);
// 開啟私有方法的使用許可權
declaredConstructor.setAccessible(true);
// 建立物件
Object object = declaredConstructor.newInstance(30,"張三");
System.out.println(object);

獲取成員變數

// 獲取所有的公開的成員變數
Class<?> c = Class.forName("com.reflect.Person");
Field[] fields = c.getFields();
for (Field field : fields) {
	System.out.println(field);
}
// 單獨獲取指定的成員變數   
// 引數是  成員變數的名稱
Field field = c.getField("name");
// 建立一個Person物件
Object object = c.newInstance();
// 通過反射,給(物件)該屬性賦值(value)
field.set(object, "wangjun");
// 檢視物件的成員變數
System.out.println(object);
		
// 獲取私有的成員變數
Field declaredField = c.getDeclaredField("age");
// 開啟私有的許可權
declaredField.setAccessible(true);
// 設定私有的成員變數
declaredField.set(object, 23);
// 列印物件的成員變數
System.out.println(object);
例題 : 
/*
利用反射完成以下方法 
1.此方法可將obj物件中名為propertyName的屬性的值設定為value.
  相當於用反射 寫一個set方法 
  引數:
  1.賦值的物件
  2.屬性名字
  3.給該屬性賦的值
2.此方法可以獲取obj物件中名為propertyName的屬性的值
  相當於用反射 寫一個get方法 
  引數:
  1.賦值的物件
  2.要獲取的屬性的名
*/
public static void main(String[] args) throws Exception {
		// 測試
		Class<?> c = Class.forName("com.lanou3g.reflect.Person");
		Object object = c.newInstance();
		setName(object, "name", "王五");
		System.out.println(object);
		Object name = getName(object, "name");
		System.out.println(name);
	}
	// 賦值
	public static void setName(Object object,String name,Object value) throws Exception{
		// 獲取.class檔案物件
		Class<? extends Object> class1 = object.getClass();
		// 從檔案中提取屬性
		Field declaredField = class1.getDeclaredField(name);
		// 開啟許可權
		declaredField.setAccessible(true);
		// 賦值
		declaredField.set(object, value);
	}
	
	public static Object getName(Object object,String name) throws Exception {
		// 獲取.class檔案物件
		Class<? extends Object> c = object.getClass();
		// 獲取對應的成員變數
		Field declaredField = c.getDeclaredField(name);
		// 開啟許可權
		declaredField.setAccessible(true);
		// 從物件中獲取name的值
		Object value = declaredField.get(object);
		return value;
	}

獲取成員方法

Class<?> c = Class.forName("com.reflect.Person");
// 獲取類中所有的公開的成員方法(包括父類的)
Method[] methods = c.getMethods();

// for (Method method : methods) {
// 	System.out.println(method);
// }

// 獲取指定的成員方法
// 引數1 獲取的方法名
// 引數2 獲取的  成員方法的引數的class型別(沒引數就不填)
Method method = c.getMethod("speak",String.class);
// 建立一個物件
Object object = c.newInstance();
// 呼叫該方法
// 引數1: 呼叫該方法的物件
// 引數2: 呼叫該方法 要傳入的引數
// 返回值: 呼叫該方法的返回值
Object invoke = method.invoke(object,"王五6666");
System.out.println(invoke);
		
		
		
// 獲取所有的成員方法(包括私有)
Method[] declaredMethods = c.getDeclaredMethods();
// 獲取私有方法
Method declaredMethod = c.getDeclaredMethod("play", String.class);
// 開啟許可權
declaredMethod.setAccessible(true);
// 呼叫方法   
Object invoke2 = declaredMethod.invoke(object, "lol");
System.out.println(invoke2);
例題 
/*
 * 利用反射 
 * 向ArraryList<String>中 新增 Integer資料 Person物件
 * 注意: 編譯成class檔案 泛型不存在
 */
ArrayList<String> list = new ArrayList<>();
// 獲取.class檔案物件
Class<?> c = Class.forName("java.util.ArrayList");
// 獲取所有方法
Method declaredMethod = c.getDeclaredMethod("add",Object.class);
// 開啟許可權
declaredMethod.setAccessible(true);
// 呼叫方法
Object invoke = declaredMethod.invoke(list, 123);
Object invoke2 = declaredMethod.invoke(list, new Person("張三", 23));
Object invoke3 = declaredMethod.invoke(list, "哈哈");
System.out.println(list);