1、什麼是反射
Java
反射就是在運作時,對于任意一個類,程式都能擷取這個類的所有屬性和方法。對于任意一個對象,程式都能調用它的所有方法和屬性。這種動态擷取類的資訊和動态調用對象方法和屬性的功能成為
Java
的反射機制。
Java反射就是把類的各種成分映射成一個個Java對象
要想解析一個類,首先需要擷取到這個類的位元組碼檔案對象,也就是
.class
檔案。而且一個類有且隻有一個
.class
檔案。
2、怎麼擷取類的位元組碼檔案
Java提供了四種方式來擷取位元組碼檔案對象。如
cn.com.kk.TestClass
這個類
(1)知道具體的類時
通過此方法擷取的Class對象不會初始化。
(2)通過執行個體對象擷取Class對象
TestClass test = new TestClass();
Class tem = test.getClass();
(3)通過類的加載器擷取
通過此方法擷取的Class對象不會初始化。
(4)通過
Class.forName()
傳入類路徑擷取(推薦)
Class.forName()
方法内部調用的是一個
native
方法
forName0(className, true, ClassLoader.getClassLoader(caller), caller)
;第二個參數表示類是否需要初始化,
Class.forName(className)
方法預設是需要初始化的。
一旦初始化,就是出發類的
static
代碼塊執行,
static
參數也會初始化。
3、如何使用反射
先設定一個Person類
package cn.com.kk.test;
public class Person {
private Integer age;
protected String name;
public Boolean sex;
Double money;
Person(String name) {};
public Person(){}
public Person(Integer age) {
}
public Person(Integer age, String name) {
super();
this.age = age;
this.name = name;
}
protected Person(Integer age, String name, String sex) {
}
private Person(Boolean sex) {
}
public String getName() {
return name;
}
protected boolean getSex() {
return sex;
}
private Integer setAge(Integer age) {
this.age = age;
return age;
}
// 測試main方法的反射
public static void main(String[] args) {
System.out.println("main方法執行了。。。");
}
}
通過反射擷取并操作這個類的相關屬性和方法
public class Test {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.jiuqi.kk.test.Person");
// 調用構造方法
// 擷取所有的公有構造方法
Constructor[] conArray = clazz.getConstructors();
for (Constructor con : conArray) {
System.out.println(con);
}
System.out.println("----------------------------------------");
// 擷取所有的構造方法
conArray = clazz.getDeclaredConstructors();
for (Constructor con : conArray) {
System.out.println(con);
}
System.out.println("----------------------------------------");
// 擷取公有、無參的構造方法
Constructor con = clazz.getConstructor(null);
System.out.println(con);
Object obj = con.newInstance(); // 執行個體化一個Person對象
System.out.println("----------------------------------------");
// 擷取私有、有參的構造方法
con = clazz.getDeclaredConstructor(Boolean.class);
System.out.println(con);
con.setAccessible(true); // 暴力通路,忽略通路修飾符
obj = con.newInstance(true); // 調用構造方法
System.out.println("----------------------------------------");
// 調用方法
// 擷取所有的公有方法
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------------------------------");
// 擷取所有的方法
methods = clazz.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------------------------------");
// 擷取公有的getName()方法
Method m = clazz.getMethod("getName", null);
System.out.println(m);
System.out.println("----------------------------------------");
// 擷取私有的setAge()方法
m = clazz.getDeclaredMethod("setAge", Integer.class);
System.out.println(m);
Object o = clazz.getConstructor().newInstance(); // 執行個體化一個person對象
m.setAccessible(true); // 忽略私有修飾符
Object result = m.invoke(o, 24); // 調用方法給參數指派,第一個是要調用的對象(反射擷取),
//第二個是傳入的實參。傳回值是該方法的傳回值。
System.out.println(result); // 24
// 擷取main方法
Method methodMain = clazz.getMethod("main", String[].class);//第一個參數:方法名稱,第二個參數:方法形參的類型,
//3、調用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一個參數,對象類型,因為方法是static靜态的,是以為null可以,
//第二個參數是String數組,這裡要注意在jdk1.4時是數組,jdk1.5之後是可變參數
//這裡拆的時候将 new String[]{"a","b","c"} 拆成3個對象。。。是以需要将它強轉。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});
}
}
4、反射的實際運用
(1)通過反射運作配置檔案内容
還是以Person類為例,相關配置檔案
pro.txt
如下
className = com.jiuqi.kk.test.Person
methodName = setAge
methodValue = 25
測試類
public class Test {
public static void main(String[] args) throws Exception {
//通過反射擷取Class對象
Class stuClass = Class.forName(getValue("className"));
//2擷取setAge()方法
Method m = stuClass.getMethod(getValue("methodName"), Integer.class);
//3.調用setAge()方法
Object o = m.invoke(stuClass.getConstructor().newInstance(),
Integer.valueOf(getValue("methodValue")));
System.out.println(o); // 25
}
//此方法接收一個key,在配置檔案中擷取相應的value
public static String getValue(String key) throws IOException{
Properties pro = new Properties();//擷取配置檔案的對象
FileReader in = new FileReader("pro.txt");//擷取輸入流
pro.load(in);//将流加載到配置檔案對象中
in.close();
return pro.getProperty(key);//傳回根據key擷取的value值
}
}
(2)通過反射越過泛型檢查
Java泛型是僞泛型,因為Java在編譯期間,所有的泛型資訊會被擦除掉,也叫類型擦除。
是以可以通過反射來越過泛型檢查
public class Test {
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<String>();
list.add("aa");
list.add("bb");
System.out.println(list);// [aa,bb]
//通過反射擷取Class對象
Class clazz = list.getClass();
//2擷取add()方法
Method m = clazz.getMethod("add", Object.class);
//3.調用add()方法
m.invoke(list, 25);
System.out.println(list); // [aa,bb,25]
}
}