1. 程式人生 > >黑馬程式設計師 反射學習筆記

黑馬程式設計師 反射學習筆記

----------android培訓java培訓、java學習型技術部落格、期待與您交流!----------

反射:

 “反射就是把Java類中的各種成分對映成相應的java類”,而在此之前,首先是必須獲得類的Class物件,再呼叫Class的相關方法,獲取例項類中的各種成分,如構造方法Constructor、域Field及包等資訊。

具體過程反射就是把Java類中的各種成分對映成相應的java類。具體就是先得到一個Field(欄位,通常都是變數)們對映的Class的例項物件--一份位元組碼。然後在通過它們操作具體的類物件。

比如:

 Constructor:得到一個Constructor[] constructors= Class.forName(“java.lang.String”).getConstructors();,通過呼叫它的newInstance得到這個constructors所在的類的新物件。String str =(String)constructor.newInstance(new StringBuffer(“abc”))。

 Field:得到一個類中的Field,再通過Field操作這個類中的Field對應的具體成員變數。如果這個變數是私有的,還可以通過暴力反射對這個變數進行操作。

 Method:得到一個類中的Method()包括main方法,在通過得到的method對這個類對應的一個具體物件進行操作(通過invoke方法)。

那麼,常見的獲取Class例項的方法有哪些呢,通過學習,一般的,有如下三種方法:

(1)對於類,格式為:類名.class,例如,System.class ,Person.class

(2)對於具體的物件,需使用getClass()方法,格式為:物件.getClass(),例如,new Date().getClass(),new Person().getClass()。

