初探Java反射機制

  反射庫提供了一個很是豐富且精心設計的工具集,以便編寫可以動態操縱java代碼的程序庫。這項功能被大量地應用於JavaBeans中。反射機制提供了在運行狀態中得到和調用修改任何一個類的屬性和方法的能力。html

  Java反射機制主要提供瞭如下功能: 在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具備的成員變量和方法;在運行時調用任意一個對象的方法;生成動態代理。java

  首先讓咱們來看一個簡單的小程序,感性的認識一下Java反射機制:小程序

 1 import java.lang.reflect.*;  2 
 3 public class reflection {  4     public static void main(String args[]) {  5         try {  6             Class c = Class.forName("java.util.Stack");  7             Method m[] = c.getDeclaredMethods();  8                 
 9             for (int i = 0; i < m.length; i++) 10  System.out.println(m[i].toString()); 11  } 12         catch (Throwable e){ 13  System.err.println(e); 14  } 15  } 16     }

輸出結果爲:數組

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反射機制列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。接下來,讓咱們詳細分析一下Java反射機制的原理和使用。安全

Class類ide

  要了解Java反射機制,首先必須得了解Class類。在程序運行期間,Java運行時系統始終爲全部的對象維護一個被成爲運行時的類型標識,這個信息跟蹤着每一個對象所屬的類。虛擬機利用運行時信息選擇相應的方法執行。保存這些信息的類被稱爲Class,這個名字很容易讓人混淆。下面來看一個例子:函數

 1 package Reflect;  2 
 3 /**
 4  * 經過一個對象得到完整的包名和類名
 5  * */
 6 class Demo{  7     //other codes...
 8 }  9  
10 class hello{ 11     public static void main(String[] args) { 12         Demo demo=new Demo(); 13  System.out.println(demo.getClass().getName()); 14  } 15 }

  每一個類中所包含的getClass()方法將會返回一個Class類的實例(getClass方法被定義在Object類中,Object類是全部類的祖先),最經常使用的Class方法是getName。這個方法將返回類的名字,若是類在某個包中,包的名字也做爲類名的一部分返回。這也是得到Class對象的第一種方法。工具

運行結果:Reflect.Demo

還能夠調用Class類的靜態方法forName得到類名對應的Class對象:測試

String className = "java.util.Date"; Class cl = Class.forName(className);

  注意,className必定要包含包的路徑。這是得到Class對象的第二種方法。this

  這邊再強調一下,Class類對象保存了每一個對象所屬類的信息,下面會講到,經過Class類對象也能夠建立一個相應類(Class保存信息的類)的實例,即某個對象。

  得到Class類對象的第三種方法很簡單。若是T是任意的Java類型,T.class將表明匹配的對象。例如:

Class cl1 = Date.class; Class cl2 = int.class; Class cl3 = Double[].class;

  一個Class對象實際上表示的是一個類型,而這個類型未必必定是一種類。例如,int不是類,但int.class是一個Class類型的對象。

  經過newInstance()函數能夠快速地建立一個類的實例:

 1 package Reflect;  2  
 3 class Person{  4     
 5     public String getName() {  6         return name;  7  }  8     public void setName(String name) {  9         this.name = name; 10  } 11     public int getAge() { 12         return age; 13  } 14     public void setAge(int age) { 15         this.age = age; 16  } 17  @Override 18     public String toString(){ 19         return "["+this.name+"  "+this.age+"]"; 20  } 21 
22     private String name; 23     private int age; 24 } 25  
26 class hello{ 27     public static void main(String[] args) { 28         Class<?> demo=null; 29         try{ 30             demo=Class.forName("Reflect.Person"); 31         }catch (Exception e) { 32  e.printStackTrace(); 33  } 34         Person per=null; 35         try { 36             per=(Person)demo.newInstance(); 37         } catch (InstantiationException e) { 38  e.printStackTrace(); 39         } catch (IllegalAccessException e) { 40  e.printStackTrace(); 41  } 42         per.setName("Rollen"); 43         per.setAge(20); 44  System.out.println(per); 45  } 46 }
運行結果:[Rollen  20]

  注意,newInstance方法調用默認的構造器初始化新建立的對象,若是這個類沒有默認的構造器,就會拋出一個異常:

java.lang.InstantiationException: Reflect.Person at java.lang.Class.newInstance0(Class.java:340) at java.lang.Class.newInstance(Class.java:308) at Reflect.hello.main(hello.java:39) Exception in thread "main" java.lang.NullPointerException at Reflect.hello.main(hello.java:47)

