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

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

反射技術:

其實就是動態載入一個指定的類,並獲取該類中的所有的內容。而且將位元組碼檔案封裝成物件,並將位元組碼檔案中的內容都封裝成物件,這樣便於操作這些成員。簡單說:反射技術可以對一個類進行解剖。

反射的好處:大大的增強了程式的擴充套件性。

反射的基本步驟:

1、獲得Class物件,就是獲取到指定的名稱的位元組碼檔案物件。

2、例項化物件,獲得類的屬性、方法或建構函式。

3、訪問屬性、呼叫方法、呼叫建構函式建立物件。

要想獲取位元組碼檔案中的成員,必須要先獲取位元組碼檔案物件

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

1、通過Object類中的getClass方法

雖然通過,但是前提必須有指定類,並對該類進行物件的建立,才可以通過呼叫getClass

方法

Class clazz=new Person().getClass();//Person是一個類名

2、使用的任意資料類的一個靜態成員class,所有的資料型別都具備的一個屬性

好處:不用new物件,但是還需要使用具體的類

Class clazz=Person.class;//Person是一個類名

3、使用Class類中的forName方法,通過給定類名來獲取對應的位元組碼檔案物件

指定什麼類名,就獲取什麼類位元組碼檔案物件,這種方式的擴充套件性最強,只要將類名的字串傳入即可,獲取對應的位元組碼檔案直接由forName方法自動完成

Class clazz=Class.forName("包名.Person");//Person是一個類名

Class類中的方法

        static Class forName(String className)

        返回與給定字串名的類或介面的相關聯的Class物件。

        Class getClass()

        返回的是Object執行時的類,即返回Class物件即位元組碼物件

        Constructor getConstructor()

        返回Constructor物件,它反映此Class物件所表示的類的指定公共構造方法。

        Field getField(String name)

        返回一個Field物件,它表示此Class物件所代表的類或介面的指定公共成員欄位。

        Field[] getFields()

        返回包含某些Field物件的陣列,表示所代表類中的成員欄位。

        Method getMethod(String name,Class… parameterTypes)

        返回一個Method物件,它表示的是此Class物件所代表的類的指定公共成員方法。

        Method[] getMehtods()

        返回一個包含某些Method物件的陣列,是所代表的的類中的公共成員方法。

        String getName()

        String形式返回此Class物件所表示的實體名稱。

        String getSuperclass()

        返回此Class所表示的類的超類的名稱

        boolean isArray()

        判定此Class物件是否表示一個數組

        boolean isPrimitive()

        判斷指定的Class物件是否是一個基本型別。

        T newInstance()

        建立此Class物件所表示的類的一個新例項。

通過Class物件獲取類例項

        通過檢視API我們知道,Class類是沒有構造方法的, 因此只能通過方法獲取類例項物件。之前我們用的已知類,建立物件的做法:

        1)查詢並載入XX.class檔案進記憶體,並將該檔案封裝成Class物件。

        2)再依據Class物件建立該類具體的例項。

        3)呼叫建構函式對物件進行初始化。

             如:Person p=new Person();

 現在用Class物件來獲取類例項物件的做法:

        1)查詢並載入指定名字的位元組碼檔案進記憶體,並被封裝成Class物件。

        2)通過Class物件的newInstance方法建立該Class對應的類例項。

        3)呼叫newInstance()方法會去使用該類的空引數建構函式進行初始化。

             如:

                     String className="包名.Person";

                     Class clazz=Class.forName(className);

                     Object obj=clazz.newInstance();

反射的用法

1)、需要獲得java類的各個組成部分,首先需要獲得類的Class物件,獲得Class物件的三種方式:

Class.forName(classname)用於做類載入

obj.getClass() 用於獲得物件的型別

類名.class     用於獲得指定的型別,傳參用

  // 1. 根據給定的類名來獲得  用於類載入

  String classname = "cn.itcast.reflect.Person";// 來自配置檔案

  Class clazz = Class.forName(classname);// 此物件代表Person.class

  // 2. 如果拿到了物件,不知道是什麼型別   用於獲得物件的型別

  Object obj = new Person();

  Class clazz1 = obj.getClass();// 獲得物件具體的型別

  // 3. 如果是明確地獲得某個類的Class物件  主要用於傳參

Class clazz2 = Person.class;

2)、反射類的成員方法:

Class clazz = Person.class;

Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});

method.invoke();

3)、反射類的建構函式:

Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})

con.newInstance(params...)

4)、反射類的屬性:

