---------------------- <a href="
http://edu.csdn.net"target="blank">ASP.Net+Android+IOS
开发</a>、<a href="
http://edu.csdn.net"target="blank">.Net
培训</a>、期待与您交流! ----------------------
一、JavaBean内省概述
1、为什么要学内省?
开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以SUN公司开发了一套API,专门用于操作JavaBean的属性,这套API就叫做内省。
2、什么是JavaBean?
JavaBean是一种特殊的java类,这种java类中的方法主要用于访问私有的字段,其中的某些方法符合某种命名规则。如果一个java类中的一些方法符合某种命名规则,则可以把它当做JavaBean来使用。
3、什么是JavaBean的属性?
JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。
如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set或get前缀,剩余部分就是属性名。如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
比如:setId()的属性名是id,ØisLast()的属性名是last,ØsetCPU的属性名是CPU,ØgetUPS的属性名是UPS……
总之,一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
4、内省访问JavaBean属性的方式
有两种方式:
1)通过PropertyDescriptor类操作Bean的属性。
2)通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后通过反射机制来调用这些方法。
下面通过一个示例来说明如何使用内省操作JavaBean的属性:
首先定义一个JavaBean,定义好属性:
package cn.itcast.fang.introspector;
import java.util.Date;
/**
* 人类
* @author 中关村阿旺
*
*/
public class Person {
public String name; //姓名
public int age; //年龄
public String sex; //性别
public Date birthday; //出生日期
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAb(){
return null;
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
然后定义一个测试类,使用内省操作JavaBean的属性:
package cn.itcast.fang.introspector;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.junit.Test;
//内省演示,使用内省api操作bean的属性
public class Demo {
//得到bean的所有属性
@Test
public void test() throws Exception{
//Introspector 类为通过工具学习有关受目标 Java Bean 支持的
//属性、事件和方法的知识提供了一个标准方法。
//对于这三种信息,Introspector 将分别分析 bean 的类和超类,
//寻找显式或隐式信息,使用这些信息构建一个全面描述目标 bean 的 BeanInfo 对象。
//希望提供有关其 bean 的显式信息的 bean 实现者可以提供某个 BeanInfo 类,
//该BeanInfo类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。
//static BeanInfo getBeanInfo(Class<?> beanClass)
//在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
BeanInfo info=Introspector.getBeanInfo(Person.class);
//static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
//在给定的“断”点之下,在 Java Bean 上进行内省,了解其所有属性和公开的方法。
//也就是不包括从stopClass中继承的属性
BeanInfo info2=Introspector.getBeanInfo(Person.class, Object.class);
//PropertyDescriptor[] getPropertyDescriptors()
//获得 beans PropertyDescriptor。 (属性描述器)
PropertyDescriptor[] pds=info.getPropertyDescriptors();
PropertyDescriptor[] pds2=info2.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
//public String getName()获得此特性的编程名称。
System.out.println(pd.getName());
}
System.out.println("==============");
for(PropertyDescriptor pd : pds2){
System.out.println(pd.getName());
}
}
//操作bean的某个指定属性
@Test
public void test2() throws Exception{
Person p=new Person();
//PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性.
//PropertyDescriptor(String propertyName, Class<?> beanClass)
//通过调用 getFoo 和 setFoo 存取方法,
//为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
PropertyDescriptor pd=new PropertyDescriptor("age", Person.class);
//Method getWriteMethod()
//获得应该用于写入属性值的方法。
Method m=pd.getWriteMethod();//相当于调用setAge()方法
//通过反射调用方法
m.invoke(p, 45);
System.out.println(p.getAge());
//Method getReadMethod()
//获得应该用于读取属性值的方法。
Method m2=pd.getReadMethod();//相当于调用getAge()方法
//通过反射调用方法
Object obj=m2.invoke(p, null);
System.out.println(obj);
}
//获得当前操作的属性的类型
@Test
public void test3() throws Exception{
Person p=new Person();
PropertyDescriptor pd=new PropertyDescriptor("name",Person.class);
Object obj=pd.getPropertyType();
System.out.println(obj);
//因为字节码对象只有一个,所以用双等号比较高效
if(obj == String.class){
Method m=pd.getWriteMethod();
m.invoke(p, "zhangsan");
System.out.println(p.getName());
}
}
}
5、BeanUtils
Sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtils。
在Beanutil中可以直接进行类型的自动转换。使用BeanUtils操作Bean的属性首先需要导入两个jar包(第三方开发工具包):beanutils包和logging包。
在Beanutil中可以直接进行类型的自动转换。使用BeanUtils操作Bean的属性首先需要导入两个jar包(第三方开发工具包):beanutils包和logging包。
演示示例如下:
注:代码中的Person类指的是上面的Person类。
package cn.itcast.fang.beanutils;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
import org.junit.Test;
public class Demo {
@Test
public void test1() throws Exception{
//BeanUtils可以填充JavaBeans属性通过反射实用方法。
Person p=new Person();
//void org.apache.commons.beanutils.BeanUtils
//.setProperty(Object bean, String name, Object value)
//throws IllegalAccessException, InvocationTargetException
//设置指定的属性值,进行类型转换为所需的符合目标的属性类型。
BeanUtils.setProperty(p, "name", "zhangsan");
System.out.println(p.getName());
}
@Test
public void test3() throws IllegalAccessException, InvocationTargetException{
//例如:接收用户浏览器传递过来的姓名、年龄、性别等信息
//由于是表单提交,所以都是字符串
String name="lisi";
String age="67";
String sex="男";
String birthday="1989-09-16";
//使用jar包中自带的Converter接口的子类DateLocaleConverter完成日期转换动作
//不过有一个bug,不能判断值为空的字符串
//比如:String birthday="";程序执行会出现异常。
ConvertUtils.register(new DateLocaleConverter(), Date.class);
Person p=new Person();
BeanUtils.setProperty(p, "name", name);
//p对象的age属性应该接收int类型的数据,但是使用BeanUtils可以自动帮我们完成转换动作
//只支持8种基本数据类型的转换
BeanUtils.setProperty(p, "age", age);
BeanUtils.setProperty(p, "sex", sex);
BeanUtils.setProperty(p, "birthday", birthday);
System.out.println(p.getName());
System.out.println(p.getAge());
System.out.println(p.getSex());
System.out.println(p.getBirthday());
}
}
二、类加载器概述
1、什么是类加载器?
我们知道java程序的运行是运行的java虚拟机中的字节码,那么类加载器就是负责把要运行的字节码加载到java虚拟机中。
l
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:
BootStrap,ExtClassLoader和AppClassLoader。
l
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap
。
l
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载。
2、类加载器之间的父子关系和管辖范围图
3、类加载器的委托机制
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托机制。
类加载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类加载器去进行真正的加载。当回退到最初的类加载器时,如果它自己也不能完成类的加载,那就应报告ClassNotFoundException异常。
下面用代码演示类加载器的委托机制:
package cn.itcast.day2;
public class ClassLoaderTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//ClassLoader getClassLoader()
//返回该类的类加载器。
Class clazz=ClassLoaderTest.class.getClassLoader().getClass();
System.out.println(clazz.getName());
System.out.println(System.class.getClassLoader());
System.out.println("------------------------");
//ClassLoader 类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。
ClassLoader cl=ClassLoaderTest.class.getClassLoader();
while(cl != null){
System.out.println(cl.getClass().getName());
cl=cl.getParent();
}
System.out.println(cl);
}
}
上述代码的打印结果为:
sun.misc.Launcher$AppClassLoader
null
------------------------
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
null
------------------------
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
分隔线上面的打印结果说明ClassLoaderTest类是由AppClassLoader类加载器加载的,System类的类加载器是BootStrap。
分隔线下面的打印结果说明了类加载器之间的父子关系。
---------------------- <a href="
http://edu.csdn.net"target="blank">ASP.Net+Android+IOS
开发</a>、<a href="
http://edu.csdn.net"target="blank">.Net
培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>