反射的概述
對於一個在運行狀態中的類,使用反射機制可以知道這個類的所有屬性和方法;對於任何一個對象,使用反射機制都可以調用它的任意一個方法和屬性。這種動態獲取信息以及動態調用對象方法的功能稱爲Java語言的反射機制。
Class類
Class類的實例表示正在運行的Java應用程序中的類和接口。也就是jvm中有N多的實例每個類都有該Class對象,甚至包括基本數據類型。
Class類沒有公共的構造方法。它是由java虛擬機以及調用類加載器中的defineClass方法自動構造的。
獲取class對象的三種方式
- ‘類的引用’.getClass()
- 類名.class
- Class.forName(“類的全路徑”)
package com.xiaopeng.reflect;
/**
* 獲取 Class 文件的三種方式
* 1、'類的引用'.getClass()
* 2、類.class
* 3、Class.forName('classPath');
* ----------------------------------
* 在運行期間,一個類,只有一個Class對象產生
*/
public class Fanshe {
public static void main(String[] args) {
Student student = new Student();
Class stuClazz = student.getClass();
//輸出class文件的全路徑
System.out.println("類Student-->" + stuClazz.getName());
//判斷兩個class文件是否相同
Class stuClazz2 = Student.class;
System.out.println(stuClazz == stuClazz2);
try {
Class stuClass3 = Class.forName("com.xiaopeng.reflect.Student");
System.out.println(stuClass3 == stuClazz2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在程序運行期間,一個類只產生一個Class對象。一般常用的第三種方法來獲取類的Class對象。
1. 第一種方式已經獲取了對象的引用,不需要獲取Class對象
2. 第二種方式需要導入類的全路徑,程序的依賴性強
3. 傳入一個字符串,獲取類的Class對象,可以使用配置文件配置該字符串
獲取構造方法並調用
//獲取所有公有的構造方法
Constructor[] consArray = clazz.getConstructors();
//獲取所有的構造方法(private/protected/public/..)
Constructor[] consArray = clazz.getDeclaredConstructors();
//獲取公有無參數的構造方法,形參表示構造方法中參數的類型String.class/int.class
Constructor con = clazz.getConstructor(null);
//調用類的構造方法
con = clazz.getDeclaredConstructor(int.class); //獲取構造方法
con.setAccessible(true); //如果構造方法是私有的,那麼需要修改爲"允許訪問"
Object o = con.newInstance(24); //通過構造方法創建對象的引用
獲取成員變量並調用
//獲取所有公有的字段
Field[] fieldArray = clazz.getFields();
//獲取所有成員變量
Field[] fieldArray = clazz.getDeclaredFields();
//使用成員變量
Object obj = clazz.getConstructor().newInstance(); //獲取對象的引用
Field f = clazz.getField("address"); //獲取對象的address成員變量
f.setAccessible(true); //若是 private 屬性,需要設置訪問權限
f.set(obj,"shanxi"); //將address屬性賦值爲shanxi
People p = (People)obj; //類型轉換
獲取成員方法並調用
//獲取所有公有方法
Method[] methodArray = clazz.getMethods();
//獲取所有成員方法
Method[] methodArray = clazz.getDeclaredMethods();
//獲取某個特定的方法,並調用方法
People p = clazz.getConstructor().newInstance(); //獲取對象的引用
Method m = clazz.getMethod("sayHello",String.class,int.class); //private String sayHello(String message,int times)
m.setAccessible(true); //私有成員方法需要設置訪問權限
Object o = m.invoke(p,"你好",20); //調用sayHello方法,並獲取輸出
反射獲取並調用main方法
Method mainMethod = clazz.getMethod("main",String[].class);
/**
* 因爲main方法是靜態方法,所以對象的引用傳入null即可
**/
mainMethod.invoke(null,(Object)new String[]{"q","w","e"})
使用泛型越過泛型檢查
從 jdk1.5 開始,引入的泛型的新特性,泛型用在編譯期,編譯過後泛型會被檫除掉。所以可以通過反射越過泛型檢查。
public static void main(String[] args){
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("aaa");
arrayList.add("bbb");
//arrayList.add(100);
Class clazz = arrayList.getClass();
try {
Method m = clazz.getMethod("add",Object.class);
m.invoke(arrayList,100);
} catch (Exception e) {
e.printStackTrace();
}
for(Object o : arrayList){
System.out.println(o);
}
}
Java的反射這麼靈活,無非是將原來需要在“編譯時”必須要做的事推遲到了“運行時”,而方式也由原來的對象變成了可以靈活配置的字符串。