Field field = clazz.getField(fieldName);

field.setAccessible(true);

field.setObject(value);

獲取了位元組碼檔案物件後,最終都需要建立指定類的物件:

建立物件的兩種方式(其實就是物件在進行例項化時的初始化方式)

1,呼叫空引數的建構函式:使用了Class類中的newInstance()方法。

Object obj=clazz.newInstance();

2,呼叫帶引數的建構函式:先要獲取指定引數列表的建構函式物件,然後通過該建構函式的物件的newInstance(實際引數進行物件的初始化。

綜上所述,第二種方式,必須要先明確具體的建構函式的引數型別,不便於擴充套件。所以一般情況下,被反射的類,內部通常都會提供一個公有的空引數的建構函式。

// 如何生成獲取到位元組碼檔案物件的例項物件。
		Class clazz = Class.forName("cn.itcast.bean.Person");//類載入
    // 直接獲得指定的型別
		clazz = Person.class;
		// 根據物件獲得型別
		Object obj = new Person("zhangsan", 19);
		clazz = obj.getClass();

Object obj = clazz.newInstance();//該例項化物件的方法呼叫就是指定類中的空引數建構函式,給建立物件進行初始化。當指定類中沒有空引數建構函式時,該如何建立該類物件呢?請看method_2();

public static void method_2() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		//既然類中沒有空引數的建構函式,那麼只有獲取指定引數的建構函式,用該函式來進行例項化。
		//獲取一個帶引數的構造器。
		Constructor constructor = clazz.getConstructor(String.class,int.class);
		//想要對物件進行初始化,使用構造器的方法newInstance();
		Object obj = constructor.newInstance("zhagnsan",30);
		//獲取所有構造器。
		Constructor[] constructors = clazz.getConstructors();//只包含公共的
		constructors = clazz.getDeclaredConstructors();//包含私有的
		for(Constructor con : constructors) {
			System.out.println(con);
		}
	}

反射指定類中的方法:

//獲取類中所有的方法。
	public static void method_1() 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);
		}
	}
	//獲取指定方法;
	public static void method_2() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		//獲取指定名稱的方法。
		Method method = clazz.getMethod("show", int.class,String.class);
		//想要執行指定方法,當然是方法物件最清楚,為了讓方法執行,呼叫方法物件的invoke方法即可,但是方法執行必須要明確所屬的物件和具體的實際引數。
		Object obj = clazz.newInstance();
		method.invoke(obj, 39,"hehehe");//執行一個方法
	}
	//想要執行私有方法。
	public static void method_3() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		//想要獲取私有方法。必須用getDeclearMethod();
		Method method = clazz.getDeclaredMethod("method", null);
		// 私有方法不能直接訪問,因為許可權不夠。非要訪問,可以通過暴力的方式。
		method.setAccessible(true);//一般很少用,因為私有就是隱藏起來,所以儘量不要訪問。
	}
	//反射靜態方法。
	public static void method_4() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		Method method = clazz.getMethod("function",null);
		method.invoke(null,null);
	}


反射中常見的異常

ClassNotFoundException

InstantiationException//例項初始化異常,通常被反射的類都會有提供空引數的建構函式,沒有對應的建構函式就會出現此類

IllegalAccessException//如果有提供,但是沒有訪問許可權的話就會出現此異常

如何通過給定的建構函式例項化

如果要通過指定的建構函式初始化物件,步驟如下:

1,獲取位元組碼檔案物件

2,再獲取給定的建構函式

3,通過建構函式初始化物件

String className = “com.itheima.Person”;

Class clazz = Class.forName(className);

//獲取指定的構造器,獲取(指定類)Person中兩個引數Stringint的建構函式

Constructor cons = clazz.getConstructor (String.class,int.class)

//有了構造器物件後,同構構造器物件來初始化該類物件

Object obj = cons.newInstance(“wangwu”,23);

//獲取欄位

Field field = clazz.getField(fieldName);//獲取收到是公共的欄位

Field field = clazz.getDeclaredField(fieldName);

//getXXX:獲取都是類中公共的成員

//getDeclaredXXX:獲取本類中已有的成員

//對其進行值的設定,必須先有物件

Object obj = clazz.newInstance();

//通過查詢父類AccessiableObject的方法,setAccessiabletrue

field.setAccessiable(true);//取消許可權檢查,暴力訪問。一般不訪問私有

field.set(obj,30)//若不在上面加暴力訪問將出現IllegalAccessException:age欄位是私有的