——java中的反射

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------

什麼是反射?

一,反射就是把java類中的各個成員,提取成其對應的java類對象。

例如:一個java類中用一個Class類的對象來表示,一個類中的組成部分都有對應的類:

Field —— 成員變量;

Method —— 成員方法;

Constructor —— 構造函數;

Package ——包

二,一個類中的每個成員可以用相對應的反射API類的一個實例對象來表示,通過調用Class類的方法可以得到這些

實例對象,就可以執行相應的操作了。


反射的基石 -- Class類

1,java中的各個java類屬於同一類事物,描述這類事物的java類名就是Class。

注:對比:衆多人用一個Person類表示,衆多的java類就用Class表示

a,一個人  -->Person

b,java類  -->Class

2,Class類代表java類,它的實例對象分別對應什麼呢?

1,對應各個類在內存中字節碼,例如,String類的字節碼,Person類的字節碼等。。。

2,一個類被類加載器加載到內存中,佔用一片存儲空間,這個空間的內容就是類的字節碼,不同的類的字節碼是不同的,

所以它們在內存中的內容是不同的,這一個個空間分別用一個個的對象來表示,這些對象具有相同的類型即Class類型。

3,如何得到各個字節碼對應的實例對象(Class類型)

有3中方式:

1:類名.class;例如:System.class

2:對象名.getClass();例如:new Date().getClass()

3:Class.forName(類的全名稱(包名+類名)字符串);例如:Class.forName("java.util.Date");

4,九個預定義Class實例對象:

1,boolean.class = Boolean.TYPE,
2,char.class = Character.TYPE, 
3,byte.class = Byte.TYPE, 
4,short.class = Short.TYPE, 
5,int.class = Integer.TYPE, 
6,long.class = Long.TYPE, 
7,float.class = Float.TYPE, 
8,double.class = Double.TYPE,
9,void.class = Void.TYPE

5,數組類型的Class對象實例對象

1,Class.isArray()

2,總之,只要好似在源程序中出現的類型,都有各自的Class實例對象,例如,int[]

構造函數的反射應用

一,Constructor類代表某個類中的一個構造方法

二,得到某個類所有的構造方法:

示例:Constructor[]  constructors = Class.forName("java.lang.String").getConstructors();

三,得到某一個構造方法://獲取時要用到參數類型

示例:Constructor  constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

四,創建示例對象:

示例:

1,普通方式:String str = new String(new StringBuffer("abc"));

2,反射方式:

Constructor  constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

String str = (String)constructor.newInstance(new StringBuffer("abc"));

五,Class.newInstance()方法:

1,示例:String obj = (String)Class.forName("java.lang.String").newInstance();

2,該方法內部先得到默認的構造方法,然後用該構造方法來創建實例對象。

3,該方法內部的具體事項代碼,用到了緩存機制來保存默認構造方法的示例對象。

構造函數反射的示例代碼:

package com.itheima.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 反射操作構造函數
 * @author wuyong
 *
 */
public class ConstrutorDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			Constructor constructor1 = String.class. getConstructor(StringBuffer .class);
			String str2 = (String) constructor1.newInstance (/*"abc"*/new StringBuffer("abc"));
			System.out .println(str2.charAt (2));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}


成員變量的反射

Field類表示某個類中的一個成員變量。

語法:

  類名 對象名 = new 類();

  Field field = 對象名.getClass().getField(屬性名字符串);

  此時field對象並不代表具體的值,只代表該類下的成員變量的一個對象,

  要獲取到指定對象裏的成員變量的值,語法如下:

  field.get(對象名);指明要取的成員變量的值屬於哪個對象裏的。
  
注:一,getField()和getDeclareField()方法的區別:

    getField()方法只能獲取到訪問級別爲可見的屬性,不能獲取到不可見的屬性,若調用該方法

      獲取不可見的屬性,會報出:notFoundFieldException的異常。

    getDeclareField()方法可以獲取該類中聲明的所有屬性,包括private所修飾的。

    二,使用Field 對象下的get(對象名)來獲取該對象下的屬性時,如果屬性的訪問修飾符爲不可見時,

     則會報出IllegaAccessException()異常。若要獲得不可見的屬性的話,可以用以下的方式來強行

     獲得,Field對象名.setAccessible(true);設置訪問級別爲可見。(稱爲暴力反射)

  例子:將一個類中的所有String類型中的"b"全部換成"a";

注:字節碼比較時,要用"=="來比較,因爲是同一份字節碼。

