现在的位置: 首页 > 综合 > 正文

黑马程序员 高新技术<三>—> 反射、内省、BeanUtils

2013年05月16日 ⁄ 综合 ⁄ 共 18357字 ⁄ 字号 评论关闭

---------------
ASP.Net+Android+IO开发S
.Net培训、期待与您交流! ---------------

第一  反射

一、概述:反射就是把java类中的各个成员映射成相应的java类。简单的说就是取出类的各个成分,然后对其进行操作,这种技术一般用于框架。

1、Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。

2、Class和class的区别

1)class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。

2)Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类。

3、反射的步骤:

1)加载类

2)确定要反射的对象(字段、构造方法、方法)

3)建立具体的反射

二、对象的创建和使用:

1、创建实例对象:不可用new Class()的方式,因为Class没有这样的构造方法。而是将字节码对象赋值给Class变量。方式有如下三种:

①、Class clazz = Class.forName("enhance.test.reflect.Person");参数是类完整的路径。

②、Class clazz = new Person().getClass();这种方式是通过类的对象的getClass方法获取类的字节码。

③、Class clazz3 = Person.class;第三种则是通过类名直接获取类的字节码。

说明:

1)此类已经加载进内存:不需要再加载,直接获取字节码。

2)此类还未加载进内存:先加载类,然后再获取字节码。

3)每个类的字节码对象只有唯一的一个

2、九个预定义的Class:

1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。

2)Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示

3)数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。

3、任何类型都有各自的Class实例对象,如int[].class、void.class等。

@Test//junit测试,方法可以独立运行
	public void testz() throws Exception {
		String str = "ab";
		String str1 = new String("155");
		Class cls = str1.getClass();
		Class cls1 = Class.forName("java.lang.String");
		Class cls2 = str.getClass();
		Class cls3 = String.class;
		
		System.out.println("cls == cls2" + (cls == cls2));


		System.out.println(cls1 == cls2);
		System.out.println(cls1 == cls3);


		System.out.println(cls1.isPrimitive());
		System.out.println(int.class.isPrimitive());
		System.out.println(int.class == Integer.class);
		// Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE,
		// Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
		System.out.println(int.class == Integer.TYPE);// Integer.TYPE获取基本数据类型
		System.out.println(int[].class.isPrimitive());
		System.out.println(int[].class.isArray());
		System.out.println(void.class.isArray());
	}
	
	/*
	 * 把字段中String类型的b全部替换成a
	 */
	@Test
	public void change() throws Exception{
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Person p = new Person();
		Field[] fields = clazz.getFields();
		for(Field field : fields){
			if(field.getType() == String.class){//字节码在内存中是相同的,所以用==
				String oldStr = (String) field.get(p);
				String newStr = oldStr.replace('b', 'a');
				field.set(p, newStr);
				System.out.println(field.get(p));
			}
		}
	}

三、反射构造函数:

1、用到的方法:

(public)getConstructor(Class<?>... parameterTypes)返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

(private)getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。

2、为什么反射出类的构造方法:反射出类的构造方法无非就是用于创建对象

3、反射构造函数的步骤:

①、加载类,有三种方式可选,最常见的是通过Class的静态forName()方法加载。

②、通过Class的对象的getConstructor或getDeclaredConstructor方法对指定的构造函数进行反射

③、如果是私有的需要通过第二步返回的Constructor对象的setAccessible(true)方法打开其访问权限,这种方式称为暴力反射

④、通过第二步返回的Constructor对象的newInstance方法创建对象

⑤、对其对象进行相对应的操作

4、反射常见的构造函数:

被反射的类:

public class Person {

	public String aa = "abaaaa";
	public String ba = "bbbbbaaa";
	private int age = 25;
	private static int password = 123;
	public final static int IN = 1212;

	public Person() {
		System.out.println("person");
	}

	public Person(String name) {
		System.out.println("person name:" + name);
	}

	public Person(String name, int age) {
		System.out.println("person name age" + name + age);
	}

	private Person(List list) {
		System.out.println("person list");
	}

	public void methodDemo() {
		System.out.println("11");
	}

	public void methodDemo(String name, int age) {
		System.out.println(name + age);
	}

	public Class[] methodDemo(String name, int[] age) {
		return new Class[] { String.class };
	}

	private void methodDemo(InputStream in) {
		System.out.println(in);
	}

	public static void methodDemo(int num) {
		System.out.println(num);
	}
	
