黑馬程序員--反射

--------------android培訓java培訓、學習型技術博客、期待與您交流! --------------

反射概述    

通過Java的反射機制,在運行狀態中,對於任意一個類(.class文件),都能夠知道這個類中的所有屬性和方法;對於任意一

個對象,都能夠調用它的任意一個方法和屬性。這種動態獲取類的信息及動態調用對象的方法功能稱爲Java語言的反射機制。

               結論:反射技術可以對類進行解剖。

 

反射原理:

            平常的java類是通過一些固定的支架(方法,成員變量等)搭建來的,搭建完成後它就具備了描述一類事物的特性。而這些支架也屬於事物,它也能夠被類描述。所以java中就提供了Class類進行描述。Class類所描述的事物是:java類在編譯完成後,會生成class文件存在硬盤上。也就是字節碼。把字節碼(保存了該類的基本信息)加載到內存中來。返回的類型是Class,當然Class就具備了描述該類固定支架(方法,成員變量等)的特性。

 

得到字節碼的方法3種:

          類名.class  例如,System.class

          對象.getClass() 例如,new Date().getClass();

          Class.forName(),例如,Class.forName("java.util.Date");

         提示:如果字節碼已經在內存中,直接拿過來。

                     如果字節碼不在內存中,需要從硬盤上把字節碼加載到內存中去。

 

分析Class類

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {

		String abc = "afd";
		Class cls1 = abc.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		// 虛擬機上做出該類一份字節碼,其他要用就直接拿了。所以打印結果都爲true
		System.out.println(cls1 == cls2);
		System.out.println(cls1 == cls3);

		// 判斷是否是基本數據類型的字節碼isPrimitive()。false
		System.out.println(cls1.isPrimitive());
		// 基本類型的字節碼和對應的封裝類的字節碼是不相同的。false、
		System.out.println(int.class == Integer.class);
		// TYPE常量代表包裝類型所包裝的基本類型的字節碼。true
		System.out.println(int.class == Integer.TYPE);
		// 數組的字節碼類型,不是原始類型false。
		System.out.println(int[].class.isPrimitive());
		// 數組的字節碼類型,判斷一個Class類型是否爲數組用isArray()。true
		System.out.println(int[].class.isArray());

		
		// Class類型,判斷基本數據類型:isPrimitive();判斷數組類型:isArray();
		// 總之:只要是在源程序中出的類型,都有各自的Class
	}

}

九個預定義的Class

                1)包括八種基本類型(byteshortintlongfloatdoublecharboolean)的字節碼對象和一種返回值爲void類型的void.class

                2Integer.TYPEInteger類的一個常量,它代表此包裝類型包裝的基本類型的字節碼,所以和int.class是相等的。基本數據類型的字節碼都可以用與之對應的包裝類中的TYPE常量表示

               只要是在源程序中出現的類型都有各自的Class實例對象,如int[].class。數組類型的Class實例對象,可以用Class.isArray()方法判斷是否爲數組類型的。

 

反射的概念

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

     例如,一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示。就像汽車是一個類,汽車中的發動機,變速箱等等也是一個個的類。表示java類的Class類顯然要提供一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是FieldMethodContructorPackage等等

 Constructor類

1、概述

        如果指定的類中沒有空參數的構造函數,或者要創建的類對象需要通過指定的構造函數進行初始化。這時怎麼辦呢?這時就不能使用Class類中的newInstance方法了。既然要通過指定的構造函數進行對象的初始化。就必須先獲取這個構造函數——ConstructorConstructor對象代表某個類的構造方法。

2、獲取構造方法:

        1)得到這個類的所有構造方法:如得到上面示例中Person類的所有構造方法

              Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();

        2)獲取某一個構造方法:

              Constructor con=Person.class.getConstructor(String.class,int.class);

3、創建實例對象:

        1)通常方式:Person p = new Person(“lisi”,30);

         2)反射方式:Person p= (Person)con.newInstance(“lisi”,30);

