為前期學習過反射,再這里再次復習總結下:【轉載請說明來源:http://www.cnblogs.com/pony1223/p/7659210.html 】
一、透徹分析反射的基礎_Class類
Class是一個類,他代表一類事物,它代表一類什么樣的事物呢? Java 程序中的各個java類屬於同一類事物,描述這類事物的java類名就是Class.
思考:
眾多的人可以用一個什么類表示? Person
眾多的類用一個什么類來表示? 答案是Class
我們知道Person代表一個人,一個人有他的身高,體重等屬性,有叫,喊等方法,人還有他的父類,它的構造方法.接口等.
那么Class類呢?他是所有類的類.那么他可以干什么呢?他可以獲取某一個類的屬性,方法,父類,構造方法,接口等....
(1)Class的本質是什么?
我們現在定義兩個對象:
Person p1 = new Person();
Person p2 = new Person();
那如何獲取這兩個對象的Class類呢?用Class cls1 = new Class()可以么?不可以,因為Class跟們就沒有這個構造方法.
Class類在內存中代表的就是一段字節碼,那什么是字節碼呢?當我們在源程序用到Person這個類的時候,首先把這個類編譯成class文件以后,存放到硬盤上,這些class文件其實就是一堆二進制代碼,要把這一堆二進制代碼加載到內存中來,然后才可以去創建一個個對象.當我在程序中用好多java類,有Person類,Math類,Date類,我又到這三個類,那么在內容中會有幾個字節碼呢?有三份字節碼,每一份字節碼就是Class的一個實例對象.
Class cls1 = Date.class;//代表Date類的字節碼
Class cls2 = Pserson.class;//代表Person類的字節碼
(2)那如何來得到這個Class呢?
總結:一個類的Class字節碼只存在一份.
Class.isPrimitive()方法.判斷一個類型是否是基本的數據類型.


總結:只要在源程序中出現的類型,都有各自的Class實例對象,例如:int[], void...
二、理解反射的概念
第一種官方理解:
反射是指在運行狀態中,對於任意一個類,都可以獲取到這個類的所有屬性和方法;對於任意一個對象,都能夠調用這個對象的任意方法和屬性;這種動態獲取信息及動態調用對象的方法,稱為JAVA語言的反射機制。
第二種個人理解:
反射,就是把Java類中的各個成分映射成相應的Java類.我們想想,一個類身上有什么,有包,有方法,有屬性,有構造方法,有接口.而這些方法相應的又返回一個類.看下面這個api,通過Class的方法可以得到什么.


