BeanUtils实现对象拷贝和属性复制的使用
对于某一属性,即原始对象和目的对象的属性名称相同,就可以拷贝原始对象的属性值到目的对象中。
注意属性必须添加set,get方法,否则拷贝不成功
基本类型之间如果是属性的类型不同,BeanUtils会尝试去强制类型转换,然后去拷贝,如果能转换则不抛出异常。所以在转换时要确保属性名称相同,属性类型最好也相同。
引入不同的包,jar中方法也不一样
是A中的值付给B
package org.springframework.beans;
BeanUtils.copyProperties(A,B);
是B中的值付给A
package org.apache.commons.beanutils;
BeanUtils.copyProperties(A,B);
对于某一属性,即原始对象和目的对象的属性名称相同,就可以拷贝原始对象的属性值到目的对象中。
注意属性必须添加set,get方法,否则拷贝不成功
基本类型之间如果是属性的类型不同,BeanUtils会尝试去强制类型转换,然后去拷贝,如果能转换则不抛出异常。所以在转换时要确保属性名称相同,属性类型最好也相同。
引入不同的包,jar中方法也不一样
package org.springframework.beans;中的
BeanUtils.copyProperties(A,B);
是A中的值付给B
package org.springframework.beans;
BeanUtils.copyProperties(A,B);
package org.apache.commons.beanutils;(常用)
BeanUtils.copyProperties(A,B);
是B中的值付给A
package org.apache.commons.beanutils;
BeanUtils.copyProperties(aValue, aLocal)
将alocal实体bean转换为对应的avalue 对象:上面的代码从aLocal对象复制属性到aValue对象。它相当简单!它不管alocal(或对应的avalue)对象有多少个属性,只管进行复制。我们假设 alocal对象有100个属性。上面的代码使我们可以无需键入至少100行的冗长、容易出错和反复的get和set方法调用。
用法
//得到TeacherForm
TeacherForm teacherForm=(TeacherForm)form;
//构造Teacher对象
Teacher teacher=new Teacher();
//赋值
teacher.setName(teacherForm.getName());
teacher.setAge(teacherForm.getAge());
teacher.setGender(teacherForm.getGender());
teacher.setMajor(teacherForm.getMajor());
teacher.setDepartment(teacherForm.getDepartment());
//持久化Teacher对象到数据库
HibernateDAO.save(teacher);
而使用BeanUtils后,代码就大大改观了,如下所示:
//得到TeacherForm
TeacherForm teacherForm=(TeacherForm)form;
//构造Teacher对象
Teacher teacher=new Teacher();
//赋值
BeanUtils.copyProperties(teacher,teacherForm);
//持久化Teacher对象到数据库
HibernateDAO.save(teacher);
如果Teacher和TeacherForm间存在名称不相同的属性,则BeanUtils不对这些属性进行处理,需要程序员手动处理。例如 Teacher包含modifyDate(该属性记录最后修改日期,不需要用户在界面中输入)属性而TeacherForm无此属性,那么在上面代码的 copyProperties()后还要加上一句:
teacher.setModifyDate(new Date());
这里要注意一点,java.util.Date是不被支持的,而它的子类java.sql.Date是被支持的。因此如果对象包含时间类型的属性,且希望被转换的时候,一定要使用java.sql.Date类型。否则在转换时会提示argument mistype异常。
BeanUtils支持的转换类型如下:
* java.lang.BigDecimal
* java.lang.BigInteger
* boolean and java.lang.Boolean
* byte and java.lang.Byte
* char and java.lang.Character
* java.lang.Class
* double and java.lang.Double
* float and java.lang.Float
* int and java.lang.Integer
* long and java.lang.Long
* short and java.lang.Short
* java.lang.String
* java.sql.Date
* java.sql.Time
* java.sql.Timestamp
Clone 方法
Java 的深拷贝和浅拷贝,其实现方式正是通过调用 Object 类的 clone() 方法来完成。在 Object.class 类中,源码为:
protected native Object clone() throws CloneNotSupportedException;
这是一个用 native 关键字修饰的方法,关于native关键字有一篇博客专门有介绍,不理解也没关系,只需要知道用 native 修饰的方法就是告诉操作系统,这个方法我不实现了,让操作系统去实现。具体怎么实现我们不需要了解,只需要知道 clone方法的作用就是复制对象,产生一个新的对象。
浅拷贝package com.ys.test;
public class Person implements Cloneable{
public String pname;
public int page;
public Address address;
public Person() {}
public Person(String pname,int page){
this.pname = pname;
this.page = page;
this.address = new Address();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void setAddress(String provices,String city ){
address.setAddress(provices, city);
}
public void display(String name){
System.out.println(name+":"+"pname=" + pname + ", page=" + page +","+ address);
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
}
package com.ys.test;
public class Address {
private String provices;
private String city;
public void setAddress(String provices,String city){
this.provices = provices;
this.city = city;
}
@Override
public String toString() {
return "Address [provices=" + provices + ", city=" + city + "]";
}
}
这是一个我们要进行赋值的原始类 Person。下面我们产生一个 Person 对象,并调用其 clone 方法复制一个新的对象。
注意:调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。
public void testShallowClone() throws Exception{
Person p1 = new Person("zhangsan",21);
p1.setAddress("湖北省", "武汉市");
Person p2 = (Person) p1.clone();
System.out.println("p1:"+p1);
System.out.println("p1.getPname:"+p1.getPname().hashCode());
System.out.println("p2:"+p2);
System.out.println("p2.getPname:"+p2.getPname().hashCode());
p1.display("p1");
p2.display("p2");
p2.setAddress("湖北省", "荆州市");
System.out.println("将复制之后的对象地址修改:");
p1.display("p1");
p2.display("p2");
}
首先看原始类 Person 实现 Cloneable 接口,并且覆写 clone 方法,它还有三个属性,一个引用类型 String定义的 pname,一个基本类型 int定义的 page,还有一个引用类型 Address ,这是一个自定义类,这个类也包含两个属性 pprovices 和 city 。
接着看测试内容,首先我们创建一个Person 类的对象 p1,其pname 为zhangsan,page为21,地址类 Address 两个属性为 湖北省和武汉市。接着我们调用 clone() 方法复制另一个对象 p2,接着打印这两个对象的内容。
从第 1 行和第 3 行打印结果:
p1:com.ys.test.Person@349319f9
p2:com.ys.test.Person@258e4566
可以看出这是两个不同的对象。
从第 5 行和第 6 行打印的对象内容看,原对象 p1 和克隆出来的对象 p2 内容完全相同。
代码中我们只是更改了克隆对象 p2 的属性 Address 为湖北省荆州市(原对象 p1 是湖北省武汉市) ,但是从第 7 行和第 8 行打印结果来看,原对象 p1 和克隆对象 p2 的 Address 属性都被修改了。
也就是说对象 Person 的属性 Address,经过 clone 之后,其实只是复制了其引用,他们指向的还是同一块堆内存空间,当修改其中一个对象的属性 Address,另一个也会跟着变化。
浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。
深拷贝深拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。
Object 类提供的 clone 是只能实现 浅拷贝的。
如何实现深拷贝?①、让每个引用类型属性内部都重写clone() 方法
既然引用类型不能实现深拷贝,那么我们将每个引用类型都拆分为基本类型,分别进行浅拷贝。比如上面的例子,Person 类有一个引用类型 Address(其实String 也是引用类型,但是String类型有点特殊,后面会详细讲解),我们在 Address 类内部也重写 clone 方法。如下:
1 package com.ys.test;
2
3 public class Address implements Cloneable{
4 private String provices;
5 private String city;
6 public void setAddress(String provices,String city){
7 this.provices = provices;
8 this.city = city;
9 }
10 @Override
11 public String toString() {
12 return "Address [provices=" + provices + ", city=" + city + "]";
13 }
14 @Override
15 protected Object clone() throws CloneNotSupportedException {
16 return super.clone();
17 }
18
19 }
Person.class 的 clone() 方法:
1 @Override
2 protected Object clone() throws CloneNotSupportedException {
3 Person p = (Person) super.clone();
4 p.address = (Address) address.clone();
5 return p;
6 }
测试还是和上面一样,我们会发现更改了p2对象的Address属性,p1 对象的 Address 属性并没有变化。
但是这种做法有个弊端,这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。
//深度拷贝
public Object deepClone() throws Exception{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
因为序列化产生的是两个完全独立的对象,所有无论嵌套多少个引用类型,序列化都是能实现深拷贝的。