天天看點

黑馬程式員-反射(高新技術)

---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------

1:什麼是反射:反射的概念主要指類可以通路、檢測和修改它本身狀态或行為的一種能力,它能讓java類自身進行檢查,并能直接操作類的内部屬性。

反射總結:就是把Java類中的所有成分反射成不同的java類

2:什麼是反射機制:Java反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類中的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為java語言的反射機制

反射的基石àClass類

1.所有的類檔案都有共同屬性,是以可以向上抽取,把這些共性内容封裝成一個類,這個類就叫Class(描述位元組碼檔案的對象)。

        Class類中就包含屬性有field(字段)、method(方法)、construction(構造函數)。

       而field中有修飾符、類型、變量名等複雜的描述内容,是以也可以将字段封裝稱為一個對象。用來擷取類中field的内容,這個對象的描述叫Field。同理方法和構造函數也被封裝成對象Method、Constructor。要想對一個類進行内容的擷取,必須要先擷取該位元組碼檔案的對象。該對象是Class類型。

       Class類描述的資訊:類的名字,類的通路屬性,類所屬于的包名,字段名稱的清單,方法名稱的清單等。每一個位元組碼就是class的執行個體對象。如:classcls=Data.class;

什麼是位元組碼:

       當源程式中用到類時,首先要從硬碟把這個類的那些二進制代碼,一個類編譯成class放在硬碟上以後,就是一些二進制代碼,要把這些二進制代碼加載到記憶體中裡面來,再用這些位元組碼去複制出一個一個對象來。

2.Class和class的差別

       1)class:Java中的類用于描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至于這個屬性的值是什麼,則由此類的執行個體對象确定,不同的執行個體對象有不同的屬性值。

       2)Class:指的是Java程式中的各個Java類是屬于同一類事物,都是Java程式的類,這些類稱為Class。例如人對應的是Person類,Java類對應的就是Class。Class是Java程式中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。

反射的作用(重點):

可以通過反射機制取得對象的類型

取得對象的方法、屬性、構造器

可以建立對象并通路任意對象方法和屬性

反射兩個缺點:

性能問題。用于字段和方法介入時反射要遠慢與直接的代碼

使用反射會模糊程式内部實際要發生的事情,使程式對程式邏輯的可讀性變差

得到位元組碼對象的方式有三種

(1)在源程式裡直接寫上類的名字.class         例:Person.class

(2)在通過對象調用getClass(),傳回位元組碼             例:per.getClass()

(3)用Class類的靜态方法去查詢或加載位元組碼             

例:Class.forName(“類名”) 

PS:一般開發都用第三種,傳入字元串即可得到類的位元組碼

問:以下得到的三個位元組碼是不是同一份?

String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1 == cls2);//輸出true
		System.out.println(cls1 == cls3); //輸出true
           

面試提問:Class.forName(“類名”)的作用是什麼?

回答:

有兩個作用

1:如果該類的位元組碼曾經被加載過,那麼直接傳回該類的位元組碼:

2:如果該類還沒有被加載進記憶體,那麼此時就進行加載該類的位元組碼,緩存在記憶體中,并傳回位元組碼對象

回答:得到的都是同一份位元組碼

isprimitive():判斷一個類是不是一個基本類型的方法

isArray():判斷一個類是不是數組類型的方法

代碼:

String str1 = "abc";
		Class cls1 = str1.getClass();
		System.out.println(cls1.isPrimitive());//結果為false, 說明String不是基本類型
		System.out.println(int.class.isPrimitive());//結果為true
		System.out.println(int.class == Integer.class); //結果為true
		System.out.println(int.class == Integer.TYPE); //結果為true
		System.out.println(int[].class.isPrimitive());//結果為false
		System.out.println(int[].class.isArray());	  //結果為true
                System.out.println(int.Class.isprimitive());//結果為true
           

總之,在源程式中出現的資料類型,都有各自對應的Class執行個體對象

九個預定義Class執行個體對象,即八個基本資料類型(boolean、byte、char、short、int、long、float和 double)和一個void類型的位元組碼對象。也就是說他們都有自己的Class對象,void的是void.class。

反射就是把Java類中的各個成分映射成相應的java類。而Class類提供了一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等資訊。這些資訊就是用相應的類的執行個體對象來表示的,他們是Field(變量或字段),Method(方法),Contructor(構造器),Package(包)等等

擷取類中的方法

getDeclaredMethods():取得類中定義了的所有的方法清單

(包括private,public,protected、packed),但是不包括從父類中繼承來的方法

