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

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

反射的基石:Class類
1.Class是Java程序中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。
2.Class和class的區別
  (1)class:Java中的類用於描述一類事物的共性,該類事物有什麼屬性,至於這個屬性的值是什麼,則由此類的實例對象確定,不同的實例對象有不同的屬性值。
  (2)Class:指的是Java程序中的各個Java類是屬於同一類事物,都是Java程序的類,這些類稱爲Class。例如人對應的是Person類,Java類對應的就是Class。
3.對象的創建
  (1)類名.class:如System.class,String.class等等
  (2)對象.class:如new Date().getClass()或者d.getClass()。(Date d = new Date())
  (3)Class.forName(“類名”):如Class.forName(”java.lang.String”)
4.九個預定義的Class
  (1)包括八種基本類型(byte、short、int、long、float、double、char、boolean)的字節碼對象和一種返回值爲void類型的void.class
  (2)Integer.TYPE是Integer類的一個常量,它代表此包裝類型包裝的基本類型的字節碼,所以和int.class是相等的。
  (3)基本數據類型的字節碼都可以用與之對應的包裝類中的TYPE常量表示.
  (4)數組類型的Class實例對象,可以用Class.isArray()方法判斷是否爲數組類型的。
5.成員方法
  (1)static Class forName(String className)
返回與給定字符串名的類或接口的相關聯的Class對象。
  (2)Class getClass()
返回的是Object運行時的類,即返回Class對象即字節碼對象
  (3)Constructor getConstructor()
返回Constructor對象,它反映此Class對象所表示的類的指定公共構造方法。
  (4)Field getField(String name)
返回一個Field對象,它表示此Class對象所代表的類或接口的指定公共成員字段。
  (5)Field[] getFields()
返回包含某些Field對象的數組,表示所代表類中的成員字段。
  (6)Method getMethod(String name,Class… parameterTypes)
返回一個Method對象,它表示的是此Class對象所代表的類的指定公共成員方法。
  (7)Method[] getMehtods()
返回一個包含某些Method對象的數組,是所代表的的類中的公共成員方法。
  (8)String getName()
以String形式返回此Class對象所表示的實體名稱。
  (9)String  getSuperclass()
返回此Class所表示的類的超類的名稱
  (10)boolean isArray()
判定此Class對象是否表示一個數組
  (11)boolean isPrimitive()
判斷指定的Class對象是否是一個基本類型。
  (12)T newInstance()
創建此Class對象所表示的類的一個新實例。

代碼演示
class demo{
	public static void main(String[] args){
		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  
  
		//public boolean isPrimitive()  
		//判定指定的 Class 對象是否表示一個基本類型。 八個基本類型和void  
		System.out.println(cls1.isPrimitive());//false  
		System.out.println(int.class.isPrimitive());//true  
		System.out.println(int.class == Integer.class);//false  
	}
}


反射
1.反射就是把Java類中的各種成分映射成相應的java類
2.一個類中的組成成分
  成員變量、方法、構造函數、包等信息,也用一個個java類來表示(如汽車是一個類,其中的發動機,變速箱等也是對應的一個個類),表示Java類的Class類顯然要提供一系列的方法來獲取其中的變量、方法、構造函數、修飾符、包等信息,這些信息就是用相應的類的實例對象來表示,他們是Field、Method、Contructor、Package等。
3.一個類中的每個成員都可用相應的反射API類的一個實例對象來表示,通過調用Class類的方法可得到這些實例對象。
4.反射中的各種類


Constructor類     
代表某個類的構造方法
1.得到某個類所有的構造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
2.得到某一個構造方法:
例子:    Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//獲得方法時要用到類型
3.創建實例對象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
//調用獲得的方法時要用到上面相同類型的實例對象
4.Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
該方法內部先得到默認的構造方法,然後用該構造方法創建實例對象。
該方法內部的具體代碼是怎樣寫的呢?用到了緩存機制來保存默認構造方法的實例對象。

