导航

java 反射技术

Posted on 2015-01-04 11:12  寒宵飞飞  阅读(266)  评论(0编辑  收藏  举报

什么是反射?反射就是将字节码中的各种成分映射到相应的java类中来,java反射技术自JDK1.1以来就出现了,目前大多数流行的框架都采用了这种技术,可见其重要性,这篇文章将详细介绍我对java反射技术的一些研究.

代表字节码对象的Class

java中所有的类都有自己特有的一份字节码,当程序调用该类时,JVM便会将这份字节码装载到内存中来。在java中主要有三种方法来得到相应的字节码对象。

  1. 通过类的实例的getClass()方法获取,如 Class clazz=new Date().getClass()得到了Date类的字节码对象。
  2. 通过类的class属性获取。如Class clazz=String.class或者Class clazz=System.class.值得注意的是,java中八个主类型 (byte,char,short,int,long,float,double,boolean)和一个返回类型void都有class属性,并且都返 回自己的字节码对象。如int.class,boolean.class,float.class等。其中他们的包装类的TYPE属性也分别返回它们主类 型的那份字节码,如int.class==Integer.TYPE为true,void.class==Void.TYPE为true;
  3. 通过Class类的forName()方法获取,如Class clazz=Class.forName("java.lang.Math");

java中可以通过Class类的isPrimitive()方法判断当前的字节码是否为主类型,isEnum()判断字节码是否为枚举类。 isArray()判断字节码是否为数组类型。某个类和它所组成的数组分别持有的字节码也不相同,例如int.class==int[].class这种 写法在编译期就会被编译器阻止,因为编译器发现两边参与比较的对象不在一个继承树分支上。只有相同类型和相同维度的数组才会共用一个字节码对象,如int a1[]=new int[1], a2[]=new int[5];int a3[][]=new int[2][5];a1.getClass()==a2.getClass()为true,a2.getClass()==a3.getClass() 为false;

代表构建器的Constructor

java中用getConstructor()和getConstructors()方法返回某一个public的构建器和所有public访问级 别的构建器,而getDeclaredConstructor()和getDeclaredConstructors()返回所有构建器中的某一个构建器 和所有的构建器。Constructor可以通过newInstance()方法创建一个该类的实例。如下例所示

Class clazz=String.class;

Constructor cs=clazz.getConstructor(StringBuffer.class);

String s=(String)cs.newInstance(new StringBuffer("abc"));

上面的代码相当于

String s=new String(new StringBuffer("abc"));

又如下例所示

String obj=String.class.getConstructor().newInstance();

上面的代码相当于

String obj=new String();

如果要得到所有的构建器可以按下面的方法做。

Constructor[] allCons=String.class.getDeclaredConstructors();

for(Construcotr con:allCons)

          System.out.println(con.getName());

除了Constructor类中提供了newInstance()方法,Class类中也有一个类似的newInstance()方法,因为我们可以直接在得到字节码对象时便可生成该字节码的对象了。

ArrayList al=(ArrayList)ArrayList.class.newInstance();事实上得到构建器对象的那一步已经被封装在Class的newInstance()方法中。

public Object newInstance()

{

          this.getConstructor().newInstance();

}

代表字段的Field

Class类中提供了getField()和getFields()得到public级别的字段中的某一个和所有public级别的字段,getDeclaredField()和getDeclaredFields()得到所有字段中的某一个和所有的字段。

如果某个字段是数组类型的,那么要对它进行反射还需要借助java.lang.reflect.Array来进行操作。有些时候得到的字段为 private访问级别的,如果这个时候想要读取和设值该字段一般情况下会报运行期异常,但是java反射技术也并不是对此无能为力,运用反射可以绕过编 译器的某些限制,来进行“暴力反射”。此时只需设置Field的setAccessible()为true就可以了。

package net.csdn.blog;
public class ReflectClass {
    public int data[]=new int[]{1,3,5,7,9};//数组类型的字段
    private String name="字段一";//private级别的字段
    float f=2.6f;
}

下面对ReflectClass类中的字段进行反射操作

package net.csdn.blog;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
public class ReflectClassTest {
    public static void main(String[] args) {
        ReflectClass rc=new ReflectClass();
        try {
            HandleReflect(rc);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    
    private static void HandleReflect(Object obj) throws IllegalArgumentException, IllegalAccessException{
        Class clazz=obj.getClass();
        Field fields[]=clazz.getDeclaredFields();
        for(Field field:fields){
            if(!field.isAccessible())
                field.setAccessible(true);
            Object fieldObj=field.get(obj);//得到字段的值
            if(field.getType().isArray()){//处理字段为数组时候的情况
                int len=Array.getLength(fieldObj);//得到数组的长度
                for(int i=0;i<len;i++){
                    System.out.println(Array.get(fieldObj, i));//打印出数组中的每个元素
                }
            }else{
                if(field.getType()==float.class)
                    field.set(obj, 5.2f);//设值字段的值
                Object o=field.get(obj);
                System.out.println(o);
            }
        }
    }
}

 

代表成员方法的Method

Class类中同样提供了getMethod(),getMethods()用来得到得到public级别的方法和所有public的方法,以及 getDeclaredMethod()和getDeclaredMethods()方法用来得到任何访问级别的某一个方法和所有任何访问级别的方法.

package net.csdn.blog;
public class ReflectMethod {
    public void sayHello(){
        System.out.println("hello world");
    }
    
    public void printArray(Object obj[]){
        for(Object o:obj)
            System.out.println(o);
    }
    
    public String getReverseMsg(String msg){
        return new StringBuffer(msg).reverse().toString();
    }
}

 对ReflectMethod类进行反射

package  net.csdn.blog;  
  
import  java.lang.reflect.InvocationTargetException;  
import  java.lang.reflect.Method;  
  
public   class  ReflectMethodTest {  
    public static void main(String argsp[]){  
        ReflectMethod rm=new  ReflectMethod();  
        try  {  
            reflectMethod(rm);  
        } catch  (Exception e) {  
            e.printStackTrace();  
        }    
    }  
      
    private static void reflectMethod(Object obj)  throws  SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{  
        Class clazz=obj.getClass();  
        Method method=clazz.getDeclaredMethod("sayHello" , null ); //得到sayHello()方法   
        method.invoke(obj, null ); //执行这个方法   
        method=clazz.getDeclaredMethod("printArray" , Object[]. class );  
        method.invoke(obj, (Object)new  Object[]{ "this is a joke" , "csdn blog" }); //此处应该作转型,以告诉编绎器不按JDK1.4的作法解析   
        method=clazz.getDeclaredMethod("getReverseMsg" , String. class );  
        String msg=(String)method.invoke(obj, "abcdefg" );  
        System.out.println(msg);  
    }  
}  

 

对内部类的反射

网上的资料对于如何运用反射将内部类映射出来的资料非常之少,为此我特意花了一个多小时研究了一会儿,下面把研究的结果写到下面。

Class类本身还提供对于获取内部类字节码的方法,分别为getClasses和getDeclaredClasses(),其中 getClasses()只能得到访问级别为public的内部类,而getDeclaredClasses()则能得到所有声明了的内部类。

由于内部类可以分为实例内部类,静态内部类,匿名内部类,前面提到的getClasses()和getDeclaredClasses()目前还都 只能得到实例内部类和静态内部类,对于后面两种情况却无能为力,为此还需要采取一些特殊的手段才能针对匿名内部类进行反射操作。 

package net.csdn.blog;
public class ReflectInnerClass {
    
    public Runnable ta=new Runnable(){
        public void run(){
            System.out.println("匿名内部类中的方法被执行了");
        }
    };
    
    private class Inner2{
        public Inner2(){
            System.out.println("Inner2类被实例化了");
        }
    }
    
    class Inner3{
        public Inner3(){
            System.out.println("Inner2类被实例化了");
        }
    }    
    public class Inner1{
        public Inner1(){
            System.out.println("Inner1类被实例化了");
        }
    }
}

下面对内部类进行反射

package  net.csdn.blog;  
  
import  java.lang.reflect.InvocationTargetException;  
import  java.lang.reflect.Modifier;  
  
public   class  ReflectInnerClassTest {  
  
    public   static   void  main(String args[]){  
        ReflectInnerClass ric=new  ReflectInnerClass();  
        try  {  
            reflectInnerClass(ric);  
        } catch  (Exception e) {  
            e.printStackTrace();  
        }   
    }  
  
    private   static   void  reflectInnerClass(ReflectInnerClass ric)  throws  InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, NoSuchFieldException {  
        Class clazz=ric.getClass();  
        Class classes[]=clazz.getDeclaredClasses();  
        for (Class c:classes){ //对成员内部类进行反射   
            int  i=c.getModifiers();  
            String s=Modifier.toString(i);  
            if (s.contains( "static" )) //静态内部类的处理   
                 c.getConstructor().newInstance();  
            else //实例内部类的处理   
                 c.getConstructor(ric.getClass()).newInstance(ric);  
        }  
        //由于匿名内部类没有构建器,因此无法创建实例,也无法直接访问其中的方法,但可以通过下面的方式巧秒的执行其中的方法或成员变量。   
        Runnable r=(Runnable)(clazz.getField("ta" ).get(ric));  
        r.run();  
          
    }  
}  

 

 

本文转载自:http://blog.csdn.net/helloapps/archive/2010/07/06/5716604.aspx