1. 程式人生 > >黑馬程式設計師——Java高新技術之反射

黑馬程式設計師——Java高新技術之反射

-------android培訓java培訓、期待與您交流! ----------

反射

JAVA反射機制是在執行狀態中,對於任意一個類 (class檔案),都能夠知道這個類的所有屬性和方法;

對於任意一個物件,都能夠呼叫它的任意一個方法和屬性; 這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。 

其實反射就是把Java類中的各種成分對映成相應的java類。

動態獲取類中資訊,就是java反射 。(反射必須有介面)可以理解為對類的解剖。

要想要對位元組碼檔案進行解剖,必須要有位元組碼檔案物件.

位元組碼檔案用Class類來定義 ,這個類提供獲取位元組碼檔案的內容,反射就是依靠這個類來完成的

獲取位元組碼物件的方式:

方式一:

Object類中的getClass()方法的。 要用這種方式,必須要明確具體的類,並建立物件。麻煩 .

方式二:

任何資料型別都具備一個靜態的屬性.class來獲取其對應的Class物件。相對簡單,但是還是要明確用到類中的靜態成員。還是不夠擴充套件。

方式三:

只要通過給定的類的 字串名稱就可以獲取該類,更為擴充套件。可是用Class類中的方法完成。該方法就是forName.這種方式只要有名稱即可,更為方便,擴充套件性更強。 

package reflect.demo;	  
import bean.Person;

public class ReflectDemo {

	public static void main(String[] args) throws ClassNotFoundException {
			
		getClassObject_1();
		getClassObject_2();
		getClassObject_3();
	
	}
	//方式三
	public static void getClassObject_3() throws ClassNotFoundException {
		

		String className="bean.Person";
		Class clazz = Class.forName(className);
		
		System.out.println(clazz);
	}
	//方式二
	public static void getClassObject_2() {
		
		Class clazz = Person.class;
		
		Class clazz1 = Person.class;
		System.out.println(clazz==clazz1);
	}
	//方式一
	public static void getClassObject_1(){
		
		Person p = new Person();
		Class clazz = p.getClass();
		
		Person p1 = new Person();
		Class clazz1 = p1.getClass();
		
		System.out.println(clazz==clazz1);
	}
}	

九個預定義Class例項物件:八個基本資料型別和void

用isPrimitive()方法來判斷是否為基本資料型別位元組碼

陣列型別的Class例項物件:比如:int[].class,用isArray()方法來判斷是否為陣列型別位元組碼

總之,只要是在源程式中出現的型別,都有各自的Class例項物件

反射所有涉及的物件都在Reflect包中,這個包叫反射包,能去解析類中成員的這些物件都是反射中必須涉及的物件
這些物件就這麼幾個,Constructor<>:構造器,有泛型;Method:方法;Field:欄位

用newInstance()方法 建立 Class 物件所表示的類的一個新例項。 

該方法內部先得到預設的構造方法,然後用該構造方法建立例項物件。

package bean;

public class Person {

	private int age;
	private String name;
	
	public Person(String name,int age) {
		super();
		this.age = age;
		this.name = name;
		
		System.out.println("Person param run..."+this.name+":"+this.age);
	
	}
	public Person() {
		super();
		
		System.out.println("person run");
		
		
	}
	
	public void show(){
		System.out.println(name+"...show run..."+age);
	}
	
	private void privateMethod(){
		System.out.println(" method run ");
	}
	
	public void paramMethod(String str,int num){
		System.out.println("paramMethod run....."+str+":"+num);
		
	}
	public static void staticMethod(){
		System.out.println(" static method run......");
	}
}
通過反射建立例項物件
package reflect.demo;

import java.lang.reflect.Constructor;

public class ReflectDemo2 {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, Exception {

		createNewObject_1();
		
	}
	
	public static void createNewObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		
		//根據給的類檔案的名字
		String name = "cn.itcast.bean.Person";
		//找尋該名稱類檔案,並載入進記憶體,併產生Class物件。
		Class clazz = Class.forName(name);
		//如何產生該類的物件呢?通過Class類中的newInstance方法,建立此 Class 物件所表示的類的一個新例項。
		//如同用一個帶有一個空引數列表的 new 表示式例項化該類。如果該類尚未初始化,則初始化之。 
		Object obj  = clazz.newInstance();
		
		
		//早期:new時候,先根據被new的類的名稱找尋該類的位元組碼檔案,並載入進記憶體,
//		並建立該位元組碼檔案物件(也就是Class檔案),並接著建立該位元組碼檔案的對應的Person物件.
//		cn.itcast.bean.Person p = new cn.itcast.bean.Person();
		
		
	}
}
Constructor類代表某個類中的一個構造方法

