首页 > 代码库 > java 反射

java 反射

 

反射:就是把Java类中的各种成分映射成一个个的Java对象。例如,一个类有:成员变量,成员方法,包等信息,利用反射技术可以对一个类进行解剖,把各个部分映射成一个个对象。

 


 

1. 先得到类的字节码对象:Class cl = Class.forName("类的全名");

                            或者:Class cl = 类名.class;

                            或者:Class cl = 类的对象.getClass();

2. 解剖类:

   Class对象提供了如下常用方法:

  •    public Constructor getConstructor(Class<?>...parameterTypes)
  •    public Method getMethod(String name, Class<?>...parameterTypes)
  •    public Field getField(String name)
  •    public Constructor getDeclaredConstructor(Class<?>...parameterTypes)
  •    public Method getDeclaredMethod(String name, Class<?>...parameterTypes)
  •    public Field getDeclaredField(String name)

 这些方法分别用于从类中解剖出构造函数、方法和成员变量(属性)。解剖出的成员分别使用Constructor、Method、Field对象表示。

先写一个简单的类

 1 package javatext; 2  3 import java.util.Date; 4  5 public class Student { 6     public String str = "hello"; 7  8     private int age = 18; 9 10     public static Date time;11     12     public Student(){}13 14     public Student(String name){15         System.out.println("姓名:" + name);16     }17 18     public Student(String name, int age){19         System.out.println(name + " " + age);20     }21 22     private Student(int age){23         System.out.println("年龄:" + age);24     }25 26     public void m1(){27         System.out.println("m1");28     }29 30     public void m2(String name){31         System.out.println("m2 " + name);32     }33 34     public String m3(String name, int age){35         System.out.println("m3 " + name + " " + age);36         return "m3:OK";37     }38 39     private void m4(int age){40         System.out.println("m4 " + age);41     }42 43     public static void m5(){44         System.out.println("m5");45     }46 47     private static void m6(String[] str){48         System.out.println(str.length);49     }50 }

 

