首页 > 代码库 > 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 反射