Java反射機制(轉)
一、什麼是JAVA的反射機制
Java反射是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static 等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields和methods的所有資訊,並可於執行時改變fields內容或喚起methods。
Java反射機制容許程式在執行時載入、探知、使用編譯期間完全未知的classes。
換言之,Java可以載入一個執行時才得知名稱的class,獲得其完整結構。
二、JDK中提供的Reflection API
Java反射相關的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下圖:
Member介面 | 該介面可以獲取有關類成員(域或者方法)後者建構函式的資訊。 |
AccessibleObject類 | 該類是域(field)物件、方法(method)物件、建構函式(constructor)物件的基礎類。它提供了將反射的物件標記為在使用時取消預設 Java 語言訪問控制檢查的能力。 |
Array類 | 該類提供動態地生成和訪問JAVA陣列的方法。 |
Constructor類 | 提供一個類的建構函式的資訊以及訪問類的建構函式的介面。 |
Field類 | 提供一個類的域的資訊以及訪問類的域的介面。 |
Method類 | 提供一個類的方法的資訊以及訪問類的方法的介面。 |
Modifier類 | 提供了 static 方法和常量,對類和成員訪問修飾符進行解碼。 |
Proxy類 |
提供動態地生成代理類和類例項的靜態方法。 |
三、JAVA反射機制提供了什麼功能
Java反射機制提供如下功能:
在執行時判斷任意一個物件所屬的類
在執行時構造任意一個類的物件
在執行時判段任意一個類所具有的成員變數和方法
在執行時呼叫任一個物件的方法
在執行時建立新類物件
在使用Java的反射功能時,基本首先都要獲取類的Class物件,再通過Class物件獲取其他的物件。
這裡首先定義用於測試的類:
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
46
47
48
49
50
51
52
53
54
55
|
class Type{ public int pubIntField; public String pubStringField; private int prvIntField; public Type(){ Log( "Default Constructor" ); } Type( int arg1, String arg2){ pubIntField = arg1; pubStringField = arg2; Log( "Constructor with parameters" ); } public void setIntField( int val) { this .prvIntField = val; } public int getIntField() { return prvIntField; } private void Log(String msg){ System.out.println( "Type:" + msg); } } class ExtendType extends Type{ public int pubIntExtendField; public String pubStringExtendField; private int prvIntExtendField; public ExtendType(){ Log( "Default Constructor" ); } ExtendType( int arg1, String arg2){ pubIntExtendField = arg1; pubStringExtendField = arg2; Log( "Constructor with parameters" ); } public void setIntExtendField( int field7) { this .prvIntExtendField = field7; } public int getIntExtendField() { return prvIntExtendField; } private void Log(String msg){ System.out.println( "ExtendType:" + msg); } } |
1、獲取類的Class物件
Class 類的例項表示正在執行的 Java 應用程式中的類和介面。獲取類的Class物件有多種方式:
呼叫getClass |
Boolean var1 = true; Class<?> classType2 = var1.getClass(); System.out.println(classType2); 輸出:class java.lang.Boolean |
運用.class 語法 |
Class<?> classType4 = Boolean.class; System.out.println(classType4); 輸出:class java.lang.Boolean |
運用static method Class.forName() |
Class<?> classType5 = Class.forName("java.lang.Boolean"); System.out.println(classType5); 輸出:class java.lang.Boolean |
運用primitive wrapper classes的TYPE 語法 這裡返回的是原生型別,和Boolean.class返回的不同 |
Class<?> classType3 = Boolean.TYPE; System.out.println(classType3); 輸出:boolean |
2、獲取類的Fields
可以通過反射機制得到某個類的某個屬性,然後改變對應於這個類的某個例項的該屬性值。JAVA 的Class<T>類提供了幾個方法獲取類的屬性。
public Field getField(String name) | 返回一個 Field 物件,它反映此 Class 物件所表示的類或介面的指定公共成員欄位 |
public Field[] getFields() | 返回一個包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位 |
public Field getDeclaredField(String name) | 返回一個 Field 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告欄位 |
public Field[] getDeclaredFields() |
返回 Field 物件的一個陣列,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Class<?> classType = ExtendType. class ; // 使用getFields獲取屬性 Field[] fields = classType.getFields(); for (Field f : fields) { System.out.println(f); } System.out.println(); // 使用getDeclaredFields獲取屬性 fields = classType.getDeclaredFields(); for (Field f : fields) { System.out.println(f); } |
輸出:
public int com.quincy.ExtendType.pubIntExtendField
public java.lang.String com.quincy.ExtendType.pubStringExtendField
public int com.quincy.Type.pubIntField
public java.lang.String com.quincy.Type.pubStringField
public int com.quincy.ExtendType.pubIntExtendField
public java.lang.String com.quincy.ExtendType.pubStringExtendField
private int com.quincy.ExtendType.prvIntExtendField
可見getFields和getDeclaredFields區別:
getFields返回的是申明為public的屬性,包括父類中定義,
getDeclaredFields返回的是指定類定義的所有定義的屬性,不包括父類的。
3、獲取類的Method
通過反射機制得到某個類的某個方法,然後呼叫對應於這個類的某個例項的該方法
Class<T>類提供了幾個方法獲取類的方法。
public Method getMethod(String name, Class<?>... parameterTypes) |
返回一個 Method 物件,它反映此 Class 物件所表示的類或介面的指定公共成員方法 |
public Method[] getMethods() |
返回一個包含某些 Method 物件的陣列,這些物件反映此 Class 物件所表示的類或介面(包括那些由該類或介面宣告的以及從超類和超介面繼承的那些的類或介面)的公共 member 方法 |
public Method getDeclaredMethod(String name,Class<?>... parameterTypes) |
返回一個 Method 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告方法 |
public Method[] getDeclaredMethods() |
返回 Method 物件的一個陣列,這些物件反映此 Class 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 使用getMethods獲取函式 Class<?> classType = ExtendType. class ; Method[] methods = classType.getMethods(); for (Method m : methods) { System.out.println(m); } System.out.println(); // 使用getDeclaredMethods獲取函式 methods = classType.getDeclaredMethods(); for (Method m : methods) { System.out.println(m); } |
輸出:
public void com.quincy.ExtendType.setIntExtendField(int)
public int com.quincy.ExtendType.getIntExtendField()
public void com.quincy.Type.setIntField(int)
public int com.quincy.Type.getIntField()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
private void com.quincy.ExtendType.Log(java.lang.String)
public void com.quincy.ExtendType.setIntExtendField(int)
public int com.quincy.ExtendType.getIntExtendField()
4、獲取類的Constructor
通過反射機制得到某個類的構造器,然後呼叫該構造器建立該類的一個例項
Class<T>類提供了幾個方法獲取類的構造器。
public Constructor<T> getConstructor(Class<?>... parameterTypes) |
返回一個 Constructor 物件,它反映此 Class 物件所表示的類的指定公共構造方法 |
public Constructor<?>[] getConstructors() |
返回一個包含某些 Constructor 物件的陣列,這些物件反映此 Class 物件所表示的類的所有公共構造方法 |
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) |
返回一個 Constructor 物件,該物件反映此 Class 物件所表示的類或介面的指定構造方法 |
public Constructor<?>[] getDeclaredConstructors() |
返回 Constructor 物件的一個陣列,這些物件反映此 Class 物件表示的類宣告的所有構造方法。它們是公共、保護、預設(包)訪問和私有構造方法 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// 使用getConstructors獲取構造器 Constructor<?>[] constructors = classType.getConstructors(); for (Constructor<?> m : constructors) { System.out.println(m); } System.out.println(); // 使用getDeclaredConstructors獲取構造器 constructors = classType.getDeclaredConstructors(); for (Constructor<?> m : constructors) { System.out.println(m); } 輸出: public com.quincy.ExtendType() public com.quincy.ExtendType() com.quincy.ExtendType( int ,java.lang.String) |
5、新建類的例項
通過反射機制建立新類的例項,有幾種方法可以建立
呼叫無自變數ctor |
1、呼叫類的Class物件的newInstance方法,該方法會呼叫物件的預設構造器,如果沒有預設構造器,會呼叫失敗. Class<?> classType = ExtendType.class; Object inst = classType.newInstance(); System.out.println(inst); 輸出: Type:Default Constructor ExtendType:Default Constructor
2、呼叫預設Constructor物件的newInstance方法 Class<?> classType = ExtendType.class; Constructor<?> constructor1 = classType.getConstructor(); Object inst = constructor1.newInstance(); System.out.println(inst); 輸出: Type:Default Constructor ExtendType:Default Constructor com.quincy.ExtendType@1006d75 |
呼叫帶引數ctor |
3、呼叫帶引數Constructor物件的newInstance方法 Constructor<?> constructor2 = classType.getDeclaredConstructor(int.class, String.class); Object inst = constructor2.newInstance(1, "123"); System.out.println(inst); 輸出: Type:Default Constructor ExtendType:Constructor with parameters com.quincy.ExtendType@15e83f9 |
6、呼叫類的函式
通過反射獲取類Method物件,呼叫Field的Invoke方法呼叫函式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
Class<?> classType = ExtendType. class ; Object inst = classType.newInstance(); Method logMethod = classType.<STRONG>getDeclaredMethod</STRONG>( "Log" , String. class ); logMethod.invoke(inst, "test" ); 輸出: Type:Default Constructor ExtendType:Default Constructor <FONT color=#ff0000>Class com.quincy.ClassT can not access a member of class com.quincy.ExtendType with modifiers "private" </FONT> <FONT color=#ff0000>上面失敗是由於沒有許可權呼叫 private 函式,這裡需要設定Accessible為 true ;</FONT> Class<?> classType = ExtendType. class ; Object inst = classType.newInstance(); Method logMethod = classType.getDeclaredMethod( "Log" , String. class ); <FONT color=#ff0000>logMethod.setAccessible( true );</FONT> logMethod.invoke(inst, "test" ); |
7、設定/獲取類的屬性值
通過反射獲取類的Field物件,呼叫Field方法設定或獲取值
1
2
3
4
5
|
Class<?> classType = ExtendType. class ; Object inst = classType.newInstance(); Field intField = classType.getField( "pubIntExtendField" ); intField.<STRONG>setInt</STRONG>(inst, 100 ); int value = intField.<STRONG>getInt</STRONG>(inst); |
1
|
|
四、動態建立代理類
代理模式:代理模式的作用=為其他物件提供一種代理以控制對這個物件的訪問。
代理模式的角色:
抽象角色:宣告真實物件和代理物件的共同介面
代理角色:代理角色內部包含有真實物件的引用,從而可以操作真實物件。
真實角色:代理角色所代表的真實物件,是我們最終要引用的物件。
動態代理:
java.lang.reflect.Proxy |
Proxy 提供用於建立動態代理類和例項的靜態方法,它還是由這些方法建立的所有動態代理類的超類 |
InvocationHandler |
是代理例項的呼叫處理程式 實現的介面,每個代理例項都具有一個關聯的呼叫處理程式。對代理例項呼叫方法時,將對方法呼叫進行編碼並將其指派到它的呼叫處理程式的 invoke 方法。 |
動態Proxy是這樣的一種類:
它是在執行生成的類,在生成時你必須提供一組Interface給它,然後該class就宣稱它實現了這些interface。你可以把該class的例項當作這些interface中的任何一個來用。當然,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的例項時你必須提供一個handler,由它接管實際的工作。
在使用動態代理類時,我們必須實現InvocationHandler介面
步驟:
1、定義抽象角色
public interface Subject {
public void Request();
}
2、定義真實角色
public class RealSubject implements Subject {
@Override
public void Request() {
// TODO Auto-generated method stub
System.out.println("RealSubject");
}
}
3、定義代理角色
public class DynamicSubject implements InvocationHandler {
private Object sub;
public DynamicSubject(Object obj){
this.sub = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("Method:"+ method + ",Args:" + args);
method.invoke(sub, args);
return null;
}
}
4、通過Proxy.newProxyInstance構建代理物件
RealSubject realSub = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSub);
Class<?> classType = handler.getClass();
Subject sub = (Subject)Proxy.newProxyInstance(classType.getClassLoader(),
realSub.getClass().getInterfaces(), handler);
System.out.println(sub.getClass());
5、通過呼叫代理物件的方法去呼叫真實角色的方法。
sub.Request();
輸出:
class $Proxy0 新建的代理物件,它實現指定的介面
Method:public abstract void DynamicProxy.Subject.Request(),Args:null
RealSubject 呼叫的真實物件的方法
待續...
相關文章
- Java反射機制Java反射
- Java核心反射機制Java反射
- java利器——反射機制Java反射
- Java的反射機制Java反射
- Java反射機制研究Java反射
- Java反射機制那些事Java反射
- Java 中的 反射機制Java反射
- Java反射機制簡答Java反射
- 說說 Java 反射機制Java反射
- JAVA(五)反射機制/AnnotationJava反射
- Java 反射機制分析指南Java反射
- Java 反射機制詳解Java反射
- Java中的類反射機制Java反射
- Java筆記-反射機制(一)Java筆記反射
- java進階(41)--反射機制Java反射
- 淺析java的反射機制Java反射
- Java註解與反射機制Java反射
- java反射機制的學習心得Java反射
- 關於Java中的反射機制Java反射
- Java基礎之反射機制(續)Java反射
- Java重點基礎:反射機制Java反射
- Java動態代理和反射機制Java反射
- Java 反射機制的三種方式Java反射
- Java 反射機制應用實踐Java反射
- Java反射機制應用實踐Java反射
- Java反射機制實現與原理Java反射
- java中的反射機制淺析Java反射
- Java 反射機制學習資料Java反射
- JAVA中的反射機制詳解Java反射
- 深度剖析訊息反射機制 (轉)反射
- 菜鳥學Java(十四)——Java反射機制(一)Java反射
- 菜鳥學Java(十五)——Java反射機制(二)Java反射
- JAVA的反射機制==>用反射分析類的實現Java反射
- Java 從入門到精通-反射機制Java反射
- 淺談Java的反射機制和作用Java反射
- 基礎篇:深入解析JAVA反射機制Java反射
- Java筆記-反射機制(三)-小demoJava筆記反射
- 分析與理解訊息反射機制 (轉)反射