JavaBean与Introspector
反射和内省操作很多时候都是在以后要做框架的时候作用非常大。
现在你学的是面向对象编程,即:你所写代码都能够找到对应的类或接口,找到具体的方法写出对应的代码。
但是以后学面向抽象编程的时候,即:我们所写的代码完全抽象,比如我们写的框架所要面向的类或方法目前并没有的,而是以后别人用我们的框架写出来的类。但是我们又怎么调用去他们的类get/set方法呢?所以这个时候要用到反射和内省进行抽象编程。
/* JavaBean与内省(Introspector): 1.JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法 主要用于访问私有字段,且方法名符合某种命名规则. 2.如果在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例 通常称之为值对象(Value Object).这些信息在类中用私有字段来存储,如果读取货设置这些字段的值 则需要通过一些相应的方法来访问,这些方法该如何命名? JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量.如果方法 名为setId,意为设置id,至于你存到哪个变量上,用管吗?getId意为获取id,至于从哪个变量上得到的,用管吗? 去掉set前缀后,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小写. setId属性名->id isLast属性名->last setCPU属性名->CPU 总而言之:一个类被当做JavaBean使用时,JavaBean的属性是根据方法名推断出来的 它根本看不到java类内部的成员变量 一个符合JavaBean特点的类可以被当做普通类一样进行使用,但把它当做JavaBean用肯定需要带来一些额外的好处 好处: 1.在JavaEE开发中,经常使用JavaBean.很多环境要求按JavaBean方式进行操作 2.JDK提供了对JavaBean进行操作的一些API,这套API就称为内省. */
//eclipse 4.3下自动生成getter和setter方法 //JavaBean的属性是由getter或setter方法决定 //只要含有其中的一个方法就是JavaBean的属性 //User类继承Object的方法getClass,因此还有一个属性为class class User{ private String userName;//字段 private String uName;//字段 private String CPU; private String controlProcessUnited; private int x; public int getX() { return x; } public void setX(int x) { this.x = x; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getuName() { return uName; } public void setuName(String uName) { this.uName = uName; } public String getCPU() { return CPU; } public void setCPU(String cPU) { CPU = cPU; } public String getControlProcessUnited() { return controlProcessUnited; } public void setControlProcessUnited(String controlProcessUnited) { this.controlProcessUnited = controlProcessUnited; } }
对JavaBean内省操作:
用一个测试类:Car
package com.itheima.day2; import java.util.Date; public class Car { private String color; private int number;//轮胎个数 private Date birthday=new Date();//为了测试 设置级联属性 public Car(String color, int number) { super(); this.color = color; this.number = number; } public String getColor(){ return color; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public void setColor(String color) { this.color = color; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } }
package com.itheima.day2; import java.lang.reflect.Method; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; public class IntrospectorDemo { public static void main(String[] args)throws Exception{ // TODO 自动生成的方法存根 //方式一:使用反射操作JavaBean //获取颜色->color->color有一个单词第二个字母小写->推断出方法名getColor Car car=new Car("红色",4); Method method=car.getClass().getMethod("getColor"); Object retVal=method.invoke(car); System.out.println(retVal);//"红色" //方式二:使用内省操作JavaBean->不用再推断方法名 PropertyDescriptor pd=new PropertyDescriptor("color",car.getClass());//第一个参数属性名,第二个参数把哪一个类当成JavaBean类 method=pd.getReadMethod();//将获取到的getColor方法封装成Method对象 retVal=method.invoke(car); System.out.println(retVal);//"红色" method=pd.getWriteMethod();//将获取到的setColor方法封装成Method对象 method.invoke(car,"黑色"); System.out.println(car.getColor());//验证是否改掉//"黑色" //测试封装后的方法 System.out.println(getProperty("number",car));//4 setProperty("number",car,10); System.out.println(car.getNumber());//10 } //对上面的操作步骤进行封装提高复用性 public static Object getProperty(String property,Object obj) throws Exception{//获取指定对象的属性值 PropertyDescriptor pd=new PropertyDescriptor(property,obj.getClass()); Method method=pd.getReadMethod(); return method.invoke(obj); /*//方法二: BeanInfo bi=Introspector.getBeanInfo(obj.getClass());//在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件,描述目标 bean 的 BeanInfo 对象。 PropertyDescriptor[] pdArrs=bi.getPropertyDescriptors();//获取到该JavaBean中所有属性信息,BeanInfo没有获取单个属性的方法 for(PropertyDescriptor pdArr : pdArrs) if(pdArr.getName().equals(property)){//获取到属性描述的属性名称(getName),遍历 Method method=pdArr.getReadMethod(); return method.invoke(obj); } return null;*/ } public static void setProperty(String Property,Object obj,Object value)throws Exception{ PropertyDescriptor pd=new PropertyDescriptor(Property,obj.getClass()); Method method=pd.getWriteMethod(); method.invoke(obj,value); } }
使用开源工具BeanUtils来操作JavaBean:
/* 使用开源BeanUtils来更方便操作JavaBean 1.从http://commons.apache.org/beanutils/下载commons-beanutils-1.8.3-bin.zip 2.将其中的.jar导入工程 3.不采用BuildPath->添加外部归档方式导入工程,这样做.jar并不在工程目录下(例如在d:\下) 一旦将工程拷贝到其它机器下,还需要把该.jar拷贝到d:\下,不然用不了 解决方式:在工程下新建lib文件夹->将.jar拷贝到lib下->Build Path */
/* public static String getProperty(Object bean,String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException public static void setProperty(Object bean,String name,Object value) throws IllegalAccessException, InvocationTargetException*/ public class BeanUtilsDemo { public static void main(String[] args)throws Exception { // TODO 自动生成的方法存根 Car car=new Car("black",20); //使用BeanUtils来set/get属性 String number=BeanUtils.getProperty(car,"number");//获取car对象color属性的值,注意返回值固定为String BeanUtils.setProperty(car,"color","red");//设置car对象number属性的值为4 BeanUtils.setProperty(car,"number","2");//传入2(自动装箱,内部需要拆箱)或"2"均可,"2"内部涉及到从String->int转换 System.out.println(car.getColor()+" "+car.getNumber()+"\n"); //级联属性设置与获取 BeanUtils.setProperty(car,"birthday.time","1000");//在Date类中一个public void setTime(long time)方法->属性time //通俗例子:设置person.head.face.eye.color System.out.println(BeanUtils.getProperty(car,"birthday.time")+"\n"); //JavaBean与Map相互转换 Map map=BeanUtils.describe(car);//会将JavaBean中 所有属性值 存入到Map中,前提是该属性值有对应get方法 System.out.println(map);//里面还有一个属性为字节码文件对象,也就是指定的JavaBean类 map=new HashMap(); map.put("color","blue"); map.put("number",2); map.put("birthday.time",200); BeanUtils.populate(car, map);//填充:将map中key对应javabean中的property,将其value赋给property System.out.println(car.getColor()+" "+car.getNumber()); System.out.println(BeanUtils.getProperty(car,"birthday.time")+"\n"); //PropertiesUtils工具类 PropertyUtils.setProperty(car,"number",13);//属性值的类型必须和Car中number类型一致均为int Object obj=PropertyUtils.getProperty(car, "number"); System.out.println(obj+" "+obj.getClass().getName());//说明上面的getProperty以Integer类型返回 } } /* 不导入commons-logging-1.1.3.jar之前的运行结果: Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory 相当于有了电视机,但遥控器是第三方->电视机没法用 需要导入遥控器->下载commons-logging(通用日志)并build path*/
package cn.itcast.feature; 2: 3: import java.lang.reflect.InvocationTargetException; 4: import java.text.ParseException; 5: import java.text.SimpleDateFormat; 6: import java.util.Date; 7: 8: import org.apache.commons.beanutils.BeanUtils; 9: import org.apache.commons.beanutils.ConversionException; 10: import org.apache.commons.beanutils.ConvertUtils; 11: import org.apache.commons.beanutils.Converter; 12: import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; 13: import org.junit.Test; 14: 15: /* 16: 虽然使用BeanUtils简化了代码量 17: 但是BeanUtils默认只能帮你进行八大基本类型间的转换或 18: String到八大基本类型转换. 19: 如果我需要用到String->Date的转换,需要我们自定义转换器 20: */ 21: public class CustomConvert { 22: private Person p=new Person(); 23: @Test 24: public void convertTest_1() throws IllegalAccessException, InvocationTargetException{ 25: BeanUtils.setProperty(p,"birthday","1980-3-1"); 26: System.out.println(p.getBirthday()); 27: } 28: JUnit测试: 2.这时候我们需要自定义转换器完成String->Date 1: @Test 2: public void convertTest_2() throws IllegalAccessException, InvocationTargetException{ 3: ConvertUtils.register(//注册一个自定义转换器完成String->Date转换 4: new Converter() { 5: @Override 6: public Object convert(Class type, Object value) { 7: // TODO Auto-generated method stub 8: if (value == null) 9: return null; 10: if (!(value instanceof String)) 11: throw new ConversionException("不支持从" 12: + value.getClass().getName() + "到" 13: + type.getName() + "的转换"); 14: String valueStr = (String) value; 15: if ((valueStr = valueStr.trim()).equals(""))// 去除字符串首尾的空格,同时判断是否是空串 16: return null; 17: 18: // 开始转换工作,以上做的判断完全为了程序的健壮性 19: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 20: try { 21: return sdf.parse(valueStr);// 该方法有异常声明但是我不能在convert方法上进行声明,只能try...catch 22: // 因为该匿名子类复写的Converter中的convert方法上没有任何异常声明 23: } catch (ParseException e) { 24: // TODO Auto-generated catch block 25: throw new RuntimeException(e); 26: } 27: 28: } 29: 30: }, Date.class); 31: BeanUtils.setProperty(p,"birthday","1980-3-1"); 32: System.out.println(p.getBirthday()); 33: }
3.如果把BeanUtils.setProperty(p,"birthday","1980-3-1");换成BeanUtils.setProperty("birthday",1980-3-1);不能通过测试:
4.使用已提供的Sting->Date的转换器:
@Test 2: public void convertTest_3() throws IllegalAccessException, InvocationTargetException{ 3: //使用API提供的转换器:DateLocaleConverter 4: ConvertUtils.register(new DateLocaleConverter(),Date.class); 5: BeanUtils.setProperty(p,"birthday","1980-3-1"); 6: System.out.println(p.getBirthday()); 7: 8: //但是该转换器有Bug,那就是传入一个空串 9: BeanUtils.setProperty(p,"birthday",""); 10: System.out.println(p.getBirthday()); 11: }
5.最后来看下throw new RuntimeException(e);打印的异常信息:
1: @Test 2: //throw new RuntimeException(Throwable cause); 3: /*用指定的原因和详细消息 (cause==null ? null :cause.toString()) 4: 构造一个新的运行时异常(它通常包含类和 cause 的详细消息)。 5: */ 6: //下面我们故意让parse抛出ParseException看下打印信息 7: public void ParseExceptionTest(){ 8: try { 9: System.out.println(new SimpleDateFormat("yyyy/MM/dd").parse("2014-1-17")); 10: } catch (ParseException e) { 11: // TODO Auto-generated catch block 12: throw new RuntimeException(e); 13: } 14: }
原文地址:https://www.cnblogs.com/xiarongjin/p/8397456.html
时间: 2024-10-09 16:47:14