通過Class類中的

getConstructors() 得到某個類所有的公共構造方法

getConstructor(Class... parameterTypes);得到某一個構造方法

Constructor類中的newInstance(Object... initargs) 使用此 Constructor 物件表示的構造方法來建立該構造方法的宣告類的新例項,並用指定的初始化引數初始化該例項。

用反射通過指定構造方法,建立物件

package reflect.demo;

import java.lang.reflect.Constructor;

public class ReflectDemo2 {	  
	  
	  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, Exception {

		createNewObject_2();
		
	}
	
	public static void createNewObject_2() throws Exception {

		String name = "cn.itcast.bean.Person";
		//找尋該名稱類檔案,並載入進記憶體,併產生Class物件。
		Class clazz = Class.forName(name);
		//獲取到了指定的建構函式對  象。
		Constructor constructor = clazz.getConstructor(String.class,int.class);
		
		//通過該構造器物件的newInstance方法進行物件的初始化。
		Object obj = constructor.newInstance("小明",38);
		
	}
}  
Field類代表某個類中的一個成員變數
通過Class類中的

getField(String name)    返回一個 Field 物件,獲取指定公共成員欄位。

getFields()  返回一個包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位。

getDeclaredField(String name) 返回一個 Field 物件,獲取指定的欄位

Field類中的

get(Object obj) 獲取指定物件上此 Field 表示的欄位的值。

set(Object obj, Object value) 將指定物件變數上此 Field 物件表示的欄位設定為指定的新值。

getType():判斷欄位的型別

若欄位是私有的,不能訪問值,就用暴力訪問

暴力訪問:指對私有欄位的訪問,通過Filed的父類AccessibleObject中的方法setAccessible(boolean flag) 取消許可權檢查

用反射獲取位元組碼檔案中的欄位。

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

import java.lang.reflect.Field;

public class ReflectDemo3 {

	public static void main(String[] args) throws Exception {
		
		getFieldDemo();
		
	}

	/*
	 * 獲取位元組碼檔案中的欄位。
	 */
	public static void getFieldDemo() throws Exception {
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		
		field = clazz.getDeclaredField("age");//只獲取本類,但包含私有。 
		
		//對私有欄位的訪問取消許可權檢查。暴力訪問。
		field.setAccessible(true);
		
		Object obj = clazz.newInstance();
		
		field.set(obj, 89);
		
		Object o = field.get(obj);//  返回指定物件上此 Field 表示的欄位的值。
		
		System.out.println(o);
			
	}
	
}</span>
Method類代表某個類中的一個成員方法

獲取指定Class中的所有公共函式

Method[] getDeclaredMethods():獲取所有的方法

Method[] getMethods():獲取公共方法

Method getDeclaredMethod(String name, Class... parameterTypes) 獲取指定的方法

Method getMethod(String name, Class... parameterTypes) 獲取指定的公共方法

通過Method類中的invoke(Object obj, Object... args) 來對其方法進行呼叫

獲取Method並對齊進行呼叫

package reflect.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo4 {

	public static void main(String[] args) throws Exception {

		getMethodDemo_3();
		
	}
	
	//獲取有引數的方法
	public static void getMethodDemo_3() throws Exception {
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method method = clazz.getMethod("paramMethod", String.class,int.class);
		
		Object obj = clazz.newInstance();
		
		method.invoke(obj, "小強",89);
		
		
	}
	//獲取其中一個無參方法
	public static void getMethodDemo_2() throws Exception {
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method method = clazz.getMethod("show", null);//獲取空引數一般方法。,要先寫上想要獲取的方法名,和引數列表,沒有引數用null
		
		Constructor constructor = clazz.getConstructor(String.class,int.class);
		Object obj = constructor.newInstance("小明",37);
		
		method.invoke(obj, null);//呼叫方法
		
		
		
	}

	/*
	 * 獲取指定Class中的所有公共函式。
	 */
	public static void getMethodDemo() throws Exception {
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method[] methods  = clazz.getMethods();//獲取的都是公有的方法。 (包括其父類)
		methods = clazz.getDeclaredMethods();//只獲取本類中所有方法,包含私有。 
		for(Method method : methods){
			System.out.println(method);
		}
		
	}

}
用反射方式來執行某個類中的main方法

