Java如何实现对象克隆

将A对象的值分别通过set方法加入B对象中

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());
    System.out.println("学生2:  "+stu2.getNumber());

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

结果:

学生1: 12345

学生2: 12345

学生1: 12345

学生2: 54321

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

public class Student implements Cloneable {

  private int number;
  private Address add;

  //get、set方法

  @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());
    System.out.println("学生2:  " + stu2.getNumber() + ",地址:  " + stu2.getAdd().getAdd());

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

  }
}

class Address implements Cloneable {
  private String add;

  //get、set方法

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

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

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;
  }

  //get、set方法
    //重写toString方法  
}
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;
  }

  //get、set方法
  //重写toString方法
}
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 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();
  }
}

调用ByteArrayOutputStreamByteArrayInputStream对象的close方法没有任何意义。这两个基于内存的流只要垃圾收集器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放。

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);
    person1.getCar().setBrand("BYD");
  }
}

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

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

通过org.apache.commons中的工具类BeanUtilsPropertyUtils进行对象复制

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();
}
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使用更普遍一点,犯错的风险更低一点。

你可能感兴趣的:(Java基础)