	public static void main(String[] args) {
		System.out.println("main");
	}

}

反射构造函数:

/*
 * 反射构造函数
 */
public class Demo {

	/*
	 * 反射无参的构造函数 :public Person() 得到构造函数无非就是用于创建对象
	 */
	@Test
	public void test1() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");// 加载类
		Constructor<Person> c = clazz.getConstructor(null);// 反射无参的构造函数,不传参
		Person p = c.newInstance(null);// 创建对象
		System.out.println(p.aa);
	}

	/*
	 * 反射有参数的构造函数 public Person(String name)
	 */
	@Test
	public void test2() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		// 反射有参数的构造函数,传递所需的对象
		Constructor<Person> c = clazz.getConstructor(String.class);
		Person p = c.newInstance("xxxxxxxx");
		System.out.println(p.aa);
	}

	/*
	 * 反射有参数的构造函数 public Person(String name,int age)
	 */
	@Test
	public void test3() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Constructor<Person> c = clazz.getConstructor(String.class, int.class);// java中任何东西都是用一个class来表示的
		Person p = c.newInstance("zhangsan", 200);
		System.out.println(p.aa);
	}

	/*
	 * 反射有参数的构造函数 private Person(List list) getDeclaredConstructor反射私有
	 */
	@Test
	public void test4() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		// 反射私有的构造函数
		Constructor<Person> c = clazz.getDeclaredConstructor(List.class);
		c.setAccessible(true);// 暴力反射,这样才能访问到private
		Person p = c.newInstance(new ArrayList());
		System.out.println(p.aa);
	}

	/*
	 * 创建对象的另一种途径,等效于test1
	 */
	@Test
	public void test5() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		// 通过Class对象直接创建对象,这里其实是调用的是无参的构造函数
		Person p = (Person) clazz.newInstance();
		System.out.println(p.aa);
	}
}

四、反射类中的方法:

1、用到的方法:

(public)getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

(private)getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

2、为什么反射出类的方法:反射出类的方法无非就是让方法运行

3、反射类的方法的步骤:

①、加载类,有三种方式可选,最常见的是通过Class的静态forName()方法加载。

②、通过Class的对象的getMethod或getDeclaredMethod方法对指定的方法进行反射

③、如果是私有的需要通过第二步返回的Method对象的setAccessible(true)方法打开其访问权限,这种方式称为暴力反射

④、通过第二步返回的Method对象的invoke方法让类中被反射出来的方法运行

4、反射常见的方法:

/*
 * 反射方法
 */
public class Demo2 {

	/*
	 * public void methodDemo()
	 */
	@Test
	public void test1() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getMethod("methodDemo", null);// 方法名,参数
		m.invoke(p, null);// 对象,参数
	}

	/*
	 * public void methodDemo(String name,int age)
	 */
	@Test
	public void test2() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getMethod("methodDemo", String.class, int.class);
		m.invoke(p, "jdi", 40);
	}

	/*
	 * public Class[] methodDemo(String name,int[] age)
	 */
	@Test
	public void test3() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getMethod("methodDemo", String.class, int[].class);
		Class[] cls = (Class[]) m.invoke(p, "zhang", new int[] { 1, 2, 3 });
		System.out.println(cls[0]);
	}

	/*
	 * private void methodDemo(InputStream in)
	 */
	@Test
	public void test4() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getDeclaredMethod("methodDemo", InputStream.class);
		m.setAccessible(true);
		m.invoke(p, new FileInputStream("c:\\1.jpg"));
	}

	/*
	 * public static void methodDemo(int num)
	 */
	@Test
	public void test5() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getMethod("methodDemo", int.class);
		m.invoke(null, 54);// 静态不需要对象
	}

	/*
	 * 主函数: public static void main(String[] args)
	 */
	@Test
	public void test6() throws Exception {
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Method m = clazz.getMethod("main", String[].class);
		//二选一,为了兼容以前的版本,所以main方法这里需要经传递的数组惊醒包装
		m.invoke(null, new Object[]{new String[]{"aa","bb"}});
		m.invoke(null, (Object)new String[]{"aa","bb"});
		
		//JDK1.5 Method的invoke(Object obj, Object... args)obj方法所属的对象,args参数
		//JDK1.4 Method的invoke(Object obj, Object obj[])obj方法所属的对象,args参数
		//public void methodDemo(String name,int age)
		//1.4 method.inovke(obj,new Object[]{"aaa","123"});
	}
}