注:

        1、創建實例時newInstance方法中的參數列表必須與獲取Constructor的方法getConstructor方法中的參數列表一致。

         2newInstance():構造出一個實例對象,每調用一次就構造一個對象。

         3、利用Constructor類來創建類實例的好處是可以指定構造函數,而Class類只能利用無參構造函數創建類實例對象。

//Constructor類代表某個類中的一個構造方法。
//得到構造方法:     通過構造函數參數列表的類型,來確定獲取的具體構造類。
		Constructor<String> strCon = String.class.getConstructor(StringBuffer.class);
//		new String(new StringBuffer("abc"));
//創建實例對象:
//		1,在對strCon進行實例化對象的時候,需要指定參數對象。
		String str2 = strCon.newInstance(new StringBuffer("abc"));
		System.out.println(str2.charAt(2));
//		2,Class類提供了一個便利,有newInstance方法。直接可以創建對象。省去了獲取Constructor再去創建對象。
//		反射的缺點:反射比較消耗性能佔用時間,進行反射以後把得出的結果存在緩存起來。爲後面儘可能的減少反射的使用量。
		String s = String.class.newInstance();

Field類

1Field類代表某個類中一個成員變量

2、方法

       Field getField(String s);//只能獲取公有和父類中公有

        Field getDeclaredField(String s);//獲取該類中任意成員變量,包括私有

        setAccessible(ture);

        //如果是私有字段,要先將該私有字段進行取消權限檢查的能力。也稱暴力訪問。

        set(Object obj, Object value);//將指定對象變量上此Field對象表示的字段設置爲指定的新值。

        Object get(Object obj);//返回指定對象上Field表示的字段的值。

import java.lang.reflect.Field;

public class FieldDemo {
	public static void main(String[] args) throws SecurityException, NoSuchFieldException, InstantiationException, IllegalAccessException {
		ReflectPoint p = new ReflectPoint(3,5);
//		字段通過字段名字區分。它不代表某個特定對象上面的值。而是告訴程序員,這個叫y的變量已經給你取到了。封裝成了一個類。
//		類上的變量,不是對象上的變量。不包括私有的。
		Field fieldY = p.getClass().getField("y");
//		從特定對象上取值。這個異常說明了,成員是私有的不讓訪問。
//		Exception in thread "main" java.lang.NoSuchFieldException: y
//		at java.lang.Class.getField(Class.java:1520)
//		at reflect.FieldDemo.main(FieldDemo.java:10)

		System.out.println(fieldY.get(p));
//		獲取所有申明的變量。包括private類型。
		Field fieldX = p.getClass().getDeclaredField("x");
//	 java.lang.IllegalAccessException: Class reflect.FieldDemo can not access a member of class reflect.ReflectPoint with modifiers "private"
//		上面異常的出現說明了,沒有獲取私有變量值的權限。
//		如果想要獲取的話,需要進行暴力反射。
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(p));
//		Class c = String.class;
//		String s = (String) c.newInstance();
//		
//		String s1 = String.class.newInstance();
	}
}
將一個對象上所有的String類型的值中含有a的,進行替換。
	private static void changeValue(ReflectPoint p) throws IllegalArgumentException, IllegalAccessException {
//	獲取ReflectPoint中所有申明的字段。
		Field[] fs = p.getClass().getDeclaredFields();
//		遍歷字段。
		for(Field f:fs){
//			getType獲取Field的類型,用==好,因爲字節碼只有一份。如果是String類型的話,就進行操作。
			if(f.getType()==String.class){
				String oldValue = (String)f.get(p);
				String newValue = oldValue.replace("b", "a");
				f.set(p,newValue);
				System.out.println(newValue);
			}
		}
	}
}


反射的作用:你給我一個對象我能把他裏面的值都改掉。例如Spring框架中用的就是這個技術。你給我一個類,我就能用你給的資源來做些事情。

