Java反射機制(轉)

小小IT墨魚發表於2017-12-01
(轉)http://www.cnblogs.com/Quincy/archive/2011/06/19/2084557.html

一、什麼是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包如下圖:

clip_image001

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

com.quincy.ExtendType@d80be3

 

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 呼叫的真實物件的方法

待續...

相關文章