利用反射分析類的能力

   在java.lang.reflect包中有三個類Field、Method和Constructor分別用於描述類的域、方法和構造器。這三個類都有一個叫作getName的方法,用來返回項目的名稱。Field類有一個getType方法,用來返回描述域所屬類型的Class對象。Method和Constructor類有可以報告參數類型的方法,Method類還有一個能夠報告返回類型的方法。這三個類還有一個叫作getModifiers的方法,它將返回一個整數,用不一樣的位開關描述public和static這樣的修飾符使用情況。

  Class類中的getFields、getMethods和getConstructors方法將分別返回類提供的public域、方法和構造器數組,其中包括超類的共有成員。Class類的getDeclareFields、getDeclareMethods和getDeclareConstructors方法將分別返回類中聲明的所有域、方法和構造器,包括私有成員和受保護成員,但不包括超類的成員

   下面看一個經過Class調用其餘類中的構造函數的例子:

 1 package Reflect;  2  
 3 import java.lang.reflect.Constructor;  4  
 5 class Person{  6     public Person() {  7  }  8     public Person(String name){  9         this.name=name; 10  } 11     public Person(int age){ 12         this.age=age; 13  } 14     public Person(String name, int age) { 15         this.age=age; 16         this.name=name; 17  } 18     public String getName() { 19         return name; 20  } 21     public int getAge() { 22         return age; 23  } 24  @Override 25     public String toString(){ 26         return "["+this.name+"  "+this.age+"]"; 27  } 28     private String name; 29     private int age; 30 } 31  