五、反射字段:

1、用到的方法:

(public)getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

(private)getDeclaredField(String name)返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

2、为什么反射出类的字段:反射出类的字段无非就是要对数据进行操作包括取值和设定值

3、反射字段的步骤:

①、加载类,有三种方式可选,最常见的是通过Class的静态forName()方法加载。

②、通过Class的对象的getField或getDeclaredField方法对指定的构造函数进行反射

③、如果是私有的需要通过第二步返回的Field对象的setAccessible(true)方法打开其访问权限,这种方式称为暴力反射

④、通过第二步返回的Field对象的get和set方法分别对其进行取值和设定值的操作

⑤、对其对象进行相对应的操作

4、反射常见的字段:

/*
 * 反射字段
 */
public class Demo3 {
	/*
	 * public String aa = "aaaaaa";
	 */
	@Test
	public void test1() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Field f = clazz.getField("aa");

		// 获取字段的值
		Object value = f.get(p);// 传入对象
		// 获取字段的类型
		Class type = f.getType();

		if (type.equals(String.class)) {
			String str = (String) value;
			System.out.println(str);
		}

		// 设置字段的值
		f.set(p, "xxxxxxxxxxxxxxxxxxxxxxxxx");
		System.out.println(p.aa);
	}

	/*
	 * private int age;
	 */
	@Test
	public void test2() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Field f = clazz.getDeclaredField("age");

		f.setAccessible(true);
		System.out.println(f.get(p));
	}

	/*
	 * private static int = 123;
	 */
	@Test
	public void test3() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Field f = clazz.getDeclaredField("password");

		f.setAccessible(true);
		System.out.println(f.get(p));
	}

	/*
	 * public final static int IN = 1212;
	 */
	@Test
	public void test4() throws Exception {
		Person p = new Person();
		Class clazz = Class.forName("enhance.test.reflect.Person");
		Field f = clazz.getDeclaredField("IN");

		// f.set(p, "1111111111");//不能对final修饰变量再次赋值
		System.out.println(f.get(p));
	}
}

六、HashSet和与hashCode的分析

示例:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class ReflectTest2 {
	public static void main(String [] args){
		Collection cons = new HashSet();
		ReflectPoint pt1 = new ReflectPoint(3,3);
		ReflectPoint pt2 = new ReflectPoint(5,5);
		ReflectPoint pt3 = new ReflectPoint(3,3);

		cons.add(pt1);
		cons.add(pt2);
		cons.add(pt3);
		cons.add(pt1);
		cons.remove(pt1);
		System.out.println(cons.size());
	}
}

public class ReflectPoint {
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "basketball";
	public String str3 = "itcast";
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		//System.out.println("demo...");//测试
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

	public String toString(){
		return str1+";" + str2 + ";" + str3;
	}
}

覆写hashCode()方法的意义:只有存入的是具有hashCode算法的集合的,覆写hashCode()方法才有价值。

1、哈希算法的由来:

若在一个集合中查找是否含有某个对象,通常是一个个的去比较,找到后还要进行equals的比较,对象特别多时,效率很低,通过哈希算法,将集合分为若干个区域,每个对象算出一个哈希值,可将哈希值分组(一般模32为一组),每组对应某个存储区域,依一个对象的哈希码即可确定此对象对应区域,从而减少每个对象的比较,只需在指定区域查找即可,从而提高从集合中查找元素的效率。

示意图:

2、如果不存入是hashCode算法的集合中,那么则不用复写此方法。

3、只有类的实例对象要被采用哈希算法进行存入和检索时,这个类才需要按要求复写hashCode()方法,即使程序可能暂时不会用到当前类的hashCode()方法,但是为提供一个hashCode()方法也不会有什么不好,没准以后什么时候就会用到这个方法,所以通常要求hashCode()和equals()两者一并被覆盖。

4、提示:

1)若同类两对象用equals()方法比较的结果相同时,他们的哈希码也必须是相等的,但反过来就不成立了,如”BB”和”Aa”两字符串用equals()比较式不相等的,但是他们的哈希值是相等的。

2)当一个对象被存储进HashSet集合中,就不能再修改参与计算哈希值的字段,否则对象被修改后的哈希值与最初被存入的HashSet集合中的哈希值就不同了。在这种情况下,即使contains()方法是用对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。