成員變量反射的實例代碼:

package com.itheima.reflect;

import java.lang.reflect.Field;

/**
 * 成員變量的反射
 * @author wuyong
 *
 */
public class FieldDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			ReflectPoint point = new ReflectPoint(3,5 );
			Field fieldY = point .getClass().getField("y" );
			
			//fieldY的值是多少?是5,錯!fieldY不是對象身上的變量,而是類上,要用它去取某個對象上對應的值
			System.out .println("成員變量Y的值:" + fieldY.get (point));
			Field fieldX = point .getClass().getDeclaredField("x" );
			//由於X是不可見的,所以就要調setAccessible,並設爲true的方式。用暴力反射的方式來獲得。
			fieldX.setAccessible (true);
			System.out .println("成員變量X的值:" + fieldX.get (point));     
			
			//2)將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的"b"改成"a"。
			Field name = point.getClass().getField("name");
			System.out.println("替換之前:" + name.get(point));
			changeStringValue(point);
			System.out .println("替換之後:" + name.get(point));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 定義一個將所有字符串的類成員變量中的”a“ 變成 ”b“的方法
	 * @param obj
	 * @throws Exception
	 */
	private static void changeStringValue(Object obj ) {
		    try {
				Field[] fields = obj. getClass().getFields ();
				for(Field field : fields ){
				     //if(field.getType().equals(String.class)){
				     if(field .getType() == String .class){
				        String oldValue = ( String)field .get( obj);
				        String newValue = oldValue.replace("b" , "a" );
				        field .set( obj, newValue );
				     }
				}
			} catch (Exception e) {
				e.printStackTrace();
			}   
		}

}
/**
 * 被反射操作的類
 * @author wuyong
 *
 */
class ReflectPoint {
	private int x;
	public int y;
	public String name = "a1b2c3d4c3b2a1";
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	public static void main(String[] args){
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i]);
		}
	}
}


成員方法的反射
Method代表某一個類中的一個成員方法

  語法:

Class cls = TestUtil1.class;

TestUtil1 tu1 = (TestUtil1)cls.newInstance();
Method method = cls.getDeclaredMethod("show", null);//指定獲取方法名爲“show”,並且沒有參數的方法

method.invoke(tu1, null);//在對象tu1中執行無參的方法

Method method2 = cls.getDeclaredMethod("show2", null);

method2.invoke(null, null);//當對象參數爲null時,表示這是一個靜態方法

調用方法的方式:

  一,使用對象名.方法名。調用;

  二,使用反射來獲得對應的方法對象,來調用invoke(Object obj,Object...args)執行方法;

其中obj是指那一個對象上的方法,args是表示該方法的參數列表


注:專家模式:哪個類擁有該方法需要的私有數據,那這個方法就屬於哪個類。


用反射調用某個類的main方法

  語法:

String className = args[0];//獲取第一個參數,首先要設置RunConfigurations中的Arguments爲要執行的類的完整類名。

Class cls2 = Class.forName(className);//通過活動到的完整類名,獲得該類的字節碼

Method main = cls2.getMethod("main", new String[]{}.getClass());//通過字節碼,獲得該類的main方法

/*

* 因爲爲了兼容JDK1.4以下的版本,在傳入參數的時候,會對參數進行裝包;執行時,會對參數進行拆包,如果

* 單純的傳入一個數組的話,則在拆包的時候將每一個元素都拆成一個參數,此時會報出:IllegalArgumentException異常

* 解決方式有以下2種。

*/

//使用一個Object數組,再把傳入的數組參數當成Object數組的一個元素在傳入。

//main.invoke(null, new Object[]{new String[]{"abc","東方","123"}});

//將傳入的參數強轉成Object對象,

main.invoke(null, (Object)new String[]{"abc","東方","123"});

成員方法反射的示例代碼:

package com.itheima.reflect;

import java.lang.reflect.Method;

/**
 * 類成員方法的反射
 * @author wuyong
 *
 */
public class MethodDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try{
			//執行普通方法
			String str1 = "asdewfs21";
			//str1.charAt(1)
			Method methodCharAt = String .class. getMethod("charAt" , int .class);
			System.out .println("字符串的第二個字符是:" + methodCharAt.invoke (str1, 1 ));
			System.out .println("字符串的第二個字符是:" + methodCharAt.invoke (str1, new Object[]{2 }));
			
