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

  ------- 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对象pt1
		ReflectPoint 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属性的值为value
		setProperties(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培训、期待与您交流! ----------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值