@liayun
2016-06-24T17:45:26.000000Z
字数 11917
阅读 1937
java基础加强
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为setId
,中文意思即为设置id
,至于你把它存到哪个变量上,用管吗?如果方法名为getId
,中文意思即为获取id
,至于你从哪个变量上取,用管吗?去掉set
前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的,如果剩余部分的第二个字母是大写的,则把剩余部分的首字母保持原样。
例,
setId()的属性名→id
isLast()的属性名→last
setCPU的属性名是什么?→CPU
getUPS的属性名是什么?→UPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
内省访问JavaBean属性的两种方式:
案例一:
有类ReflectPoint
如下:
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) { // 快捷键——Alt+Shift+S + O
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return str1 + ":" + str2 + ":" + str3;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
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 int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
直接new一个PropertyDescriptor
(属性描述器)对象的方式来让大家了解JavaBean API的价值,先用一段代码读取JavaBean的属性,然后再用一段代码设置JavaBean的属性。
public class IntroSpectorTest1 {
public static void main(String[] args) throws Exception {
ReflectPoint pt = new ReflectPoint(3, 6);
// 获得x属性的值
String propertyName = "x";
// "x"-->"X"-->"getX"-->MethodGetX-->
PropertyDescriptor pd = new PropertyDescriptor(propertyName, pt.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt);
System.out.println(retVal);
// 设置x属性的值
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName, pt.getClass());
Method methodSetX = pd1.getWriteMethod();
methodSetX.invoke(pt, 90);
// 通过对象的getX()方法获取设置的x的属性值
System.out.println(pt.getX());
}
}
用eclipse将读取属性和设置属性的流水帐代码分别抽取成方法。
步骤为:选中要抽取的代码
→Refactor
→Extract Method...
→设置Method name:
public class IntroSpectorTest1 {
public static void main(String[] args) throws Exception {
ReflectPoint pt = new ReflectPoint(3, 6);
// 获得x属性的值
String propertyName = "x";
// "x"-->"X"-->"getX"-->MethodGetX-->
Object retVal = getProperty(pt, propertyName);
System.out.println(retVal);
// 设置x属性的值
Object value = 90;
setProperty(pt, propertyName, value);
System.out.println(pt.getX());
}
// 只要调用这个方法,并给这个方法传递了一个对象、属性名和设置值,它就能完成属性修改的功能。
private static void setProperty(ReflectPoint pt, String propertyName, Object value)
throws IntrospectionException, IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName, pt.getClass());
Method methodSetX = pd1.getWriteMethod();
methodSetX.invoke(pt, value);
}
private static Object getProperty(ReflectPoint pt, String propertyName)
throws IntrospectionException, IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, pt.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt);
return retVal;
}
}
采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性。在程序中把一个类当作JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当作JavaBean看的结果信息。
这里只关注getProperty()
方法:
private static Object getProperty(ReflectPoint pt, String propertyName)
throws IntrospectionException, IllegalAccessException, InvocationTargetException {
BeanInfo beanInfo = Introspector.getBeanInfo(pt.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds) {
if(pd.getName().equals(propertyName)) {
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt);
break;
}
}
return retVal;
}
得到BeanInfo最好采用obj.getClass()
方式,而不要采用类名.class
方式,这样程序更通用。
案例二:
有类Person如下:
public class Person { // javabean
private String name; // 字段
private String password; // 字段
private int age; // 字段
// 注意:Person类从Object类继承了getClass()方法,所以还有一个class属性
public String getAb() {
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
注意:Person类从Object类继承了getClass()方法,所以还有一个class属性。
使用内省api操作bean的属性。
得到bean的所有属性。
public void test1() throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class);
PropertyDescriptor[] pds = info.getPropertyDescriptors();
for(PropertyDescriptor pd : pds) {
System.out.println(pd.getName());
}
}
输出:
ab
age
class
name
password
若只想获得Person类自己的属性,不想获取从父类继承的属性,可这样做:
public void test1() throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class, Object.class); // 若只想获得Person类自己的属性,不想获取从父类继承的属性
PropertyDescriptor[] pds = info.getPropertyDescriptors();
for(PropertyDescriptor pd : pds) {
System.out.println(pd.getName());
}
}
操作bean的指定属性:age
。
public void test2() throws Exception {
Person p = new Person();
PropertyDescriptor pd = new PropertyDescriptor("age", Person.class);
// 得到属性的写方法,为属性赋值
Method method = pd.getWriteMethod(); // 得到public void setAge(int age) {...}
method.invoke(p, 45);
// 获取属性的值
method = pd.getReadMethod(); // 得到public int getAge() {...}
System.out.println(method.invoke(p, null));
}
高级点的内容:获取当前操作的属性的类型。
public void test3() throws Exception {
PropertyDescriptor pd = new PropertyDescriptor("age", Person.class);
System.out.println(pd.getPropertyType()); // 输出int
}
Sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtils,在BeanUtils中可以直接进行类型的自动转换。
Beanutils工具包的常用类:
①BeanUtils
②PropertyUtils
③ConvertUtils.regsiter(Converter convert, Class clazz)`
④自定义转换器
Download
commons-beanutils-1.9.2-bin.zip
进行下载就OK了,若要查看源码也可下载commons-beanutils-1.9.2-src.zip
。使用BeanUtils
在项目中导入commons-beanutils-1.9.2.jar包即可(PS:把此jar包复制到项目的lib文件夹,右击包
→Build Path
→add to build path
显示奶瓶即可)
注意:commons-beanutils-1.9.2.jar要与commons-logging-1.2.Jar共用。
在前面内省例子的基础上,用BeanUtils类先get原来设置好的属性,再将其set为一个新值。
System.out.println(BeanUtils.getProperty(pt, "x"));
BeanUtils.setProperty(pt, "x", "89");
以下代码会输出java.lang.String
。
System.out.println(BeanUtils.getProperty(pt, "x").getClass().getName()); // java.lang.String
注意: BeanUtils操作属性是以String类型设置进去的,自动进行类型转换(String→int),但实际JavaBean的属性是int,获取属性时也以String返回。
结论:get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。
BeanUtils支持属性的级联操作(属性链):
若类ReflectPoint
中有属性birthday
,并有其setter/getter方法如下:
private Date birthday = new Date();
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
BeanUtils支持属性的级联操作如下:
BeanUtils.setProperty(pt1, "birthday.time", "111"); // 支持属性的级联操作(属性链)
System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
再来看一个案例,有Person类如下:
public class Person { // javabean
private String name; // 字段
private String password; // 字段
private int age; // 字段
private Date birthday;
public String getAb() {
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
使用beanUtils操作bean的属性(第三方JAR包)。
public void test1() throws IllegalAccessException, InvocationTargetException {
Person p = new Person();
BeanUtils.setProperty(p, "name", "xcc");
System.out.println(p.getName());
}
注意:BeanUtils对数据进行转换,转换时只支持8种基本数据类型,碰到复杂类型无法转换!
所以,下面的代码是有问题的。
public void test2() throws IllegalAccessException, InvocationTargetException {
String name = "aaaa";
String password = "123";
String age = "34";
String birthday = "1980-09-09";
Person p = new Person();
BeanUtils.setProperty(p, "name", name);
BeanUtils.setProperty(p, "password", password);
BeanUtils.setProperty(p, "age", age); // 对数据进行转换,转换时只支持8种基本数据类型
BeanUtils.setProperty(p, "birthday", birthday); // 碰到复杂类型无法转换!!!否则必须给beanUtils注册日期转换器
System.out.println(p.getName());
System.out.println(p.getPassword());
System.out.println(p.getAge());
System.out.println(p.getBirthday());
}
为了让日期赋到bean的birthday属性上,我们给beanUtils注册一个日期转换器。
public void test3() throws IllegalAccessException, InvocationTargetException {
String name = "aaaa";
String password = "123";
String age = "34";
String birthday = "1980-09-09";
// 为了让日期赋到bean的birthday属性上,我们给beanUtils注册一个日期转换器
ConvertUtils.register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
if(value == null) { // 先检查
return null;
}
if(!(value instanceof String)) {
throw new ConversionException("只支持String类型的转换!!"); // BeanUtils文档中建议抛此异常
}
String str = (String) value; // 非空且为String
if(str.trim().equals("")) { // 再判断是否有值
return null;
}
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try {
return (T) df.parse(str);
} catch (ParseException e) {
/*
* 出现异常,一定要通知上一层程序,抛个异常给上一层
* 不能打印在控制台上
*/
// e.printStackTrace(); // error
// throw new RuntimeException(); // error
throw new RuntimeException(e); // 异常链不能断,必须把原来的异常信息封装进去,抛出异常给上一层,上一层就会知道到底出了什么问题
}
}
}, 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(p.getName());
System.out.println(p.getPassword());
System.out.println(p.getAge());
// Date date = p.getBirthday();
// System.out.println(date.toLocaleString());
System.out.println(p.getBirthday());
}
注意:异常的处理原则——出现异常,一定要通知上一层程序,抛个异常给上一层,不能打印在控制台上;且异常链不能断,必须把原来的异常信息封装进去,抛出异常给上一层,上一层就会知道到底出了什么问题。
再我们自定义转换器之前,应该看看该工具类有没有给我们提供相应的转换器,可发现该工具类给我们提供了一个转换器DateLocaleConverter
。
public void test4() throws IllegalAccessException, InvocationTargetException {
String name = "aaaa";
String password = "123";
String age = "34";
String birthday = "1980-09-09";
ConvertUtils.register(new DateLocaleConverter(), Date.class); // 此转换器DateLocaleConverter有bug
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(p.getName());
System.out.println(p.getPassword());
System.out.println(p.getAge());
// Date date = p.getBirthday();
// System.out.println(date.toLocaleString());
System.out.println(p.getBirthday());
}
注意:此转换器DateLocaleConverter有bug,对于String birthday = "";
会报异常,没有我们自定义的转换器健壮。
用map集合中的值,填充bean的属性。
public void test5() throws IllegalAccessException, InvocationTargetException {
Map map = new HashMap();
map.put("name1", "aaa"); // 注意:map集合中的键必须和bean的属性名称一致
map.put("password", "123");
map.put("age", "23");
map.put("birthday", "1980-09-09");
ConvertUtils.register(new DateLocaleConverter(), Date.class);
Person bean = new Person();
BeanUtils.populate(bean, map); // 用map集合中的值,填充bean的属性
System.out.println(bean.getName());
System.out.println(bean.getPassword());
System.out.println(bean.getAge());
// Date date = p.getBirthday();
// System.out.println(date.toLocaleString());
System.out.println(bean.getBirthday());
}
注意:map集合中的键必须和bean的属性名称一致。
使用PropertyUtils
用PropertyUtils类先get原来设置好的属性,再将其set为一个新值。
PropertyUtils.setProperty(pt, "x", 78);
System.out.println(PropertyUtils.getProperty(pt, "x"));
若设置属性时,
PropertyUtils.setProperty(pt1, "x", "9");
则会报异常java.lang.IllegalArgumentException: argument type mismatch
。
且,如下代码会输出java.lang.Integer
。
System.out.println(PropertyUtils.getProperty(pt, "x").getClass().getName());
结论:get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。
记录遇到的一个error
:
The project was not built due to "Could not write file: E:\MyJava\java_adv\javaenhance\bin\cn.". Fix the problem, then try refreshing this project and building it since it may be inconsistent.
借助有道翻译为:
此项目由于不能写入文件
E:\MyJava\java_adv\javaenhance\bin\cn
而不能被构建。解决此问题,试着刷新该项目并构建,因为它可能是不一致的。
我也不知道是什么情况,只能百度了,查到解决办法如下:
工程有错误的话,用下eclipse的project
→clean
。