反射

 1 package javatext; 2  3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Field; 5 import java.lang.reflect.Method; 6 import java.util.Date; 7  8 public class _1{ 9     10     public static void main(String[] args)throws Exception{11         Class cl = Class.forName("javatext.Student"); //Student类在内存中的字节码对象12         13         Student s = (Student)cl.newInstance();  //可以直接用类的字节码对象,建立该类新的实例,调用的是默认的构造方法14         System.out.println(s.str);15         16         Constructor c = cl.getConstructor(String.class);  //获取相应的构造方法,只能是public的方法17         c.newInstance("Chris"); //创建新实例18         19         Constructor cc = cl.getDeclaredConstructor(int.class); //获取相应的构造方法(所有,包括私有构造方法)20         cc.setAccessible(true);21         cc.newInstance(22);22         23         Constructor[] ccc = cl.getDeclaredConstructors();  //获取所有的构造方法(包括私有)24         System.out.println(ccc.length);25         26         Method m1 = cl.getMethod("m1", null); //获取名为m1,参数为null的方法27         m1.invoke(s, null);  //调用该方法,s为该类的一个实例,null为该方法的参数28         29         Method m2 = cl.getMethod("m2", String.class);30         m2.invoke(s, "Chris");31         32         Method m3 = cl.getMethod("m3", String.class, int.class);33         String str = (String)m3.invoke(s, "Chris", 22);34         System.out.println(str);35         36         Method m4 = cl.getDeclaredMethod("m4", int.class);37         m4.setAccessible(true);38         m4.invoke(s, 22);39         40         Method m5 = cl.getMethod("m5", null);41         m5.invoke(null, null);  //对于静态方法,对象可以为s,也可以为null42         43         Method m6 = cl.getDeclaredMethod("m6", String[].class);44         m6.setAccessible(true);45         m6.invoke(null, (Object)new String[]{"a", "b", "c"}); //参数中带有数组的,要将其转换成一个对象46         //注:由于要向下兼容,jdk1.5及以上版本要兼容jdk1.4。在1.4中,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给47         //invoke方法时,会出现参数数量错误的问题。48         //解决办法:invoke(null, new Object[]{new String[]{"xxx", "xx"}});49         //  或者:  invoke(null, (Object)new String[]{"XX", "XXX"});50         //这样编译器会做特殊处理,编译时不把参数当作数组看待,也就不会将数组打散成若干参数了。51         52         Field f1 = cl.getField("str");53         String string = (String)f1.get(s);54         System.out.println(string);55         56         f1.set(s, "hehe"); //更改类中的str字段57         System.out.println(s.str);58         59         Field f2 = cl.getDeclaredField("age");60         f2.setAccessible(true);61         int age = (Integer)f2.get(s);62         System.out.println(age);63         64         f2.set(s, 80);65         age = (Integer)f2.get(s);66         System.out.println(age);67         68         Field f3 = cl.getField("time");69         f3.set(null, new Date());70         System.out.println(Student.time);71     }    72 } 

 结果:

hello姓名:Chris年龄:224m1m2 Chrism3 Chris 22m3:OKm4 22m53hellohehe1880Sat Sep 17 20:38:34 GMT+08:00 2016

 


 

内省:开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以Sun公司开发了一套API,专门用于操作java对象的属性。

通过内省技术访问(java.beans包提供了内省的API)JavaBean的两种方式。

1. 通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后通过反射机制来调用这些方法。

2. 通过PropertyDescriptor类操作Bean的属性。

写一个简单类:

 1 package javatext; 2  3 public class Person { 4     private String name = "Chris"; 5     private int age; 6     public String getName() { 7         return name; 8     } 9     public void setName(String name) {10         this.name = name;11     }12     public int getAge() {13         return age;14     }15     public void setAge(int age) {16         this.age = age;17     }18 }

内省:

package javatext;import java.beans.BeanInfo;import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.lang.reflect.Method;public class _1{    public static void main(String[] args)throws Exception{        Person p = new Person();                //获取所有属性        BeanInfo bi = Introspector.getBeanInfo(Person.class);//获取Person类中的属性,被封装到了BeanInfo中,若想获得(不包括Object类中的)属性,则参数为(Person.class, Object.class)        PropertyDescriptor[] pds = bi.getPropertyDescriptors();//获取类中的所有的属性描述器        System.out.println(pds.length); //显示3个属性,以为Object类中有getClass()方法,也算作1个属性        for(PropertyDescriptor pd : pds){            System.out.println(pd.getName()); //打印属性名称,即去掉get、set的名称        }                //操作Bean的指定属性        PropertyDescriptor pd = new PropertyDescriptor("name", Person.class);        Method m = pd.getReadMethod(); //获取getName()方法,即读方法        String value = http://www.mamicode.com/(String)m.invoke(p, null);        System.out.println(value);                Method m1 = pd.getWriteMethod(); //获取setName()方法,即写方法        m1.invoke(p, "Lily");        System.out.println(p.getName());    }    } 

 结果:

3ageclassnameChrisLily

 

内省--beanutils工具包:

Beanutils是Apache开发的一套快速操作JavaBean getter/setter方法的API,目前比较流行。

准备包:commons-beanutils.jar, commons-logging.jar

语法:

 (1)设置:BeanUtils.setProperty(Object bean, String propertyName, String propertyValue);

 (2)获取:BeanUtils.getProperty(Object bean, String PropertyName);

注:BeanUtils可以进行类型的自动转换,但仅限基本类型。

继续用上面的Person类

 1 package javatext; 2  3 import java.text.DateFormat; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6  7 import org.apache.commons.beanutils.BeanUtils; 8 import org.apache.commons.beanutils.ConvertUtils; 9 import org.apache.commons.beanutils.Converter;10 import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;11 12 public class _1{13     public static void main(String[] args)throws Exception{14         Person p = new Person();15         String str = BeanUtils.getProperty(p, "name"); //调用getName()方法16         System.out.println(str);17         18         BeanUtils.setProperty(p, "name", "Lily"); //设置值19         System.out.println(p.getName());20         21         //非基本类型的属性设置22         //给BeanUtils注册类型转换器23         ConvertUtils.register(new Converter(){24             //type:目标类型25             //value:当前传入的值26             public Object convert(Class type, Object value){27 //                if(type.equals(Date.class)){28 //                    //字符串转换为Date29 //                }else{30 //                    //Date转换为字符串31 //                }32                 DateFormat df = new SimpleDateFormat("yyyy-MM-dd");33                 if(value instanceof String){34                     //字符串转换为Date35                     String v = (String)value;36                     try{37                         Date d = df.parse(v);38                         return d;39                     }catch(Exception e){40                         throw new RuntimeException(e);41                     }42                 }else{43                     //Date转换为字符串44                     Date d = (Date)value;45                     return df.format(d);46                 }47             }48         }, Date.class);49         BeanUtils.setProperty(p, "birthday", "2016-09-17");50         System.out.println(p.getBirthday());

 

结果:

ChrisLilySat Sep 17 00:00:00 GMT+08:00 2016

 

或者

1 ConvertUtils.register(new DateLocaleConverter(), Date.class);2 BeanUtils.setProperty(p, "birthday", "1999-09-09");3 System.out.println(p.getBirthday());

注:其中的DateLocalConverter()就是对上面代码的封装。

 

BeanUtils将Map属性自动放到Bean中

注:可以操作的原则是,Map的key必须要与Bean的属性一致。

 

1 Map m = new HashMap();2 m.put("name", "Chris");3 Person p = new Person();4 BeanUtils.populate(p, m);5 System.out.println(person);

 

java 反射