Java中实现对象克隆的方法

方式一:使用setter方法

Student stu1 = new Student();  
stu1.setNumber(12345);  
Student stu2 = new Student();  
stu2.setNumber(stu1.getNumber());

方式二:实现Cloneable接口并重写Object类中的clone()方法

浅克隆: 当对象被复制时,只复制对象本身和其中包含的值类型成员变量,而引用类型成员对象并没有复制

public class Student implements Cloneable {
    private int number;
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }

    //实现clone接口
    @Override
    public Object clone() {
        Student stu = null;
        try {
            //浅克隆
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

    //测试克隆
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setNumber(12345);
        Student stu2 = (Student) stu1.clone();
        System.out.println("学生1:  "+stu1.getNumber() + " " + stu1.hashCode());
        System.out.println("学生2:  "+stu2.getNumber() + " " + stu2.hashCode());

        stu2.setNumber(54321);
        System.out.println("学生1:  "+stu1.getNumber() + " " + stu1.hashCode());
        System.out.println("学生2:  "+stu2.getNumber() + " " + stu2.hashCode());
    }
}

运行结果:

学生1:  12345 1580066828
学生2:  12345 491044090
学生1:  12345 1580066828
学生2:  54321 491044090

从程序的运行结果可以看出,学生2修改值类型变量(如int)number后,自身number值发生改变,但并不影响学生1

深克隆: 将原型对象的所有引用对象也复制一份给克隆对象

public class Student implements Cloneable {

    private int number;
    private Address add;

    public void setAdd(Address add) {
        this.add = add;
    }
    private Address getAdd(){
        return this.add;
    }
    public void setNumber(int number){
        this.number = number;
    }
    public int getNumber(){
        return this.number;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try {
            //浅复制
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        //深度复制
        stu.add = (Address) add.clone();
        return stu;
    }

    public static void main(String[] args) {
        Address add = new Address();
        add.setAdd("北京市");
        Student stu1 = new Student();
        stu1.setNumber(12345);
        stu1.setAdd(add);

        Student stu2 = (Student) stu1.clone();
        System.out.println("学生1:  " + stu1.getNumber() + ",地址:  " + stu1.getAdd().getAdd() + ", " + stu1.hashCode());
        System.out.println("学生2:  " + stu2.getNumber() + ",地址:  " + stu2.getAdd().getAdd() + ", " + stu2.hashCode());

        stu2.setNumber(54321);
        add.setAdd("杭州市");
        System.out.println("学生1:  " + stu1.getNumber() + ",地址:  " + stu1.getAdd().getAdd() + ", " + stu1.hashCode());
        System.out.println("学生2:  " + stu2.getNumber() + ",地址:  " + stu2.getAdd().getAdd() + ", " + stu1.hashCode());
    }
}

class Address implements Cloneable {
    public String add;

    @Override
    protected Object clone() {
        Address add = null;
        try {
            add = (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
    public String getAdd(){
        return this.add;
    }
}

运行结果:

学生1:  12345,地址:  北京市, 1580066828
学生2:  12345,地址:  北京市, 491044090
学生1:  12345,地址:  杭州市, 1580066828
学生2:  54321,地址:  北京市, 1580066828

当去掉上述代码中的stu.add = (Address) add.clone();时,运行结果如下。可以看出引用类型对象Address add并未复制到克隆对象中

学生1:  12345,地址:  北京市, 1580066828
学生2:  12345,地址:  北京市, 491044090
学生1:  12345,地址:  杭州市, 1580066828
学生2:  54321,地址:  杭州市, 1580066828

方式三:实现Serializable接口

通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
Car类

import java.io.Serializable;

public class Car implements Serializable {

    private static final long serialVersionUID = 6880783148707693267L;

    //品牌
    private String brand;
    //最高时速
    private int maxSpeed;

    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
}

Person类

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = 7592930394427200495L;

    //姓名
    private String name;
    //年龄
    private int age;
    //座驾
    private Car car;

    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

CloneUtil类

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class CloneUtil {
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
    }
}

CloneTest类

public class CloneTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person("lisi", 18, new Car("BMW", 300));
        Person person1 = CloneUtil.clone(person);
        System.out.println("person car before:" + person.getCar().getBrand() + " " + person.hashCode() + " " + person.getCar().hashCode());
        System.out.println("person1 car before:" + person1.getCar().getBrand() + " " + person1.hashCode() + " " + person1.getCar().hashCode());
        person1.getCar().setBrand("BYD");
        System.out.println("person car after:" + person.getCar().getBrand() + " " + person.hashCode() + " " + person.getCar().hashCode());
        System.out.println("person1 car after:" + person1.getCar().getBrand() + " " + person1.hashCode() + " " + person1.getCar().hashCode());
    }
}

运行结果

person car before:BMW 895328852 1304836502
person1 car before:BMW 824909230 122883338
person car after:BMW 895328852 1304836502
person1 car after:BYD 824909230 122883338

备注1: 修改克隆的Person 对象person1 关联的 汽车对象 的品牌属性,原来的Person对象person关联的汽车不会受到任何影响,因为在克隆Person对象时其关联的Car对象也被克隆了

备注2: 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过范型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译期完成的,不是在运行时抛出异常,这种方案明显优于使用Object类的clone方法克隆对象

方式四:通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制

使用BeanUtils

try {
  Student stu1 = new Student();
  stu1.setNumber(12345);
  Student stu2 = new Student();
  BeanUtils.copyProperties(stu2, stu1);
} catch (IllegalAccessException e) {
  e.printStackTrace();
} catch (InvocationTargetException e) {
  e.printStackTrace();
}

使用PropertyUtils

try {
  Student stu1 = new Student();
  stu1.setNumber(12345);
  Student stu2 = new Student();
  PropertyUtils.copyProperties(stu2, stu1);
  catch (NoSuchMethodException e) {
    e.printStackTrace();
  }

两者区别:
BeanUtils提供了类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行自动转换;而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍,犯错的风险更低

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值