操纵类的属性,有两种方法
反射
内省
面向对象的编程中,对于用户提交过来的数据,要封装成一个javaBean,也就是对象
其中Bean的属性不是由字段来决定的,而是由get和Set方法来决定的
public class Person { private String name ; private String password; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return null; } }
对于这个Bean,表面上看来只有三个属性,name,password,age
但是
@Test public void test() throws Exception { BeanInfo info = Introspector.getBeanInfo(Person.class); PropertyDescriptor[] pds = info.getPropertyDescriptors(); for(PropertyDescriptor pd:pds){ System.out.println(pd.getName()); } }
实际上从结果来看属性会有5个
age
class
name
password
sex
原因呢,就是属性石根据get和Set方法确定的,而所有的类都从Object继承而来,Object本身有一个属性
public final Class<?> getClass()
如果不需要父类的属性,有一个重载方法
public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
内省把Bean(相当于对象)的所有属性封装在一个BeanInfo对象里,拿到了BeanInfo就相当于拿到了Bean的所有属性
得到每一个属性的元数据(是个属性数组)
得到属性的读方法和写方法
然后就可以操作读方法或者写方法
@Test public void test() throws Exception { Person p = new Person(); PropertyDescriptor pd = new PropertyDescriptor("age", Person.class); //得到写方法 Method method = pd.getWriteMethod(); method.invoke(p,48); //得到读方法 method = pd.getReadMethod(); System.out.println(method.invoke(p, null)); }
获取属性类型
@Test public void test() throws Exception { Person p = new Person(); PropertyDescriptor pd = new PropertyDescriptor("age", Person.class); Class c = pd.getPropertyType(); System.out.println(c); }
这上面是Sun公司的一套用法,后来Apache觉得麻烦,就开发了自己的一套用法
当然,要在Eclipse里引入jar包
Commons-BeanUtils 提供对 Java 反射和自省API的包装
commons-logging 提供了对日志实现的包装,包括log4j,jdk1.4日志类
一般的项目使用logging 包作为日志工具,Log类地方法记录日志;
BeanUtils 作bean数据提取和注射
BeanUtils的使用依赖于日志文件这个知识库,所以两个都要导入
同时在编写的时候,要导入源码(通过F2)
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException{ Person p = new Person(); BeanUtils.setProperty(p, "age", “11”); System.out.println(p.getName()); }
Person类中的age类型为int,这里给传递一个String是可以的,因为
BeanUtils支持八种基本数据类型之间的相互转化
但是如果遇到复杂类型,要注册复杂类型转化器
比如DateTime类型
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException{ Person p = new Person(); BeanUtils.setProperty(p, "age", 11); ConvertUtils.register(new Converter() { @Override public Object convert(Class type, Object value) { if(value == null){ return null; } if( !(value instanceof String)){ throw new ConversionException("只支持String类型数据"); } String str = (String)value; if(str.trim().equals("")){ return null; } SimpleDateFormat dr = new SimpleDateFormat("yyyy-MM-dd"); try{ return dr.parse(str); }catch(ParseException e){ throw new RuntimeException(e); } } }, Date.class); BeanUtils.setProperty(p, "birthday", "1988-01-14"); System.out.println(p.getName()); }
看看register方法
public static void register(Converter converter, Class clazz) { ConvertUtilsBean.getInstance().register(converter, clazz); }
所以要实例化一个转化器
1. 对new Converter()这里用到了匿名类
2. 在使用数据value的时候,要检测,再使用
3. 抛异常:
try{ return dr.parse(str); }catch(ParseException e){ throw new RuntimeException(e); }
这一句话中,不能把异常直接抛给父类,因为这个匿名类是子类,是在覆盖父类的方法,子类方法不能抛比父类方法更多的异常,所以一定要抓catch
也不能使用
try{ return dr.parse(str); }catch(ParseException e){
e.printStackTrace(); }
这样的话,会把异常直接打印在控制台上,并没有通知上一层,上一层是不知道的,会继续执行
所有的程序都不是给自己调用的,是给上一层调用的,所以出了问题一定要告诉上一层,不能直接打印在控制台上。
可以使用map
同时ConvertUtils已经实现好了一些转换器,如日期转换器
public class Demo1 { public static void main(String[] args) throws IllegalAccessException, InvocationTargetException{ Map map = new HashMap(); map.put("name", "aaa"); map.put("password", "123"); map.put("age", "23"); map.put("birthday", "1989-01-14"); ConvertUtils.register(new DateLocaleConverter(), Date.class); Person bean = new Person(); BeanUtils.populate(bean, map); } }