简单说,之前存入的对象和修改后的对象,是具有不同的哈希值,被认为是不同的两个对象,这样的对象不再使用,又不移除,而越来越多,就会导致内存泄露。

补充:

内存泄露:某些对象不再使用了,占用着内存空间,并未被释放,就会导致内存泄露;也就是说当程序不断增加对象,修改对象,删除对象,日积月累,内存就会用光了,就导致内存溢出。

3)对象在调用方法时,对象会先进行一次自身hashCode()方法的调用,再进行操作方法。

七、反射在实际开发中的作用:

一、概述:反射一般是用于框架中

1、框架:通过反射调用位置Java类的一种方式。

如房地产商造房子用户住,门窗和空调等等内部都是由用户自己安装,房子就是框架,用户需使用此框架,安好门窗等放入到房地产商提供的框架中。

框架和工具类的区别:工具类被用户类调用,而框架是调用用户提供的类。

2、框架机器要解决的核心问题:

我们在写框架(造房子的过程)的时候,调用的类(安装的门窗等)还未出现,那么,框架无法知道要被调用的类名,所以在程序中无法直接new其某个类的实例对象,而要用反射来做。

3、简单框架程序的步骤:

1)右击项目名-->File-->命名config.properties的文件,写入键值对:className=java.util.ArrayList,等号右边的可以自己定义集合的名称,即用户可以对此记事本修改成自己的类名。

2)代码实现,加载此文件:

①将文件读取到读取流中,一定要用完整的路径,可以使用getRealPath()方法获取路径名,再加上自己定义的文件夹名。

②用Properties类的load()方法将流加载经内存,即提取文件中的信息。

③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。

3)通过getProperty()方法获取类名属性,将传入的类名赋值给指定变量。

4)用反射的方式,创建对象newInstance()

5)进行相关的具体操作。

二、类加载器:

1、简述:类加载器是将.class的文件加载进内存,也可将普通文件中的信息加载进内存。

2、文件的加载问题:

1)eclipse会将源程序中的所有.java文件加载成.class文件,以确保编译,然后放到classPath指定的目录中去。并且会将非.java文件原封不动的复制到.class指定的目录中去。在真正编译的时候,使用classPath目录中的文件,即放置.class文件的目录。

2)写完程序是要讲配置文件放到.class文件目录中一同打包,这些都是类加载器加载的,资源文件(配置文件)也同样加载了配置文件。

3)框架中的配置文件都要放到classPath指定的文件夹中,原因是它的内部就是用类加载器加载的文件。

3、资源文件的加载:是使用类加载器。

1)由类加载器ClassLoader的一个对象加载经内存,即用getClassLoader()方法加载。若要加载普通文件,可用getResourseAsStream(String name)在classPath的文件中逐一查找要加载的文件。

2)在.class身上也提供了方法来加载资源文件,其实它内部就是先调用了Loader方法,再加载的资源文件。

如:Reflect.class.getResourseAsStream(String name)

4、配置文件的路径问题:

第一、用绝对路径,通过getRealPath()方法运算出来具体的目录,而不是内部编码出来的。

一般先得到用户自定义的总目录,在加上自己内部的路径。可以通过getRealPath()方法获取文件路径。对配置文件修改是需要要储存到配置文件中,那么就要得到它的绝对路径才行,因此,配置文件要放到程序的内部。

第二、配置路径问题:

①如果配置文件和classPath目录没关系,就必须写上绝对路径,

②如果配置文件和classPath目录有关系,即在classPath目录中或在其子目录中(一般是资源文件夹resource),那么就得写相对路径,因为它自己了解自己属于哪个包,是相对于当前包而言的。

public class FrameDemo {
	public static void main(String[] args) throws Exception {
		/*
		 * 1、配置文件的路径必须是完整的路径,但完整的路径不是硬编码,而是运算出来的
		 * 配置文件的路径需在程序内部
		 */
//		InputStream in = new FileInputStream("config.properties");
		/*
		 * 2、通过类加载器加载
		 * 但是配置文件属性是只读的,就是不能将文件写出(就是没有OutputStream)
		 */
//		InputStream in = FrameDemo.class.getClassLoader().getResourceAsStream(
//				"enhance/test/reflect/config.properties");
		/*
		 * 3、用自己的类加载
		 */
		InputStream in = FrameDemo.class.getResourceAsStream("config.properties");
		
		Properties pro = new Properties();
		pro.load(in);
		in.close();//用完资源立马关闭,防止少量的内存泄露
		String className = pro.getProperty("className");
		
		//创建数组
		Collection collection = (Collection) Class.forName(className).newInstance();
		collection.add("123");
		collection.add("354");
		collection.add("123");
		
		System.out.println(collection.size());
	}
}

