一.概念
反射就是把Java的各種成分映射成相應的Java類。
Class類的構造方法是private,由JVM創建。
反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查并且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法并且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。 (來自Sun)
JavaBean 是 reflection 的實際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過 reflection 動態的載入并取得 Java 組件(類) 的屬性。
反射是從1.2就有的,后面的三大框架都會用到反射機制,涉及到類”Class”,無法直接new CLass(),其對象是內存里的一份字節碼.
Class 類的實例表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,注釋是一種接口。每個數組屬于被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。基本的 Java類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也表示為 Class 對象。Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的。
1
2
3
4
5
6
|
Person p1 = new Person(); //下面的這三種方式都可以得到字節碼 CLass c1 = Date. class (); p1.getClass(); //若存在則加載,否則新建,往往使用第三種,類的名字在寫源程序時不需要知道,到運行時再傳遞過來 Class.forName( "java.lang.String" ); |
CLass.forName()字節碼已經加載到java虛擬機中,去得到字節碼;java虛擬機中還沒有生成字節碼 用類加載器進行加載,加載的字節碼緩沖到虛擬機中。
考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import java.lang.reflect.*; public class DumpMethods { public static void main(String args[]) { try { Class c = Class.forName( "java.util.Stack" ); Method m[] = c.getDeclaredMethods(); for ( int i = 0 ; i < m.length; i++) System.out.println(m[i].toString()); } catch (Throwable e){ System.err.println(e); } } } |
1
2
3
4
5
|
public synchronized java.lang.Object java.util.Stack.pop() public java.lang.Object java.util.Stack.push(java.lang.Object) public boolean java.util.Stack.empty() public synchronized java.lang.Object java.util.Stack.peek() public synchronized int java.util.Stack.search(java.lang.Object) |
這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。這個程序使用 Class.forName 載入指定的類,然后調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。
以下示例使用 Class 對象來顯示對象的類名:
1
2
3
4
|
void printClassName(Object obj) { System.out.println( "The class of " + obj + " is " + obj.getClass().getName()); } |
還可以使用一個類字面值(JLS Section 15.8.2)來獲取指定類型(或 void)的 Class 對象。例如:
1
|
System.out.println( "The name of class Foo is: " +Foo. class .getName()); |
在沒有對象實例的時候,主要有兩種辦法。
1
2
3
|
//獲得類類型的兩種方式 Class cls1 = Role. class ; Class cls2 = Class.forName( "yui.Role" ); |
注意第二種方式中,forName中的參數一定是完整的類名(包名+類名),并且這個方法需要捕獲異常。現在得到cls1就可以創建一個Role類的實例了,利用Class的newInstance方法相當于調用類的默認的構造器。
1
2
3
|
Object o = cls1.newInstance(); //創建一個實例 //Object o1 = new Role(); //與上面的方法等價 |
二.常用方法
1.isPrimitive(判斷是否是基本類型的字節碼)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class TestReflect { public static void main(String[] args) { // TODO Auto-generated method stub String str = "abc" ; Class cls1 = str.getClass(); Class cls2 = String. class ; Class cls3 = null ; //必須要加上null try { cls3 = Class.forName( "java.lang.String" ); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(cls1==cls2); System.out.println(cls1==cls3); System.out.println(cls1.isPrimitive()); System.out.println( int . class .isPrimitive()); //判定指定的 Class 對象是否表示一個基本類型。 System.out.println( int . class == Integer. class ); System.out.println( int . class == Integer.TYPE); System.out.println( int []. class .isPrimitive()); System.out.println( int []. class .isArray()); } } /* * true true false true false true false true */ */ |
2.getConstructor和getConstructors()
java中構造方法沒有先后順序,通過類型和參數個數區分。
1
2
3
4
5
6
7
8
9
10
11
|
/** * Java學習交流QQ群:589809992 我們一起學Java! */ public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException { // TODO Auto-generated method stub String str = "abc" ; System.out.println(String. class .getConstructor(StringBuffer. class )); } } |
3.Filed類代表某一類中的一個成員變量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import java.lang.reflect.Field; public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception { ReflectPointer rp1 = new ReflectPointer( 3 , 4 ); Field fieldx = rp1.getClass().getField( "x" ); //必須是x或者y System.out.println(fieldx.get(rp1)); /* * private的成員變量必須使用getDeclaredField,并setAccessible(true),否則看得到拿不到 */ Field fieldy = rp1.getClass().getDeclaredField( "y" ); fieldy.setAccessible( true ); //暴力反射 System.out.println(fieldy.get(rp1)); } } class ReflectPointer { public int x = 0 ; private int y = 0 ; public ReflectPointer( int x, int y) { //alt + shift +s相當于右鍵source super (); // TODO Auto-generated constructor stub this .x = x; this .y = y; } } |
三.典型例題
1.將所有String類型的成員變量里的b改成a。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import java.lang.reflect.Field; public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception { ReflectPointer rp1 = new ReflectPointer( 3 , 4 ); changeBtoA(rp1); System.out.println(rp1); } private static void changeBtoA(Object obj) throws RuntimeException, Exception { Field[] fields = obj.getClass().getFields(); for (Field field : fields) { //if(field.getType().equals(String.class)) //由于字節碼只有一份,用equals語義不準確 if (field.getType()==String. class ) { String oldValue = (String)field.get(obj); String newValue = oldValue.replace( 'b' , 'a' ); field.set(obj,newValue); } } } } class ReflectPointer { private int x = 0 ; public int y = 0 ; public String str1 = "ball" ; public String str2 = "basketball" ; public String str3 = "itcat" ; public ReflectPointer( int x, int y) { //alt + shift +s相當于右鍵source super (); // TODO Auto-generated constructor stub this .x = x; this .y = y; } @Override public String toString() { return "ReflectPointer [str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]" ; } } |
2.寫一個程序根據用戶提供的類名,調用該類的里的main方法。
為什么要用反射的方式呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java學習交流QQ群:589809992 我們一起學Java! */ public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception { String str = args[ 0 ]; /* * 這樣會數組角標越界,因為壓根沒有這個字符數組 * 需要右鍵在run as-configurations-arguments里輸入b.Inter(完整類名) * */ Method m = Class.forName(str).getMethod("main",String[].class); //下面這兩種方式都可以,main方法需要一個參數 m.invoke(null, new Object[]{new String[]{"111","222","333"}}); m.invoke(null, (Object)new String[]{"111","222","333"});//這個可以說明,數組也是Object /* * m.invoke(null, new String[]{"111","222","333"}) * 上面的不可以,因為java會自動拆包 */ } } class Inter { public static void main(String[] args) { for (Object obj : args) { System.out.println(obj); } } } |
3.模擬 instanceof 操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class S { } public class IsInstance { public static void main(String args[]) { try { Class cls = Class.forName( "S" ); boolean b1 = cls.isInstance( new Integer( 37 )); System.out.println(b1); boolean b2 = cls.isInstance( new S()); System.out.println(b2); } catch (Throwable e) { System.err.println(e); } } } |
在這個例子中創建了一個S 類的 Class 對象,然后檢查一些對象是否是S的實例。Integer(37) 不是,但 new S()是。
四.Method類
代表類(不是對象)中的某一方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import java.lang.reflect.Field; import java.lang.reflect.Method; /* * 人在黑板上畫圓,涉及三個對象,畫圓需要圓心和半徑,但是是私有的,畫圓的方法 * 分配給人不合適。Java學習交流QQ群:589809992 我們一起學Java! * * 司機踩剎車,司機只是給列車發出指令,剎車的動作還需要列車去完成。 * * 面試經常考面向對象的設計,比如人關門,人只是去推門。 * * 這就是專家模式:誰擁有數據,誰就是專家,方法就分配給誰 */ public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception { String str = "shfsfs" ; //包開頭是com表示是sun內部用的,java打頭的才是用戶的 Method mtCharAt = String. class .getMethod( "charAt" , int . class ); Object ch = mtCharAt.invoke(str, 1 ); //若第一個參數是null,則肯定是靜態方法 System.out.println(ch); System.out.println(mtCharAt.invoke(str, new Object[]{ 2 })); //1.4語法 } } |
五.數組的反射
Array工具類用于完成數組的反射操作。
同類型同緯度有相同的字節碼。
int.class和Integer.class不是同一份字節碼,Integer.TYPE,TYPE代表包裝類對應的基本類的字節碼 int.class==Integer.TYPE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import java.util.Arrays; /* * 從這個例子看出即便字節碼相同但是對象也不一定相同,根本不是一回事 * */ public class TestReflect { public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception { int [] a = new int [ 3 ]; int [] b = new int []{ 4 , 5 , 5 }; //直接賦值后不可以指定長度,否則CE int [][] c = new int [ 3 ][ 2 ]; String[] d = new String[]{ "jjj" , "kkkk" }; System.out.println(a==b); //false System.out.println(a.getClass()==b.getClass()); //true //System.out.println(a.getClass()==d.getClass()); //比較字節碼a和cd也沒法比 System.out.println(a.getClass()); //輸出class [I System.out.println(a.getClass().getName()); //輸出[I,中括號表示數組,I表示整數 System.out.println(a.getClass().getSuperclass()); //輸出class java.lang.Object System.out.println(d.getClass().getSuperclass()); //輸出class java.lang.Object //由于父類都是Object,下面都是可以的 Object obj1 = a; //不可是Object[] Object obj2 = b; Object[] obj3 = c; //基本類型的一位數組只可以當做Object,非得還可以當做Object[] Object obj4 = d; //注意asList處理int[]和String[]的區別 System.out.println(Arrays.asList(b)); //1.4沒有可變參數,使用的是數組,[[I@1bc4459] System.out.println(Arrays.asList(d)); //[jjj, kkkk] } } |
六.結束語
以上就是反射機制的簡單的使用,顯然學過spring的朋友一定明白了,為什么可以通過配置文件就可以讓我們獲得指定的方法和變量,在我們創建對象的時候都是通過傳進string實現的,就好像你需要什么,我們去為你生產,還有我們一直在用Object,這就說明java語言的動態特性,依賴性大大的降低了。
如有疑問請留言或者到本站社區交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:http://geek.csdn.net/news/detail/237078