			//執行Person的main方法,及靜態方法的演示
//			Person.main(new String[]{"111","222","333cc"});
			//靜態方法由於不用對象就可以調用,所以對象那個參數是null。	
			//用反射的方式調用ReflectPoint的main方法,
			//注:運行前要設置當前主函數的參數步驟:Run Configureations ---> Arguments --->要調用的類的全名稱如:com.itheima.reflect.ReflectPoint
			String startingClassName = args [0];
			Method mainMethod = Class .forName(startingClassName).getMethod ("main", String[].class);
			//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
			mainMethod.invoke (null, (Object )new String []{"111","222" ,"333"}); //數組,jdk會將數組拆成多個參數,從而出現參數不對異常。
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

}
/**
 * 被反射操作的類
 * @author wuyong
 *
 */
class ReflectPoint {
	private int x;
	public int y;
	public String name = "a1b2c3d4c3b2a1";
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	public static void main(String[] args){
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i] + "aa");
		}
	}
}


數組的反射,及數組與Object

數組的反射

  1,具有相同位數(例如:一維對一維)和元素類型的數組屬於同一個類型,即具有相同的Class實例對象

  2,代表數字的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class

  3,基本類型的一維數組可以被當做Object類型使用,不能當作Object[]類型使用;

     非基本類型的一維數組,既可以當作Object類型使用,又可以當作Object[]類型使用。

  4,Arrays.asList()方法處理int[]和String[]的差異

當處理的是一維的基本數據類型的數組時,其保存的是一維數組的完整名稱,即內存中的地址如:[I @a6d7f3。但二維的基本數據類型數組是可以遍歷的。

當處理的是引用類型的數組,則會將每一個元素都保存到集合中。

  5,Array工具類用於完成對數組的反射操作。

  6,通過反射獲取元素的步驟:

1,獲取數組的字節碼:對象.getClass()

2,判斷是否是數組:字節碼對象.isArray()
3,獲取數組的長度:getlength(Object obj)

      4,遍歷數組元素

      5,通過元素.getClass().getName()方法獲取元素對應的類型


數組反射的示例代碼:

package com.itheima.reflect;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * 數組的反射,及數組與Object對象
 * @author wuyong
 *
 */
public class ArrayDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int [] a1 = new int []{1, 2,3 };
		int [] a2 = new int [4];
		int[][] a3 = new int [2][ 3];
		String [] a4 = new String[]{"a" ,"b","c"};
		System.out .println("a1 = a2" + (a1.getClass () == a2.getClass ())); //true , 相同的類型和相同的維數有相同字節碼對象
		//JDK1.5之後,編譯錯誤,因爲出現了泛型是。類型不同,沒有可比性
//		System.out .println(a1.getClass () == a4.getClass ());//false
		//JDK1.5之後,編譯錯誤維數不同,沒有可比性
//		System.out .println(a1.getClass () == a3.getClass ());//false
		System.out .println("a1的名稱" + a1.getClass ().getName());//[I
		System.out .println("a1的父類名稱" + a1.getClass ().getSuperclass().getName());//java.lang.Object
		System.out .println("a4的父類名稱" + a4.getClass ().getSuperclass().getName());//java.lang.Object


		Object aObj1 = a1 ;
		Object aObj2 = a4 ;
		//Object[] aObj3 = a1;//不行,因爲基本數據類型不是Object
		Object[] aObj4 = a3; //二維數組,將一維數組作爲一個對象,
		Object[] aObj5 = a4; //string本身就是一個對象


		System.out .println("a1的toString()形式:" + a1);
		System.out .println("a1的toString()形式:" + a4);
		//基本類型的一維數組傳入asList方法後,會將數組當成一個對象元素存入集合中。
		System.out .println("a1的保存集合後的形式:" + Arrays.asList (a1)); //[I@12344 asList(T...t) 作爲對象傳遞進去了。
		//引用類型就可以遍歷
		System.out .println(Arrays.asList (a4)); //[a,b,c]  asList(Object[] obj) 兼容jdk1.4,作爲數組傳遞進去


//		數組的反射


		//傳遞數組調用
		printObject(a4 );
		
		//傳遞對象調用
		printObject("xyz" );
	}
	/**
	 * 數組的反射,如果是數組則遍歷,如果是當對象就輸出
	 * @param obj
	 */
	private static void printObject(Object obj ) {
        Class clazz = obj. getClass();
         if(clazz .isArray()){ //如果是數組,拆開數組,打印裏面的元素
             int len = Array.getLength (obj);
             for(int i=0;i 


---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------







































 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章