32 class hello{ 33     public static void main(String[] args) { 34         Class<?> demo=null; 35         try{ 36             demo=Class.forName("Reflect.Person"); //Demo中保存了Person類的信息,包括域、方法和構造器 37         }catch (Exception e) { 38  e.printStackTrace(); 39  } 40         Person per1=null; 41         Person per2=null; 42         Person per3=null; 43         Person per4=null; 44         //取得所有的構造函數
45         Constructor<?> cons[]=46         try{ 47             per1=(Person)cons[0].newInstance(); 48             per2=(Person)cons[1].newInstance("Rollen"); 49             per3=(Person)cons[2].newInstance(20); 50             per4=(Person)cons[3].newInstance("Rollen",20); 51         }catch(Exception e){ 52  e.printStackTrace(); 53  } 54  System.out.println(per1); 55  System.out.println(per2); 56  System.out.println(per3); 57  System.out.println(per4); 58  } 59 }
View Code

返回一個實現類的接口:

 1 package Reflect;
 2  
 3 interface China{
 4     public static final String name="Rollen";
 5     public static  int age=20;
 6     public void sayChina();
 7     public void sayHello(String name, int age);
 8 }
 9  
10 class Person implements China{
11     public Person() {
12          
13     }
14     public Person(String sex){
15         this.sex=sex;
16     }
17     public String getSex() {
18         return sex;
19     }
20     public void setSex(String sex) {
21         this.sex = sex;
22     }
23     @Override
24     public void sayChina(){
25         System.out.println("hello ,china");
26     }
27     @Override
28     public void sayHello(String name, int age){
29         System.out.println(name+"  "+age);
30     }
31     private String sex;
32 }
33  
34 class hello{
35     public static void main(String[] args) {
36         Class<?> demo=null;
37         try{
38             demo=Class.forName("Reflect.Person");
39         }catch (Exception e) {
40             e.printStackTrace();
41         }
42         //保存全部的接口
43         Class<?> intes[]=demo.getInterfaces();
44         for (int i = 0; i < intes.length; i++) {
45             System.out.println("實現的接口   "+intes[i].getName());
46         }
47     }
48 }
View Code

運行結果:

實現的接口   Reflect.China
View Code

取得其餘類中的父類:

 1 class hello{
 2     public static void main(String[] args) {
 3         Class<?> demo=null;
 4         try{
 5             demo=Class.forName("Reflect.Person");
 6         }catch (Exception e) {
 7             e.printStackTrace();
 8         }
 9         //取得父類
10         Class<?> temp=demo.getSuperclass();
11         System.out.println("繼承的父類爲:   "+temp.getName());
12     }
13 }
View Code

運行結果:

繼承的父類爲:   java.lang.Object
View Code

   下面經過一個完整的例子來理解Field、Method和Constructor對象的具體使用。這個程序將提醒用戶輸入類名,而後輸出類中全部的方法和構造器的簽名,以及所有的域名。假如用戶輸入

  java.lang.Double

程序將會輸出:

 1 public final class java.lang.Double extends java.lang.Number
 2 {
 3    public java.lang.Double(double);
 4    public java.lang.Double(java.lang.String);
 5 
 6    public boolean equals(java.lang.Object);
 7    public static java.lang.String toString(double);
 8    public java.lang.String toString();
 9    public int hashCode();
10    public static int hashCode(double);
11    public static double min(double, double);
12    public static double max(double, double);
13    public static native long doubleToRawLongBits(double);
14    public static long doubleToLongBits(double);
15    public static native double longBitsToDouble(long);
16    public volatile int compareTo(java.lang.Object);
17    public int compareTo(java.lang.Double);
18    public byte byteValue();
19    public short shortValue();
20    public int intValue();
21    public long longValue();
22    public float floatValue();
23    public double doubleValue();
24    public static java.lang.Double valueOf(java.lang.String);
25    public static java.lang.Double valueOf(double);
26    public static java.lang.String toHexString(double);
27    public static int compare(double, double);
28    public static boolean isNaN(double);
29    public boolean isNaN();
30    public static boolean isFinite(double);
31    public static boolean isInfinite(double);
32    public boolean isInfinite();
33    public static double sum(double, double);
34    public static double parseDouble(java.lang.String);
35 
36    public static final double POSITIVE_INFINITY;
37    public static final double NEGATIVE_INFINITY;
38    public static final double NaN;
39    public static final double MAX_VALUE;
40    public static final double MIN_NORMAL;
41    public static final double MIN_VALUE;
42    public static final int MAX_EXPONENT;
43    public static final int MIN_EXPONENT;
44    public static final int SIZE;
45    public static final int BYTES;
46    public static final java.lang.Class TYPE;
47    private final double value;
48    private static final long serialVersionUID;
49 }
View Code

下面是源碼:

 1 public class ReflectionTest  2 {  3    public static void main(String[] args)  4  {  5       // read class name from command line args or user input
 6  String name;  7       if (args.length > 0) name = args[0];  8       else
 9  {  10          Scanner in = new Scanner(System.in);  11          System.out.println("Enter class name (e.g. java.util.Date): ");  12          name = in.next();  13  }  14 
 15       try
 16  {  17          // print class name and superclass name (if != Object)
 18          Class cl = Class.forName(name);      19          Class supercl = 20          String modifiers = 21          if (modifiers.length() > 0) System.out.print(modifiers + " ");  22          System.out.print("class " + name);  23          if (supercl != null && supercl != Object.class) System.out.print(" extends "
 24                + supercl.getName());  25 
 26          System.out.print("\n{\n");  27  28  System.out.println();  29  30  System.out.println();  31  32          System.out.println("}");  33  }  34       catch (ClassNotFoundException e)  35  {  36  e.printStackTrace();  37  }  38       System.exit(0);  39  }  40 
 41    /**
 42  * Prints all constructors of a class  43  * @param cl a class  44     */
 45    public static void printConstructors(Class cl)  46  {  47       Constructor[] constructors = 48    
 49       for (Constructor c : constructors)  50  {  51          String name = 52          System.out.print("   ");  53          String modifiers = 54          if (modifiers.length() > 0) System.out.print(modifiers + " ");  55          System.out.print(name + "(");  56 
 57          // print parameter types
 58          Class[] paramTypes = 59          for (int j = 0; j < paramTypes.length; j++)  60  {  61             if (j > 0) System.out.print(", ");  62  System.out.print(paramTypes[j].getName());  63  }  64          System.out.println(");");  65  }  66  }  67 
 68    /**
 69  * Prints all methods of a class  70  * @param cl a class  71     */
 72    public static void printMethods(Class cl)  73  {  74       Method[] methods = 75 
 76       for (Method m : methods)  77  {  78          Class retType = 79          String name = 80 
 81          System.out.print("   ");  82          // print modifiers, return type and method name
 83          String modifiers = 84          if (modifiers.length() > 0) System.out.print(modifiers + " ");  85          System.out.print(retType.getName() + " " + name + "(");  86 
 87          // print parameter types
 88          Class[] paramTypes = 89          for (int j = 0; j < paramTypes.length; j++)  90  {  91             if (j > 0) System.out.print(", ");  92  System.out.print(paramTypes[j].getName());  93  }  94          System.out.println(");");  95  }  96  }  97 
 98    /**
 99  * Prints all fields of a class 100  * @param cl a class 101     */
102    public static void printFields(Class cl) 103  { 104       Field[] fields = cl.getDeclaredFields(); 105 
106       for (Field f : fields) 107  { 108          Class type = f.getType(); 109          String name = f.getName(); 110          System.out.print("   "); 111          String modifiers = Modifier.toString(f.getModifiers()); 112          if (modifiers.length() > 0) System.out.print(modifiers + " "); 113          System.out.println(type.getName() + " " + name + ";"); 114  } 115  } 116 }

在運行時分析對象

  在編譯程序時,若是知道想要查看的域名和類型,查看指定的域是一件很容易的事情,而利用反射機制能夠查看在編譯時還不清楚的對象域。咱們知道,經過調用getDeclareFields方法可得到Field類對象,前面的程序中咱們也能夠知道,經過Field對象的getType和getName方法能夠獲得域的類型和名字,而經過get方法咱們能夠獲得對應域的值。若是f是一個Field類型的對象,obj是某個包含f域的類的對象,f.get(obj)將返回一個對象,其值爲obj的f域的當前值。一樣,咱們能夠經過set函數在運行中設置某一個域的值。

 1 class hello {  2     public static void main(String[] args) throws Exception {  3         Class<?> demo = null;  4         Object obj = null;  5  
 6         demo = Class.forName("Reflect.Person");  7         obj = demo.newInstance();  8  
 9         Field field = demo.getDeclaredField("name"); 10         field.setAccessible(true); 11         field.set(obj, "Jack"); 12  System.out.println(field.get(obj)); 13  } 14 }

  這裏注意的一點是,反射機制的默認行爲是受限於Java的訪問控制的,name是一個私有的域,因此get和set訪問它將會拋出一個異常。若是一個java程序沒有受到安全管理器的控制,就能夠覆蓋訪問控制。爲了達到這個目的,須要調用Field、Method和Constructor對象的setAccessible方法。

 使用反射編寫泛型數組代碼

   在實際使用中咱們經常會用到Arrays類的copyOf方法實現擴展已經填滿的數組(動態的建立數組)。

Person[] a = new Person[100];
...
//arrays is full
a = Arrays.copyOf(a, 2*a.length);

  那麼如今問題來了,咱們不看源碼,考慮如何實現這樣一個通用的動態數組生成方法呢?咱們首先可能會想到把Person[]數組轉變爲Object[]數組,So咱們會這樣實現:

public static Object[] badCopyOf(Object[] a, int newLength) {
        Object[] newarray = new Object[newLength];
        System.arraycopy(a, 0, newarray, 0, newLength);
        return newarray;
    }

  然而,在實際使用獲得的結果數組時會出現問題,由於返回的結果爲Object[]類型,不能被轉換成其餘類型的數組。將一個Person[]臨時轉化成Object[]數組,而後再把它轉換回來是能夠的,但一個從開始就是Object[]的數組卻永遠不能轉換成Person[]數組。So,咱們須要知道原數組的類型才能建立與原數組類型相同的新數組。而在構建數組的過程當中很是重要的一個函數是java.lang.reflect包中Array類中的靜態方法newInstance,它可以構造新數組:

Object newArray = Array.newInstance(componentType, newLength);

這兩個參數,第一個是數組元素的類型,第二個是數組的長度。咱們能夠經過Array.getLength(a)來獲得數組長度。

那麼咱們如今主要的任務是得到數組元素類型,通常須要進行如下工做:

1)首先得到a數組的類對象。

2)確認它是一個數組。

3)使用Class類(只能定義表示數組的類對象)的getComponentType方法肯定數組對應的類型。

因而乎:

public static Object goodCopyOf(Object[] a, int newLength) {
        Class cl = a.getClass();
        if(!cl.isArray())
            return null;
        Class componentType = cl.getComponentType();
        int length = Array.getLength(a);
        Object newArray = Array.newInstance(componentType, length);
        System.arraycopy(a, 0, newArray, 0, Math.min(newLength, length));
        return newArray;
    }

簡單測試:

int[] a = {1, 2, 3, 4, 5};
a = (int[]) goodCopyOf(a, 10);

 最後咱們再附上jdk中copyOf的實現源碼:

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

 

 

 

 

 

參考資料:

http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html

《java核心技術 卷一》

相關文章
相關標籤/搜索