代碼演示:
class demo2{
	public static void main(String[] args){
		//new String(new StringBuffer("abc"));  
		//public Constructor<T> getConstructor(Class<?>... parameterTypes)  
		//參數是參數類型返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法  
		Constructor constructor1 = String.class.getConstructor(StringBuffer.class);  
  
		//public T newInstance(Object... initargs)  
		//使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。  
		//如果是"abc"運行報錯參數不匹配,因爲這個構造方法對應的是接收StringBuffer類型的構造方法,你在調用的時候傳來String  
		//而不是啓動一個StringBuffer,編譯期看不到,因爲編譯器只知道你是構造方法,所以運行就報錯  
		String str2 = (String)constructor1.newInstance(/*"abc"*/new StringBuffer("abc"));  
		System.out.println(str2.charAt(2));//c  
	}
}


Field類 
代表某個類中的一個成員變量
1.演示用eclipse自動生成Java類的構造方法
2.問題:得到的Field對象是對應到類上面的成員變量,還是對應到對象上的成員變量?類只有一個,而該類的實例對象有多個,如果是與對象關聯,哪關聯的是哪個對象呢?所以字段fieldX 代表的是x的定義,而不是具體的x變量。
3.示例代碼:
class demo3{
	public static void main(String[] args){
		ReflectPoint point = new ReflectPoint(1,7);
		Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
		System.out.println(y.get(point));
		//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
		Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
		x.setAccessible(true);
		System.out.println(x.get(point));
	}
}

代碼演示:
class FieldDemo{
	public static void main(String[] args){
		ReflectPoint pt1 = new ReflectPoint(3,5);  
		//public Field getField(String name)  
		//參數是字段名,返回一個 Field對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。  
		Field fieldY = pt1.getClass().getField("y");  
		//fieldY的值是多少?是5,錯!fieldY不是對象身上的變量,而是類上,要用它去取某個對象上對應的值  
		System.out.println(fieldY.get(pt1));//5  
  
		//public Field getDeclaredField(String name)  
		//參數字段名,返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。  
		Field fieldX = pt1.getClass().getDeclaredField("x");  
  
		//public void setAccessible(boolean flag)  
		//值爲 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。用於訪問私有變量。  
		fieldX.setAccessible(true);  
		System.out.println(fieldX.get(pt1));//3   
	}
}


Method類
代表某個類中的一個成員方法
1.得到類中的某一個方法:
例子:    Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
2.調用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1)); 
如果傳遞給Method對象的invoke()方法的第一個參數爲null,這有着什麼樣的意義呢?說明該Method對象對應的是一個靜態方法!
3.jdk1.4和jdk1.5的invoke方法的區別:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的語法,需要將一個數組作爲參數傳遞給invoke方法時,數組中的每個元素分別對應被調用方法中的一個參數,所以,調用charAt方法的代碼也可以用Jdk1.4改寫爲 charAt.invoke(“str”, new Object[]{1})形式。

代碼演示:
class MethodDemo{
	public static void main(String[] args){
		//public Method getMethod(String name,Class<?>... parameterTypes)  
		//返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。  
		//每一個參數是方法名,第二個參數是參數列表  
Method methodCharAt = String.class.getMethod("charAt", int.class);  
		//public Object invoke(Object obj,Object... args)  
		//對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。  
		//參數:   obj - 從中調用底層方法的對象,args - 用於方法調用的參數  
		//如果是靜態方法可以忽略對象,參數爲null  
		System.out.println(methodCharAt.invoke(str1, 1));//b  
		System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//c  
	}
}


