天天看點

深入解析Java反射原理

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]
	}
}