使用场景
- 在实际的业务使用场景中我们会碰到复制对象的需求,例如财务场景中,系统之间集成,需要传凭证给财务系统,这里就会有借方数据和贷方数据,通常我们会将借方数据塞到一个对象中,贷方数据塞到一个对象中,在粘贴复制代码时你会发现,借方数据和贷方数据之间的差别可能就几个字段不同,代码写完交付了,项目经理突然提出要修改一个字段传的数据,去翻看代码时发现借方对象和贷方对象都有这个字段,需要改好几个地方。这个时候我们就想要是能复制对象就好了,这样我修改就只要修改一个对象,后面的全自动完成,减少代码的维护工作量。
三种复制方法
- 实现Cloneable接口,重写clone方法
- 序列化
- 使用BeanUtils工具类复制对象
代码示例
- 这里我按照三种方法的顺序附上相应的示例代码。
实现Cloneable接口,重写clone方法
- 创建一个Car类,此类实现了Cloneable接口,重写了clone方法
public class Car implements Cloneable {
private String brand;
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public Car clone() {
try {
return (Car) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
- 测试复制代码如下,先复制一个Car对象,再修改原来的对象,可以检测出是否真的复制成功了,而不是只有对象的引用
public class Test {
public static void main(String[] args){
Car car = new Car();
car.setBrand("奥迪A6");
car.setPrice(45);
Car cloneCar = car.clone();
car.setPrice(11);
System.out.println(cloneCar.toString());
}
}
序列化
- Java中常见的序列化方式是实现Serializable接口,示例代码如下
public class Car implements Serializable {
private String brand;
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
- 测试代码如下,主要思想是先将对象转成流,再将流转成对象,这么一进一出,对象已经不是原来的对象了,赋予了新的生命
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Car car = new Car();
car.setBrand("奥迪A6");
car.setPrice(45);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeObject(car);
byte[] bytes = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Car cloneCar = (Car) objectInputStream.readObject();
System.out.println(cloneCar.toString());
}
}
使用BeanUtils工具类复制对象
- 创建一个普通的类,这里不用继承什么类,也不用实现什么接口,就是最普通的类,随心所欲
public class Car{
private String brand;
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
- 测试代码如下,使用org.springframework.beans包下的BeanUtils的copyProperties方法就可以实现复制对象
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.setBrand("奥迪A6");
car.setPrice(45);
Car cloneCar = new Car();
BeanUtils.copyProperties(car, cloneCar);
System.out.println(cloneCar.toString());
}
}
总结
- 上述三种方法中,我在项目中常用到的是第一种和第三种方法,出于对代码效率上的好奇,我后来写了一个示例计算了下第一种方法和第三种方法的效率,使用第一种方法和第三种方法复制100万个对象出来,耗时如下,从下面可以看出clone的效率远远高于copyProperties,大家可以翻看源码,可以发现clone最终调用的是native方法,而copyProperties是采用的反射的方式。
方法名称 | 耗时(ms) |
重写clone()方法 | 45 |
使用BeanUtils.copyProperties方法 | 1671 |