(3當然,若給定類名,則可使用Class類的靜態方法forName(),格式為:Class.forName("類名"),例如,Class.forName("java.util.Date");

1.獲取構造方法(Constructor):

通過反射可以獲取到java類中的構造方法,通過構造方法可以在執行時動態的建立java物件,而不用通過new來建立。對於Class類,有四個方法可以獲取構造方法,getConstructors返回的是類中所有public許可權的構造方法陣列,getConstructor根據引數的列表返回打個public許可權的構造方法。此外,getDeclaredConstructors和getDeclaredConstructor與之前的兩個類似,但是,這兩個獲取的是類中真正宣告的除private許可權的構造方法,且忽略從父類中繼承下來的構造方法。構造方法獲取後,便可以呼叫構造方法了。


import java.lang.reflect.Constructor;
public class ReflectGetConst {
	public static void main(String[] args) throws Exception{
		//反射獲取構造方法
		Constructor<MathMethods> constructor = MathMethods.class.getDeclaredConstructor(String.class,String.class);
//利用構造方法建立例項物件
		MathMethods mathMethods = constructor.newInstance("myAdd","add");
		System.out.println("name:"+mathMethods.getName()+"\nforWhat:"+mathMethods.getForWhat());
	}
}

class MathMethods{
	private static String id = "001";//私有靜態域
	public String name = "shinian";
	private String forWhat = "No";//私有域
	MathMethods(){}	
MathMethods(String name,String forWhat){
		this.name = name;
		this.forWhat = forWhat;
	}
	public int add(int a,int b){
		return a+b;
	}
	public double add(double ...b){
		double sum = 0.0;
		for(int i=0;i<b.length;i++){
			sum+=b[i];
		}
		return sum;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getForWhat() {
		return forWhat;
	}
	public void setForWhat(String forWhat) {
		this.forWhat = forWhat;
	}
}
}

2、     獲取域(Field)

反射不僅能夠獲取上述的構造方法,也能獲取類中public修飾的域,若是private,則需通過getDeclaredField(),再經setAccessible(true)之後進行暴力似獲取。而對於getField()方法,暴力獲取是無效的,其只能訪問public修飾的域。請看下錶

private

預設

protected

public

getField()

×

×

×

getField(),再經setAccessible(true)之後

×

×

×

getDeclaredField()

×

getDeclaredField(),再經setAccessible(true)之後

這裡需要留意的是對靜態域的反射操作,靜態域與非靜態域的區別在於靜態域使用時不需要提供具體的例項,涉及到需要例項引數時,使用null即可,
public static void main(String[] args) throws Exception{
		
		Field staticField =MathMethods.class.getDeclaredField("id");
		staticField.setAccessible(true);
		//通過反射改變id域的值
		System.out.println(staticField.get(new MathMethods()));
		
		//通過反射改變id域的值,無需建立具體物件
		staticField.set(null, "007");
		System.out.println(staticField.get(new MathMethods()));
		
		System.out.println("---分割線---");
		
		Field nameField =MathMethods.class.getDeclaredField("name");
		MathMethods mathMethods = new MathMethods();
		
		//通過反射改變id域的值,需要具體的物件
		nameField.set(mathMethods, "張三");
		System.out.println(nameField.get(mathMethods));
		
		
	}
執行結果:
001
007
---分割線---
張三

3、      獲取方法(Method)

         這裡需要注意兩點,其一是,利用getMethod(String name,Class<?>... parameterTypes),獲取某個方法的物件時,對於引數是陣列或者是可變引數的,需要的是同類型的陣列的Class物件。其二是對某個方法物件呼叫

方法Objectinvoke(Object obj,Object... args)時,務必注意該方法的返回值是Object型別,引數也均為Object型別,必要的時候是需要對引數進行型別強制轉換成Object型別的。

/*獲取MathMethods類中的public double add(double ...b){}方法並呼叫*/
public class ReflectGetConst {
	public static void main(String[] args) throws Exception{
		//反射獲取類的成員方法
		Method method = MathMethods.class.getMethod("add", double[].class);
		double result = (Double) method.invoke(new MathMethods("myAdd","add"), (Object)new double[]{1.1,2.1,3.5});//第二個引數強轉成Object類型別,返回值Object型強轉成Double型別
		System.out.println(result);//列印結果:6.7
	}

4、      運算元組

使用反射對陣列進行操作是經過專門的java.lang.reflect.Array這個工具類來實現的,
import java.lang.reflect.Array;
public class ReflectArray {
	public static void main(String[] args) {
		ArrayDemo();
		
	}
	public static void ArrayDemo(){
		//一維陣列
		String[] strArr = (String[])Array.newInstance(String.class, 2);
		Array.set(strArr, 0, "張三");//反射賦值
		strArr[1] = "李四";//普通賦值
		print(strArr);
		print("---1---");
		//二維陣列
		
		int[][] intArr = (int[][])Array.newInstance(int.class, 1,1);
		intArr[0][0] = 1;
		print(intArr[0][0]);
		print("---2---");
		
		//類似int[][][] intArr2 = new int[1][1][1]方式
		int[][][] intArr2 = (int[][][])Array.newInstance(int.class, 2,2,2);
		intArr2[0][0][0] = 11;
		intArr2[0][0][1] = 22;
		print(intArr2[0][0][1]);
		
		print("---3---");
		
		int[] notObject = new int[]{13,23,33}; 
		Object[] objcetes1 = strArr;
		//Object[] objcetes2 = notObject;//該句報錯,原因是int等基本資料型別不是直接繼承自Object
		/*那麼下面這句為什麼又不報錯呢,因為intArr是二維陣列,可以視為二維數組裡面儲存的元素是一維陣列,
		 * 是一個物件,它預設繼承Object*
		 */
		
		Object[] objectes3 = intArr;
		print(objectes3.length);//從列印結果為1也可以看出,intArr裡實際存放的元素是一維陣列
	}
	public static void print(Object[] obj){
		for(int i = 0;i<obj.length;i++){
			System.out.println(obj[i]);
		}
	}
	public static void print(Object obj){
		System.out.println(obj);
	}
}

總結:反射還是比較難的,要好好練習才能好好掌握。

----------android培訓java培訓、java學習型技術部落格、期待與您交流!----------