使用:位元組碼對象.getDeclaredMethods(),傳回值類型是Methods類型

getMethod(name,參數位元組碼):擷取類的所有公有方法,包括從父類中繼承來的方法,傳回Method類型(這點是與getDeclaredMethods()的差別)

name為想取得的方法的名字,後面的參數的自己碼為方法可能有多個重載形式,這裡通過參數的位元組碼來識别

例:調用String類型的charAt()

String str = “abc”;

Method methodCharAt = str.getClass.getMethod(“charAt”,int.class);

System.out.println(methodCharAt.invoke(str,1));//傳入要截取的字元串對象和要操作的下标

//輸出是“b”       ps: invoke()表示調用

如果invoke()中傳入的對象是null,說明該方法對應的方法是靜态方法,靜态方法調用不需要對象

代碼示例:

Method methodCharAt = String.class.getMethod("charAt", int.class);
		System.out.println(methodCharAt.invoke(str1, 1));
		System.out.println(methodCharAt.invoke(str1, new Object[]{2}));
		
		//TestArguments.main(new String[]{"111","222","333"});
		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"});
           

擷取類中的構造方法

getConstructor(A.class):取得類的構造方法(不能擷取私有構造方法)

用法(位元組碼.getConstructor(A.class)),如果有多個構造方法,傳入構造方法的所接收的類位元組碼,傳回值類型是Constructor類型

getDeclaredConstructors():取得類中的所有構造方法(無論是私有還是公有)

用法(位元組碼.getDeclaredConstructors()),傳回值類型是Constructor數組

PS:通過getDeclaredConstructors()擷取私有的構造方法,可以突破單例模式不能建立對象的限制,由外部建立對象

代碼示例:

Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
		String str2 = (String)constructor1.newInstance(/*"abc"*/new StringBuffer("abc"));
		System.out.println(str2.charAt(2));
           

例:得到String的構造方法    :String.class.getConstructor()

String有不止一個構造方法,要得到哪一個構造方法,如何指定?

在getConstructor()中傳入參數,傳入的參數同樣為位元組碼,想得到的構造方法有幾個對象就傳幾個位元組碼檔案

如:String.class.getConstructor(StringBuffer.class)//這個就是得到參數為類型為StringBuffer.的構造方法

擷取方法的參數類型

Type[] getGenericParameterTypes():按照聲明順序傳回Type對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型的。如果底層方法不帶參數,則傳回長度為 0的數組。

用法:Type[] types = method. getGenericParameterTypes();

Field:類中的成員變量

取得某個類的某個成員變量:getFields(“name”),傳入的name是成員的名字,但這種隻能得到公有的成員,私有的不行(得到的隻是變量,但是并不是得到變量的值,要得到變量的值要得到該變量後,再通過get(指定對象)包括父類中的定義的成員

要修改值用set(指定對象)

擷取類中的成員變量

per.class.getFields(“age”),這樣就得到了Person這個類的成員變量age,并不是得到age的值

getDeclaredField():也是得到成員變量,與getFields(“name”)的差別在于:無論公有私有都可以得到,不受權限的控制

代碼:

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectTest {

	public static void main(String[] args) throws Exception {
		ReflectPoint pt1 = new ReflectPoint(3,5);
		Field fieldY = pt1.getClass().getField("y");
		//fieldY的值是多少?是5,錯!fieldY不是對象身上的變量,而是類上,要用它去取某個對象上對應的值
		System.out.println(fieldY.get(pt1));
		Field fieldX = pt1.getClass().getDeclaredField("x");
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(pt1));	
		
		changeStringValue(pt1);
		System.out.println(pt1);
}
public class ReflectPoint {
	private Date birthday = new Date();
	
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "basketball";
	public String str3 = "itcast";
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	@Override
	public String toString(){
		return str1 + ":" + str2 + ":" + str3;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
}
           

練習(重點):得到一個類中的String類型的成員變量,并把成員變量的值中的a用e來替換

private static void changeValue(Object obj)
{//注釋為思路與步驟
	Field[] field = objgetClass.getFields();//得到所有的成員變量的數組
	For(Field field: Field)//周遊該數組,取出成員變量
        {
	        if(field.getType()==String.class)//判斷取出的成員變量是否是String類型的
                {
	                String oldValue = field.get(obj);//取出obj該對象的String成員變量的值
	                String newValue = oldValue.replace(“a”,”e”);//替換字母後傳回一個新的字元串
	                field.set(obj,newValue);//設定obj對象的String成員變量的新值
                }
        }
}
           

---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------

詳細請檢視:http://edu.csdn.net

繼續閱讀