用反射方式執行某個main方法
1.目標:
寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類中的main方法。用普通方式調完後,大家要明白爲什麼要用反射方式去調啊?
2.問題:
啓動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),通過反射方式來調用這個main方法時,如何爲invoke方法傳遞參數呢?按jdk1.5的語法,整個數組是一個參數,而按jdk1.4的語法,數組中的每個元素對應一個參數,當把一個字符串數組作爲參數傳遞給invoke方法時,javac會到底按照哪種語法進行處理呢?jdk1.5肯定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成爲若干個單獨的參數。所以,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,new String[]{“xxx”}),javac只把它當作jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,因此會出現參數類型不對的問題。
3.解決辦法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,編譯器會作特殊處理,編譯時不把參數當作數組看待,也就不會數組打散成若干個參數了

代碼演示:
private static void methodTest(String [] args) throws Exception {    
    String str1 = "abc";    
    //一般方法:    
    System.out.println(str1.charAt(1));    
    //反射方法 :    
    Method methodCharAt =    
        Class.forName("java.lang.String").getMethod("charAt",int.class);    
    System.out.println(methodCharAt.invoke(str1,1));    
        
    //用反射方式執行某個main方法    
    //一般方式:    
    Test.main(new String[]{"111","222","333"});    
    System.out.println("-------");    
        
    //反射方式:    
    String startingClassName = args[0];    
    Method methodMain =    
        Class.forName(startingClassName).getMethod("main",String[].class);    
        //方案一:強制轉換爲超類Object,不用拆包    
        methodMain.invoke(null,(Object)new String[]{"111","222","333"});    
        //方案二:將數組打包,編譯器拆包後就是一個String[]類型的整體    
        methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}});    
    }    
//定義一個測試類    
class Test{    
    public static void main(String [] args){    
        for(String arg : args){    
            System.out.println(arg);    
        }    
    }    
}    


數組的反射
1.具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象(此處比較與值無關)。
2.代表數組的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class。
基本類型的一維數組可以被當作Object類型使用,不能當作Object[]類型使用;非基本類型的3.一維數組,既可以當做Object類型使用,又可以當做Object[]類型使用。
4.Arrays.asList()方法處理int[]和String[]時的差異。
5.Array工具類用於完成對數組的反射操作。

代碼演示:
class ArrayDemo{
	public static void main(String[] args){
		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.getClass() == a2.getClass());//true  
		//System.out.println(a1.getClass() == a4.getClass());//false  
		//System.out.println(a1.getClass() == a3.getClass());//false  
		System.out.println(a1.getClass().getName());//[I  
		System.out.println(a3.getClass().getName());//[[I  
		System.out.println(a4.getClass().getName());//[Ljava.lang.String;  
		System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object  
		System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object  
  
		Object aObj1 = a1;  
		Object aObj2 = a4;  
		//基本類型的一維數組,它是不能轉換成Object數組的,因爲int數組裏裝的是int類型不是Object的  
		//Object[] aObj3 = a1;  
		Object[] aObj4 = a3;  
		Object[] aObj5 = a4;  
  
		System.out.println(a1);//[I@30c028cc  
		System.out.println(a4);//[Ljava.lang.String;@17b68215  
	}
}


反射的作用-實現框架功能
1.框架與框架要解決的核心問題
我做房子賣給用戶住,由用戶自己安裝門窗和空調,我做的房子就是框架,用戶需要使用我的框架,把門窗插入進我提供的框架中。框架與工具類有區別,工具類被用戶的類調用,而框架則是調用用戶提供的類。
2.框架要解決的核心問題
我在寫框架(房子)時,你這個用戶可能還在上小學,還不會寫程序呢?我寫的框架程序怎樣能調用到你以後寫的類(門窗)呢?
因爲在寫才程序時無法知道要被調用的類名,所以,在程序中無法直接new 某個類的實例對象了,而要用反射方式來做。
3.綜合案例
先直接用new  語句創建ArrayList和HashSet的實例對象,演示用eclipse自動生成 ReflectPoint類的equals和hashcode方法,比較兩個集合的運行結果差異。
然後改爲採用配置文件加反射的方式創建ArrayList和HashSet的實例對象,比較觀察運行結果差異。
引入了elipse對資源文件的管理方式的講解。


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