Java反射學習筆記

一 定義及功能

       Java的反射機制指的是,Java程序在運行過程中,對於任意一個類,都能夠動態的獲得這個類的任意的屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取對象屬性和方法的功能稱爲Java語言的反射機制。

        Java反射機制可以提供一下功能:
1 在運行時判斷任意一個對象所屬的類;
2 在運行時調用任意一個對象的方法;
3 在運行時判斷任意一個類所具有的的成員變量和方法;
4 在運行時構造任意一個類的對象;
5 生成動態代理;
      我們首先使用一個例子來說明Java反射機制是如何工作的。 

<span style="font-size:18px;">package reflect.test;</span>
<span style="font-size:18px;">
import java.lang.reflect.Method;

//測試類
class Demo {
	
}

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {
		
	<span style="white-space:pre">	</span>Demo demo = new Demo();
		System.out.println(demo.getClass().getName());
	}
}</span>

輸出結果:

reflect.test.Demo


      這樣就可以獲得demo對象所屬的類的命名空間和類名。這個過程使用了對象的getClass()方法來載入指定的類,然後調用getName()方法來獲取名稱。


二 獲取步驟及方式

使用Java的反射機制,需要遵循三個步驟:
1 獲得你要操作的類的Class對象;
2 通過第一步獲得的class對象,獲取要操作的類的方法名或者屬性對象;

3 操作第二步獲取的屬性或者對象。

Java運行的時候,無論某個類實例化多少個對象,他們都會對應一個Class對象,它表示正在運行的程序的類和接口。如何獲取這個類呢?常用的方法有三個:

1 通過Class的靜態方法forName(),直接輸入類的全路徑;
2 通過對象的getClass()方法,獲得所指向的Class對象。
3 通過類名的.class語法,獲得Class對象;

下面通過實例來說明,如何通過以上三個方法獲取Class對象。

<span style="font-size:18px;">package reflect.test;
import java.lang.reflect.Method;

//測試類
class Demo {

}

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {

		// 三種方式獲取操作類的class對象
		Class<?> demo1 = null;
		Class<?> demo2 = null;
		Class<?> demo3 = null;
		// 方法一 通過輸入String類的全路徑
		try {
			demo1 = Class.forName("java.lang.String");
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 方法二 通過實例化後的對象得到Class類
		String strTest = "";
		demo2 = strTest.getClass();
		// 方法三 通過類的class語法,獲取Class類
		demo3 = String.class;
		// 輸出Class類
		System.out.println(demo1.getName());
		System.out.println(demo2.getName());
		System.out.println(demo3.getName());
	}
}</span>

輸出結果:
java.lang.String
java.lang.String

java.lang.String


三 功能詳解
前邊我們說過了反射的五個主要用途,我們已經試驗了第一個用途,通過反射獲得對象所屬的類,下面我們分別來說明其他的幾個用途。
1 通過反射來執行對象的某個方法,代碼如下:
<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Method;

//測試類
class Display {
	// 帶有參數的輸出方法
	public void show(String name, String content) {
		System.out.println(name + content);
	}
}

