基础加强_JavaBean,反射和内省,BeanUtils工具

来源:互联网 发布:买家如何注册淘宝客 编辑:程序博客网 时间:2024/06/04 19:16

  ------- android培训、java培训、期待与您交流! ---------- 

反射的应用

反射就是把java类中的各种成分映射成相应的java类。

首先讨论下昨天遇到的问题,

/* * 对两个使用了泛型的集合操作,出现下面问题 */public static void main(String[] args) throws Exception {//通过反射向Integer集合中添加String可以正常取出打印  ArrayList<Integer> arr1 = new ArrayList<Integer>();  arr1.getClass().getMethod("add", Object.class).invoke(arr1, "abc");  System.out.println(arr1.get(0));  //通过反射向String集合中添加Integer,在打印时却出现类型转换异常  //这是什么原因?  ArrayList<String> arr2 = new ArrayList<String>();  arr2.getClass().getMethod("add", Object.class).invoke(arr2, 2);  System.out.println(arr2.get(0));}
泛型信息在生成class文件时被擦除,通过反射就可以操作原来不允许的类型。表面看来是没有问题的。

但是我们却忽略了编译时根据泛型String会静态绑定println(String str)方法,但是在运行时却取出了Integer类型,自然会出现类型转换异常。

如果泛型不是String,编译时会调用println(Object obj)方法,运行时再根据obj的实际类型动态绑定调用相应的toString方法,动态绑定实际上是运行时绑定,在运行时根据参数类型决定调用合适的方法。但是当在编译期可以明确的,就会静态绑定某个方法。

下面再来学习一些反射的应用:

Class类中有一个方法:
class.isPrimitive()判断Class对象是不是基本数据类型
九个预定义Class实例对象:8中基本数据类型和void.class。这些类对象由 Java 虚拟机创建
int.class == Integer.class;//false
Int.class == Integer.TYPE//true

JDK升级可变参数和数组的问题

问题产生:用反射的方式根据用户提供的类名,去执行该类中的main方法。
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
按jdk1.5的语法,整个数组被当成是一个参数,
按jdk1.4的语法,数组中的每个元素对应一个参数,
当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数时,使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题
//TestArguments.main(new String[]{"111","222","333"});String startingClassName = args[0];Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);// invoke(obj, obj...args)invoke方法在JDK1.5的参数变为可变参数//下面是两种可行的解决方法,将数组包装数组的元素或者明确字符串数组参数是一个对象//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});mainMethod.invoke(null, (Object)new String[]{"111","222","333"});

反射的应用--->实现框架功能

框架和工具类的区别,工具类被用户的类调用,框架则是调用用户的类。通过代码来学习:

获取配置文件:

public static void main(String[] args) throws Exception{Properties props = new Properties();//使用相对路径后去配置文件,移植性不好//InputStream ips = new FileInputStream("config.properties");/*一个类加载器能加载.class文件,那它当然也能加载classpath环境下的其他文件,注意:直接使用类加载器时,文件名不能以/打头。*///InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/javaenhance/config.properties");//Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");//如果使用class类加载器加载文件时,文件名以/开头,表示绝对路径,从classpath下寻找文件InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/javaenhance/config.properties");props.load(ips);Ips.close();String className = props.getProperty("className");Class clazz = Class.forName(className);//已经知道配置文件使用的是Collection的子类,通过反射创建对象Collection collection = (Collection)clazz.newInstance();//Collection collection = new ArrayList();ReflectPoint pt1 = new ReflectPoint(3,3);ReflectPoint pt2 = new ReflectPoint(5,5);ReflectPoint pt3 = new ReflectPoint(3,3);collection.add(pt1);collection.add(pt2);collection.add(pt3);collection.add(pt1);System.out.println(collection.size());}

JavaBean

JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。
如果方法名为setId,中文意思即为设置id,
如果方法名为getId,中文意思即为获取id,
去掉前缀,剩余部分就是属性名。

java.Beans包 API用于对JavaBean进行操作。

PropertyDescriptor 类,用于对JavaBean属性进行操作
public PropertyDescriptor(String propertyName,Class<?> beanClass) 构造方法
Method getReadMethod() 获得读取属性值的方法。 
Method getWriteMethod() 获得写入属性值的方法。 
BeanInfo 接口---->SimpleBeanInfo 空语句实现类
PropertyDescriptor[] getPropertyDescriptors() 获得 beans PropertyDescriptor。 
IntroSpector 类 
static BeanInfo getBeanInfo(Class<?> beanClass) 在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。 

通过代码学习内省

public class IntroSpectorTest {/** * @param args */public static void main(String[] args) throws Exception {// 创建一个JavaBean对象pt1ReflectPoint pt1 = new ReflectPoint(3,5);String propertyName = "x";//"x"-->"X"-->"getX"-->MethodGetX-->//抽取获取属性值的方法,通过内省返回pt1对象的propertyName属性的值Object retVal = getProperty(pt1, propertyName);System.out.println(retVal);Object value = 7;//抽取设置属性值的方法,通过内省设置pt1对象的propertyName属性的值为valuesetProperties(pt1, propertyName, value);//BeanUtils工具包,BeanUtils类get属性返回String字符串System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());//BeanUtils类set属性时可以接受任意类型的对象,通常使用字符串BeanUtils.setProperty(pt1, "x", "9");System.out.println(pt1.getX());//java7的新特性,Map的初始化方式//Map map = {name:"zxx",age:18};//BeanUtils.setProperty(map, "name", "lhm");//在JavaBean类ReflectPoint中定义一个Date字段,可以直接通过字段设置Date的time属性BeanUtils.setProperty(pt1, "birthday.time", "111");System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));//PropertyUtils的get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型PropertyUtils.setProperty(pt1, "x", 9);System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());}private static void setProperties(Object pt1, String propertyName,Object value) throws Exception {PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());Method methodSetX = pd2.getWriteMethod();methodSetX.invoke(pt1,value);}private static Object getProperty(Object pt1, String propertyName)throws Exception {//使用PropertyDescriptor获取属性的方法/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());Method methodGetX = pd.getReadMethod();Object retVal = methodGetX.invoke(pt1);*///BeanInfo接口,通过内省获取JavaBean的信息BeanInfo beanInfo =  Introspector.getBeanInfo(pt1.getClass());PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();Object retVal = null;for(PropertyDescriptor pd : pds){if(pd.getName().equals(propertyName)){Method methodGetX = pd.getReadMethod();retVal = methodGetX.invoke(pt1);break;}}return retVal;}}

apache  BeanUtils工具包   需要logging包
添加jar包,设置buildpath
采用BeanUtils去获取带有抽象方法的枚举类的成员对象的属性时,会出现错误,要自己用内省加暴力反射方式才可以获取。主要原因是枚举类的抽象子类不是public类型的。


 ------- android培训、java培训、期待与您交流! ----------

原创粉丝点击