Method類

1、概述:Method類代表某個類中的一個成員方法。調用某個對象身上的方法,要先得到方法,再針對某個對象調用。

2、專家模式:誰調用這個數據,就是誰在調用它的專家。

如人關門:

        調用者:是門調用關的動作,對象是門,因爲門知道如何執行關的動作,通過門軸之類的細節實現。

        指揮者:是人在指揮門做關的動作,只是給門發出了關的信號,讓門執行。

        總結:變量使用方法,是方法本身知道如何實現執行的過程,也就是“方法對象”調用方法,才執行了方法的每個細節的。

3、方法

        Method[] getMethods();//只獲取公共和父類中的方法。

        Method[] getDeclaredMethods();//獲取本類中包含私有。

        Method   getMethod("方法名",參數.class(如果是空參可以寫null);

        Object invoke(Object obj ,參數);//調用方法

        如果方法是靜態,invoke方法中的對象參數可以爲null

如:

獲取某個類中的某個方法:(如String str =abc”)

        1)通常方式:str.charAt(1)

        2)反射方式:

                                  Method charAtMethod =Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

                                  charAtMethod.invoke(str,1);

   說明:如果傳遞給Method對象的invoke()方法的第一個參數爲null,說明Method對象對應的是一個靜態方法

//	通過方法名字,和參數列表來確定方法。導包的時候看到com...這種包是內部用的(不要導),看到java...這就是外部用的。
		String str = "abc";
		Method methodCharAt = String.class.getMethod("charAt", int.class);
		
//		已知Method,調用誰的Method並且還要給它傳參數。
		Character ch = (Character) methodCharAt.invoke(str, 2);
		System.out.println(ch);
//		靜態方法的調用,因爲靜態方法不需要對象。第一個參數應該是null。所以:
//		Character ch2 = (Character) methodCharAt.invoke(null, 2);
		
		String className= args[0];
		invokeMain(className);

 

調用其他類的main方法

      爲什麼要用反射的方式調用main ?

      main的參數是String數組,數組裏面可以裝類名字符串。

           獲取這這些類的名字。通過反射的原理可以獲得各個類中的main方法。

 分別執行這幾個類

private static void invokeMain(String className) throws Exception {
		Method main = Class.forName(className).getMethod("main", String[].class);
//		main是靜態,所以第一個參數是空的。
//	java.lang.IllegalArgumentException: wrong number of arguments jdk1.4特點,給進去一個數組會將數組拆掉,拆掉後的值作爲參數造成的。導致參數變多了。
		main.invoke(null, new Object[]{new String[]{"1","2","3"}});
//		main.invoke(null, (Object)new String[]{"1","2","3"});
	}


數組的反射

 

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

public class ArrayReflectDemo {
	public static void main(String[] args) {
		int[] a = new int[3];
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[3];
		// 數組class是否相同:1.數據類型2數組的維數
		System.out.println(a.getClass() == a2.getClass());
		// 數組的父類是Object
		System.out.println(a4.getClass().getSuperclass().getName());
		//	
		Object aObj1 = a;
		Object aObj2 = a2;
		// 因爲a是一維數組,裏面裝的是int型變量,不是Object子類。不行。
		// Object[] obj1=a;
		// 二維數組,數組裏面套數組。內層數組(一維數組)相當於是Object。可以
		Object[] aobj3 = a3;
		// String數組裏面裝的是String類型的變量。可以
		Object[] aobj4 = a4;
		System.out.println(Arrays.asList(a));
		System.out.println(Arrays.asList(a4));
//		數組的反射。
		
		printObjcet(a4);
	}

	private static void printObjcet(Object obj) {
		Class clazz = obj.getClass();
		if(clazz.isArray()){
//			反射
			int len = Array.getLength(obj);
			for(int i=0;i<len;i++){
				System.out.println(Array.get(obj, i));
			}
		}
	}
}


 

 

 

發佈了23 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章