三、構造方法的反射應用
package study.javaenhance; import java.lang.reflect.Constructor; public class ReflectTest { public static void main(String[] args) throws Exception { String str = "abc"; Class clz1 = String.class; Class clz2 = str.getClass(); Class clz3 = Class.forName("java.lang.String"); System.out.println(clz1 == clz2); System.out.println(clz1 == clz3); getConstructor(); } /** * 目前String 類型中有很多構造方法,我想要找到獲取構造方法為StringBuffer類型的一個 * @throws Exception * @throws SecurityException */ public static void getConstructor() throws SecurityException, Exception { String.class.getConstructor(StringBuffer.class); //那么上面的獲取到構造方法有什么用呢? /** * 通常,我們創建String 對象為 String abc = new String(new StringBuffer("abc")) 方式, * 下面我們通過反射來實現: * 1.加載類 * 2.解析類 * */ Constructor constructor = String.class.getConstructor(StringBuffer.class); String str = (String) constructor.newInstance(new StringBuffer("abc")); System.out.println(str); } }
具體的原理,如何使用可以參考:http://www.cnblogs.com/pony1223/p/7445950.html
四、成員變量的反射
成員變量的反射,在這里我們先定義一個類,用於講解成員變量的反射。
package study.javaenhance.util;import java.util.Date; 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; } }
接下來,我們要采用反射的方式來獲取其中的x 成員和 y成員
//成員變量的反射 //1.首先我們先初始化一下ReflectPoint類的xy值,以便反射的時候獲取其值 ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getField("x"); //3.解析類 System.out.println(xField.get(rp));
上述會出現一個錯誤:
Exception in thread "main" java.lang.NoSuchFieldException: x
at java.lang.Class.getField(Class.java:1520)
at study.javaenhance.ReflectTest.main(ReflectTest.java:25)
也就是說找不到這個,為什么呢?很明顯,因為是私有的,因此需要采用另外一個方法來獲取,修改如下:
ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getDeclaredField("x"); //3.解析類 System.out.println(xField.get(rp));
Exception in thread "main" java.lang.IllegalAccessException: Class study.javaenhance.ReflectTest can not access a member of class study.javaenhance.util.ReflectPoint with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Field.doSecurityCheck(Field.java:960)
at java.lang.reflect.Field.getFieldAccessor(Field.java:896)
at java.lang.reflect.Field.get(Field.java:358)
at study.javaenhance.ReflectTest.main(ReflectTest.java:27)
現在是看到,卻沒法拿到,需要修改訪問的權限打開,修改為如下:
ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getDeclaredField("x"); xField.setAccessible(true); //3.解析類 System.out.println(xField.get(rp));
對於非私有的成員變量,沒有這么麻煩,如下即可:
//成員變量的反射 //1.首先我們先初始化一下ReflectPoint類的xy值,以便反射的時候獲取其值 ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getDeclaredField("x"); xField.setAccessible(true); //3.解析類 System.out.println(xField.get(rp)); Field yField = ReflectPoint.class.getField("y"); System.out.println(yField.get(rp));
下面做一個小例子,我們看到ReflectPoint類中有很多成員變量,我們現在要做的是將String類型的成員變量的值中含b的字母替換稱為a,如:ball 替換為aall
代碼如下:
private static void changeStringValue(Object rp) throws Exception { //思路:1.反射獲取要所有的成員變量 2.獲取成員變量的類型判斷是否為String類型 3.如果是則獲取其值 4.替換 5.回填 Field[] fields = rp.getClass().getFields(); for (Field field : fields) { if(field.getType() == String.class) { String oldValue = (String) field.get(rp); String newValue = oldValue.replace("b", "a"); field.set(rp, newValue); } } }
客戶端調用代碼:
ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getDeclaredField("x"); xField.setAccessible(true); //3.解析類 System.out.println(xField.get(rp)); Field yField = ReflectPoint.class.getField("y"); System.out.println(yField.get(rp)); //案例:changeStringValue System.out.println(rp); changeStringValue(rp); System.out.println(rp);
結果為:
2
4
ball:basketball:itcast
aall:aasketaall:itcast
五、成員方法的反射
//方法的反射 Method methodCharAt = String.class.getMethod("charAt", int.class); System.out.println(methodCharAt.invoke(str, 1)); //兼容JDK5.0之前的寫法 System.out.println(methodCharAt.invoke(str, new Object[]{2}));
可以看出還是比較簡單的和成員的方式差不多,但是下面需要注意的是,如果有一個類,我們需要通過反射的方式去調用main方法,該如何去做呢?
先定義一個類:
class TestArguments{ public static void main(String[] args){ for(String arg : args){ System.out.println(arg); } } }
最先想到的代碼如下:
//調用main方法,不采用TestArguments.main(new String[]{"1","2","3"}); 這種方式,因為有些場景我們的類是傳入的是你運行期才知道的 Class clazz = Class.forName("study.javaenhance.TestArguments"); Method method = clazz.getMethod("main",String[].class); //因為main方法是靜態方法,所以第一個參數傳入的為null method.invoke(null,new String[]{"1","2","3"});
結果如下:

為什么會這樣呢?我們本身的使用其實是正確的,只是因為SUN公司的JDK5.0的可變參數出現后,為了兼容以前的數組方式所以出現了上面的錯誤。
在JDK5.0前:public Object invoke(Object obj, Object... args) 是采用的為 public Object invoke(Object obj, Object[] obj)
采用的數組方式進行的接受,那么這個時候你傳入的是String[]{"1","2","3"} 那么它正好和Object[] obj匹配,因為在1.5前就會開始拆分,那么就變成了傳入了3個String類的參數到了main方法上面,因此這個時候就參數類型個數無法匹配了,所以就出現了上面的錯誤了,如何修改匹配呢?方式如下:
//調用main方法,不采用TestArguments.main(new String[]{"1","2","3"}); 這種方式,因為有些場景我們的類是傳入的是你運行期才知道的 Class clazz = Class.forName("study.javaenhance.TestArguments"); Method method = clazz.getMethod("main",String[].class); //因為main方法是靜態方法,所以第一個參數傳入的為null //method.invoke(null,new String[]{"1","2","3"}); //這樣拆開后就是main方法想要的參數了 method.invoke(null,new Object[]{new String[]{"1","2","3"}}); //這樣就是一個對象傳入了,不需要拆了 method.invoke(null,(Object)new String[]{"1","2","3"});
六、數組與Object的關系
先看下面的例子:
//數組與Object的關系 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(a1.getClass().getSuperclass().getName());//java.lang.Object System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
從上面的例子說明:
具有相同的維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象
代表數組的Class實例對象的個頭Superclass方法返回的父類為Object類對應的class
基本類型的一維數組可以被當做Object類型使用個,不能當做Object[]類型使用;非基本類型的一維數組,既可以當做Object類使用,又可以當做Object[]類型使用 --- 這句話需要理解一下,參考下面的代碼:
//數組與Object的關系 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(a1.getClass().getSuperclass().getName());//java.lang.Object System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object Object aObj1 = a1; //因為a1 是一個int[] 類型的數組所以是object類型 Object aObj2 = a4;//因為a4 是一個String[]類型的數組所以是Object類型 //Object[] aObj3 = a1;// 報錯,因為a1 是一個int[]數組里面的元素都是int型,不是Object類型所以不可以如果改成包裝類就可以 Object[] aObj4 = a3; //a3 是一個二維數組,那么里面的一維數組就是一個Object類型,所以可以匹配Object[]代表里面每個元素類型為Object Object[] aObj5 = a4; //String[] 里面的每一個元素都是String類型,也是Object類型 所以可以匹配. System.out.println(a1); System.out.println(a4);
我們發現最后a1 和 a4 的輸出是:
[I@87816d
[Ljava.lang.String;@422ede
這個是什么東西呢?我們找下JDK文檔看下:
在Class 的getName 方法中說明如下:

即代表的是int類型的數組和String類型的數組,但我們肯定不希望輸出這個樣子,我們希望的看到的是里面的值,那么肯定有人會說用遍歷的方法遍歷出來,當然這是一種方法,但沒有簡單的方法嗎?
七、數組的反射應用
上面提到了遺留的問題,我們本節來解決,解決前我們先學習兩個類:Array類 和Arrays類
Array 類提供了動態創建和訪問 Java 數組的方法。需要說明下Array 是 數組的反射類,處於反射包下面,所以我們看到引用類型下對於類,我們是用Class 對於數組我們是用Array
Arrays 此類包含用來操作數組(比如排序和搜索)的各種方法。此類還包含一個允許將數組作為列表來查看的靜態工廠。
我們現在就是要利用Arrays類中的asList方法來完成顯示的目的.
//數組與Object的關系 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(a1.getClass().getSuperclass().getName());//java.lang.Object System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object Object aObj1 = a1; //因為a1 是一個int[] 類型的數組所以是object類型 Object aObj2 = a4;//因為a4 是一個String[]類型的數組所以是Object類型 //Object[] aObj3 = a1;// 報錯,因為a1 是一個int[]數組里面的元素都是int型,不是Object類型所以不可以如果改成包裝類就可以 Object[] aObj4 = a3; //a3 是一個二維數組,那么里面的一維數組就是一個Object類型,所以可以匹配Object[]代表里面每個元素類型為Object Object[] aObj5 = a4; //String[] 里面的每一個元素都是String類型,也是Object類型 所以可以匹配. System.out.println(a1); System.out.println(a4); //Array應用 System.out.println(Arrays.asList(a1)); System.out.println(Arrays.asList(a4));
結果為:
[[I@87816d]
[a, b, c]
為什么呢?如果對前面的數組與Object 關系理解的話,這里就清楚了,因為在5.0之前沒有可變參數,因此傳入的a1 是int[] 這個時候在Object[] 數組接受的時候發現Object[] = a1 這個是不匹配的,因此這個時候它無法處理,於是會走可變參數可變參數的時候,於是會當做一個參數處理,那么就是List集合里面直接添加這個數組了,然后結果顯示為[[I@hashCode 了;而String[] 傳入的時候,因為Object[] = String[] 是匹配的於是會走5.0前的方式,於是會拆分,那么List集合中就添加了3個元素,所以就顯示出了上述的結果。
那么現在有一個需求:
寫一個方法,傳遞一個參數,判斷這個參數是一個數組,還是一個普通類型,如果是普通類型,我就直接把他打印出來。如果是數組,我們就遍歷后把值打印出來。(好處,就是不用每次都去for循環,只需要調用這個方法即可,公共性)
private static void printObject(Object obj) { Class clazz = obj.getClass(); if(clazz.isArray()) { //如果是數組,利用數組的反射類Array類來進行相關操作 int len = Array.getLength(obj); for(int i=0;i<len;i++){ System.out.println(Array.get(obj, i)); } } else { System.out.println(obj); } }
即完成了之前的遺留問題.
到此基本的反射應用總結到這里,最后附上全代碼。
package study.javaenhance; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import study.javaenhance.util.ReflectPoint; public class ReflectTest { public static void main(String[] args) throws Exception { String str = "abc"; Class clz1 = String.class; Class clz2 = str.getClass(); Class clz3 = Class.forName("java.lang.String"); System.out.println(clz1 == clz2); System.out.println(clz1 == clz3); getConstructor(); //成員變量的反射 //1.首先我們先初始化一下ReflectPoint類的xy值,以便反射的時候獲取其值 ReflectPoint rp = new ReflectPoint(2,4); //2.要反射,后續加載類 Field xField = rp.getClass().getDeclaredField("x"); xField.setAccessible(true); //3.解析類 System.out.println(xField.get(rp)); Field yField = ReflectPoint.class.getField("y"); System.out.println(yField.get(rp)); //案例:changeStringValue System.out.println(rp); changeStringValue(rp); System.out.println(rp); //方法的反射 Method methodCharAt = String.class.getMethod("charAt", int.class); System.out.println(methodCharAt.invoke(str, 1)); //兼容JDK5.0之前的寫法 System.out.println(methodCharAt.invoke(str, new Object[]{2})); //調用main方法,不采用TestArguments.main(new String[]{"1","2","3"}); 這種方式,因為有些場景我們的類是傳入的是你運行期才知道的 Class clazz = Class.forName("study.javaenhance.TestArguments"); Method method = clazz.getMethod("main",String[].class); //因為main方法是靜態方法,所以第一個參數傳入的為null //method.invoke(null,new String[]{"1","2","3"}); //這樣拆開后就是main方法想要的參數了 method.invoke(null,new Object[]{new String[]{"1","2","3"}}); //這樣就是一個對象傳入了,不需要拆了 method.invoke(null,(Object)new String[]{"1","2","3"}); //數組與Object的關系 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(a1.getClass().getSuperclass().getName());//java.lang.Object System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object Object aObj1 = a1; //因為a1 是一個int[] 類型的數組所以是object類型 Object aObj2 = a4;//因為a4 是一個String[]類型的數組所以是Object類型 //Object[] aObj3 = a1;// 報錯,因為a1 是一個int[]數組里面的元素都是int型,不是Object類型所以不可以如果改成包裝類就可以 Object[] aObj4 = a3; //a3 是一個二維數組,那么里面的一維數組就是一個Object類型,所以可以匹配Object[]代表里面每個元素類型為Object Object[] aObj5 = a4; //String[] 里面的每一個元素都是String類型,也是Object類型 所以可以匹配. System.out.println(a1); System.out.println(a4); //Array應用 System.out.println(Arrays.asList(a1)); System.out.println(Arrays.asList(a4)); //寫一個方法,傳遞一個參數,判斷這個參數是一個數組,還是一個普通類型,如果是普通類型,我就直接把他打印出來。如果是數組,我們就遍歷后把值打印出來 printObject(a1); printObject(a4); printObject("xyz"); } private static void printObject(Object obj) { Class clazz = obj.getClass(); if(clazz.isArray()) { //如果是數組,利用數組的反射類Array類來進行相關操作 int len = Array.getLength(obj); for(int i=0;i<len;i++){ System.out.println(Array.get(obj, i)); } } else { System.out.println(obj); } } private static void changeStringValue(Object rp) throws Exception { //思路:1.反射獲取要所有的成員變量 2.獲取成員變量的類型判斷是否為String類型 3.如果是則獲取其值 4.替換 5.回填 Field[] fields = rp.getClass().getFields(); for (Field field : fields) { if(field.getType() == String.class) { String oldValue = (String) field.get(rp); String newValue = oldValue.replace("b", "a"); field.set(rp, newValue); } } } /** * 目前String 類型中有很多構造方法,我想要找到獲取構造方法為StringBuffer類型的一個 * @throws Exception * @throws SecurityException */ public static void getConstructor() throws SecurityException, Exception { String.class.getConstructor(StringBuffer.class); //那么上面的獲取到構造方法有什么用呢? /** * 通常,我們創建String 對象為 String abc = new String(new StringBuffer("abc")) 方式, * 下面我們通過反射來實現: * 1.加載類 * 2.解析類 * */ Constructor constructor = String.class.getConstructor(StringBuffer.class); String str = (String) constructor.newInstance(new StringBuffer("abc")); System.out.println(str); } } class TestArguments{ public static void main(String[] args){ for(String arg : args){ System.out.println(arg); } } }
參考資料:
張孝祥Java基礎增強
