初探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核心技术 卷一》

相关文章
相关标签/搜索