第二  内省

一、概述:

内省主要针对JavaBean进行操作。

二、JavaBean(存在于java.bean包中)

1、简述:

1)JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法主要用于访问私有的字段,且方法都符合某种特殊的命名规则。

2)它是一种特殊的Java类,其中的方法名称等,都符合特殊的规则。只要一个类中含有get和set打头的方法,就可以将其当做JavaBean使用。

3)字段和属性:

字段就是我们定义的一些成员变量,如private String name;等

而属性是具有某些功能,Bean属性,是含有get或set方法的那些属性的字段,即这个变量的get属性,set属性等。

2、作用:

如果要在两个模板之间传递多个信息,可将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO),这些信息在类中用私有字段来储存,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。

3、命名方式:

JavaBean的属性是根据其中的setter和getter方法来确定的,而不是依据其中的变量,如方法名为setId,则中文意思是设置Id,getId也是如此;去掉前缀,剩余部分就是属性名称,如果剩余部分的第二个字母小写,则把剩余部分改为小写。如:getAge/setAge-->age;gettime-->time;setTime-->time;getCPU-->CPU。

4、总之、一个类被当做JavaBean使用时,JavaBaan的属性是根据方法名推断出来的,它根本看不到Java类内部的成员变量。

5、JavaBean的好处:

一个符合JavaBean特点的类当做普通类一样可以使用,但是把它当做JavaBean类用肯定有好处的:

1)在JavaEE开发中,经常要使用JavaBean。很多环境就要求按JavaBean的方式进行操作。

2)JDK中提供了对JavaBean进行操作的API,这套API称为内省,若要自己通过getX的方式来访问私有x,可用内省这套API,操作JavaBean要比使用普通的方式更方便。

/*
 * 使用内省api操作bean的属性
 */
public class IntrospectorDemo {

	/*
	 * 得到类的属性
	 */
	@Test
	public void test1() throws Exception {
		// BeanInfo info = Introspector.getBeanInfo(Book.class);//得到bean所有的属性,包含父类的
		BeanInfo info = Introspector.getBeanInfo(Book.class, Object.class);// 得到本类所有的属性
		PropertyDescriptor[] pds = info.getPropertyDescriptors();//得到所有的属性描述其
		for (PropertyDescriptor pd : pds) {
			System.out.println(pd.getName());//取出所有的属性
		}
	}

	/*
	 * 操作bean的指定属性
	 */
	@Test
	public void test2() throws Exception {
		Book b = new Book();
		// 得到属性描述器
		PropertyDescriptor dp = new PropertyDescriptor("name", Book.class);
		System.out.println(dp.getPropertyType());// 得到类型
		// 设置值
		Method method = dp.getWriteMethod();// public void setName(String name)
		method.invoke(b, "java");

		// 获取值
		method = dp.getReadMethod();
		System.out.println(method.invoke(b, null));// public String getName()
	}
}

第三  BeanUtils工具包

一、概述:

1、BeanUtils等工具包都是由Apache提供,为了便于开发。
2、BeanUtils可以将8种基本数据类型进行自动的转换,因此对于非基本数据类型,就需要注册转换器Converter,这就需要ConverUtils包

二、好处:

1、提供的set或get方法中,传入的是字符串,返回的还是字符串,因为在浏览器中,用户输入到文本框的都是以字符串的形式发送至服务器上的,所以操作的都是字符串。也就是说这个工具包的内部有自动将整数转换为字符串的操作。

2、支持属性的级联操作,即支持属性链。如可以设置:人的脑袋上的眼镜的眼珠的颜色。这种级联属性的属性连如果自己用反射,那就很困难了,通过这个工具包就可以轻松调用。

3、可以和Map集合进行相互转换:可将属性信息通过键值对的形式作为Map集合存储(通过staticjava.util.Map describe(java.lang.Object bean)的方法),也可以将Map集合转换为JavaBean中的属性信息(通过static void populate(java.lang.Object bean, java.util.Map properties)的方法)。

示例:

/*
 * 创建一个bean,专门用于封装数据
 */
