使用场景

  • 在实际的业务使用场景中我们会碰到复制对象的需求,例如财务场景中,系统之间集成,需要传凭证给财务系统,这里就会有借方数据和贷方数据,通常我们会将借方数据塞到一个对象中,贷方数据塞到一个对象中,在粘贴复制代码时你会发现,借方数据和贷方数据之间的差别可能就几个字段不同,代码写完交付了,项目经理突然提出要修改一个字段传的数据,去翻看代码时发现借方对象和贷方对象都有这个字段,需要改好几个地方。这个时候我们就想要是能复制对象就好了,这样我修改就只要修改一个对象,后面的全自动完成,减少代码的维护工作量。

三种复制方法

  • 实现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