有這三種形式:

mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); 
mainMethod.invoke(null,new Object[1]);用這個的時候,不能遍歷main中的args

package reflect.demo;

public class MainTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(String arg:args){
			System.out.println("----"+arg+"---");
		}
	}

}


package reflect.demo;

import java.lang.reflect.Method;

public class ReflectMain {

	/**
	 * @param args
	 * @throws Exception 
	 * @throws SecurityException 
	 */
	public static void main(String[] args) throws  Exception {
		// TODO Auto-generated method stub
		Class clazz = MainTest.class;
		
		Method method = clazz.getMethod("main",String[].class);
		
//		method.invoke(null, new Object[1]);這時會報錯,因為args沒引數
		
//		method.invoke(null, new Object[]{new String[]{"aaa","bbb"}});
		
		method.invoke(null,(Object)new String[]{"aaa","bbb"});

	}

}
陣列的反射

具有相同維數和元素型別的陣列屬於同一個型別,即具有相同的Class例項物件。

代表陣列的Class例項物件的getSuperClass()方法返回的父類為Object類對應的Class。

基本型別的一維陣列可以被當作Object型別使用,不能當作Object[]型別使用;
非基本型別的一維陣列,既可以當做Object型別使用,又可以當做Object[]型別使用。

二維陣列以上,則既可以當做Object型別使用,又可以當做Object[]型別使用。

package reflect.demo;

public class ReflectArray {

	public static void main(String[] args) {
		//基本型別一維陣列
		int[]arr1 = new int[6];
		int[]arr2 = new int[7];
		System.out.println(arr1.getClass()==arr2.getClass());//true
		//基本型別二維陣列
		int[][]arr3 = new int[5][3];
		int[][]arr4 = new int[5][3];
		System.out.println(arr3.getClass()==arr4.getClass());//true
	
		//基本型別的一維陣列可以被當作Object型別使用,不能當作Object[]型別使用;
//		Object []o = arr1;//編譯錯誤
		Object o1 = arr1;
		//非基本型別以為陣列	
		String[] s1 = new String[6];
		String[] s2 = new String[6];
		System.out.println(s1.getClass()==s2.getClass());//true
		
		Object o2 = arr3;
		//非基本資料型別既可以當做Object型別使用,又可以當做Object[]型別使用
		Object o3 = s1;
		Object[] o4 = s1;
		System.out.println(o3==o4);//true
	}
}
將任意一個物件中的所有String型別的成員變數所對應的字串內容中的"a"改成"b"
package reflect.text;

import java.lang.reflect.Field;


public class ReflectText {
	public static void main(String[]args) throws Exception{
		//獲取指定類檔案中所有的欄位,然後取值,改值
		
		//獲取指定類檔名字
		String name = "reflect.text.person";
		
		//獲取指定類檔案
		Class clazz = Class.forName(name);

		//獲取所有欄位
		Field[] field = clazz.getDeclaredFields();
		
		//建立物件
		person p = (person) clazz.newInstance();
		
		//遍歷欄位,並改值
		for(Field f: field){
			//如果欄位是String型別的,該值,位元組碼比較最好用等號比較
			if(f.getType()==String.class){
				//取消許可權檢查。暴力訪問
				f.setAccessible(true);
				//獲取原來的字串
				String oldVlaue = (String) f.get(p);
				//將字串中的a改為b
				String newVlaue = oldVlaue.replace("a", "b");
				//將修改後的欄位的值,給p物件
				f.set(p, newVlaue);
			}
		}
		System.out.println(p);
	}
}
public class person {
	private String s1 = "ahsa";
	private String s2 = "amana";
	private String s3 = "aapqoqa";
	

	public String toString() {
		return "person [s1=" +s1 + ", s2=" + s2 + ", s3=" + s3 + "]";
	}
}