public class ReflectMethod {
	public static void main(String[] args) {
		// 1 獲取display類的對象
		Display display = new Display();
		Class<?> displayTest = display.getClass();
		// 2 獲取操作方法對象
		Method method = null;
		try {
			method = displayTest.getMethod("show", String.class, String.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//3 執行獲得的方法
		try {
			method.invoke(display, "小蓋", "你好!");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}</span>

輸出結果:
小蓋  你好!

前邊說過,使用反射的第一步就是獲取這個類,對應我們代碼註釋的第一步;第二步是獲取你將要操作的方法對象,對應我們代碼第二步,這個方法包括三個參數,第一個參數是我們要操作的那個方法名,第二個和第三個參數分別是我們要調用的那個方法的參數類型,這個方法的參數個數是根據要調用的方法的參數個數來確定的,所以,參數個數是個不確定的數據。第三步是執行這個方法,即我們的invoke方法,其實調用的就是show方法,這個方法有三個參數,第一個參數是一個對象,使我們要調用的那個類的一個對象,後邊的參數是我們要調用的方法需要的參數,這個參數必須與要調用方法的參數一致。
2 通過反射來給某個類的屬性賦值,代碼如下:

<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Field;
//測試實體類
class Person{
	private String name;
	private Integer age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
}
public class ReflectAttribute {
	
	public static void main(String[] args){
		//實例化一個源對象並且賦值
		Person fromPerson = new Person();
		fromPerson.setName("小蓋");
		fromPerson.setAge(22);
		//實例化一個目標對象
		Person toPerson = new Person();
		try {
			//給目標對象屬性賦值
			replace(fromPerson, toPerson);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		System.out.println(toPerson.getName() +" : "+ toPerson.getAge().toString());
	}
	
	public static void replace (Object fromClass,Object toClass) throws SecurityException, NoSuchFieldException{
		//獲取源對象的類
		Class<?> fromObject = fromClass.getClass();
		//獲取源對象的屬性集
		Field[] fromFields = fromObject.getDeclaredFields();
		//獲取目標對象的類
		Class<?> toObject = toClass.getClass();
		//定義目標對象的屬性集
		Field toField = null;
		//循環源對象屬性集
		for (Field fromField : fromFields) {
			//獲取源對象的屬性名稱和對應目標對象的屬性名稱
			String name = fromField.getName();
			toField = toObject.getDeclaredField(name);
			//設置屬性操作權限
			fromField.setAccessible(true);
			toField.setAccessible(true);
			try {
				//給目標對象屬性賦值
				toField.set(toClass, fromField.get(fromClass));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
}</span>

運行結果:
小蓋 : 22

反射機制中的類有Class對應,方法有Method對應,屬性由Field對應。Field提供了get和set屬性,但是由於屬性時私有類型,所以需要設置訪問權限。本例中是通過循環分別爲每個屬性設置訪問權限,您也可以通過設置屬性集的權限來同意修改訪問權限,在此不再贅述。
3 通過反射在運行時動態創建類的一個對象,代碼如下:
<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Field;

class Student{
	private String stuName;
	private String stuNo;
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	public String getStuNo() {
		return stuNo;
	}
	public void setStuNo(String stuNo) {
		this.stuNo = stuNo;
	}
	
}

public class ReflectClass {
	
	public static void main (String[] args){
		//實例化源對象
		Student fromStudent = new Student();
		fromStudent.setStuName("小蓋");
		fromStudent.setStuNo("11050241003");
		//調用replace方法生成目標對象
		Student toStudent = (Student)replace(fromStudent);
		System.out.println("姓名: " + toStudent.getStuName());
		System.out.println("學號: " + toStudent.getStuNo());
	}
	
	private static Object replace(Object fromClass){
		//1--2 獲得源對象的類和屬性集、定義目標對象
		Class<?> fromObject = fromClass.getClass();
		Field[] fromFields = fromObject.getDeclaredFields();
		Object toObject = null;
		try {
			//3 通過類直接創建目標對象
			toObject = fromObject.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		//給目標對象創建屬性等
		for (Field fromField : fromFields) {
			fromField.setAccessible(true);
			try {
				fromField.set(toObject, fromField.get(fromClass));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return toObject;
	}
}</span>

輸出結果:
姓名: 小蓋
學號: 11050241003

Class的newInstance方法只能創建無參數的構造函數的類,如果構造函數帶有參數,那麼就需要另外一種方式。屬性賦值和上例一樣,這裏不再贅述。

四 注意事項

        在獲取類的方法、屬性和構造函數時,會有getxxx和getDeclatedxxx兩種方法。它們的區別是前者只能返回訪問權限爲public的方法和屬性,包括父類的;後者返回的是所有訪問權限的方法和屬性,不包括父類的。

五 總結

        反射的應用範圍很廣,使用反射可以降低程序之間的耦合性,增加程序的靈活性。

         以後會根據實際應用情況更加深入的研究反射,敬請期待。

發佈了124 篇原創文章 · 獲贊 168 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章