public class Person {
	private int password;// 字段
	private String name;// 字段
	private int age;// 字段
	private Date birthday;

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	/*
	 * 一个bean的属性有她其中的get或set方法决定 
	 * 现在这个bean中就有四个属性 本类四个继承Object一个getClass属性
	 */
	public int getPassword() {
		return password;
	}

	public void setPassword(int password) {
		this.password = password;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

/*
 * 使用beanutils操作bean的属性(第三方jar包)
 */
public class Demo {
	/*
	 * 简单的操作
	 */
	@Test
	public void test1() throws Exception {
		Person p = new Person();
		// 通过BeanUtils设置属性
		BeanUtils.setProperty(p, "name", "java book");
		// 通过BeanUtils获取属性
		System.out.println(BeanUtils.getProperty(p, "name"));
	}

	/*
	 * 基本数据类型的自动匹配
	 * 只支持8种基本数据类型
	 */
	@Test
	public void test2() throws Exception {
		String name = "xcc";
		String password = "123";
		String age = "23";

		Person p = new Person();
		BeanUtils.setProperty(p, "name", name);
		BeanUtils.setProperty(p, "password", password);
		BeanUtils.setProperty(p, "age", age);
		
		System.out.println(BeanUtils.getProperty(p, "name"));
		System.out.println(BeanUtils.getProperty(p, "password"));
		System.out.println(BeanUtils.getProperty(p, "age"));
	}
	
	/*
	 * 自定义类型转化器
	 */
	@Test
	public void teat3() throws Exception{
		String name = "xcc";
		String password = "123";
		String age = "23";
		String birthday = "1980-08-08";
		
		//为了让日期赋到bean的birthday上,所以需要给BeanUtils注册一个日期转化器
		ConvertUtils.register(new Converter() {
			@Override
			public Object convert(Class type, Object value) {
				if(value == null){
					return null;
				}
				if(!(value instanceof String)){
					new ConversionException("这里只支持String类型的转换");
				}
				
				String str = (String) value;
				if(str.trim().equals("")){
					return null;
				}
				
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
				try {
					return df.parse(str);
				} catch (ParseException e) {
					new RuntimeException(e);//异常链不能断
				}
				return null;
			}
		}, Date.class);
		
		Person p = new Person();
		BeanUtils.setProperty(p, "name", name);
		BeanUtils.setProperty(p, "password", password);
		BeanUtils.setProperty(p, "age", age);
		BeanUtils.setProperty(p, "birthday", birthday);
		
		System.out.println(BeanUtils.getProperty(p, "name"));
		System.out.println(BeanUtils.getProperty(p, "password"));
		System.out.println(BeanUtils.getProperty(p, "age"));
		System.out.println(BeanUtils.getProperty(p, "birthday"));
	}
	
	/*
	 * BeanUtils提供了很多转换器,一般情况下可以直接拿过来用的
	 */
	@Test
	public void teat4() throws Exception{
		String name = "xcc";
		String password = "123";
		String age = "23";
		String birthday = "1980-08-08";
		
		ConvertUtils.register(new DateLocaleConverter(), Date.class);
		
		Person p = new Person();
		BeanUtils.setProperty(p, "name", name);
		BeanUtils.setProperty(p, "password", password);
		BeanUtils.setProperty(p, "age", age);
		BeanUtils.setProperty(p, "birthday", birthday);
		
		System.out.println(BeanUtils.getProperty(p, "name"));
		System.out.println(BeanUtils.getProperty(p, "password"));
		System.out.println(BeanUtils.getProperty(p, "age"));
		System.out.println(BeanUtils.getProperty(p, "birthday"));
	}
	
	/*
	 * 用map集合填充bean的属性
	 */
	@Test
	public void teat5() throws Exception{
		Map<String,String> map = new HashMap<String,String>();
		map.put("name", "zhangs");
		map.put("password", "123");
		map.put("age", "23");
		map.put("birthday", "1989-01-02");
		
		ConvertUtils.register(new DateLocaleConverter(), Date.class);
		Person p = new Person();
		BeanUtils.populate(p, map);//将map中的值填充到bean中
		
		System.out.println(BeanUtils.getProperty(p, "name"));
		System.out.println(BeanUtils.getProperty(p, "password"));
		System.out.println(BeanUtils.getProperty(p, "age"));
		System.out.println(BeanUtils.getProperty(p, "birthday"));
	}
}

抱歉!评论已关闭.