• Java - 对象克隆


    目录

    对象克隆

    浅克隆

    深克隆

    多层克隆


    对象克隆

    在讨论对象克隆之前, 可能会有人说 , 不能直接new一个吗?为什么要克隆

    首先关于这个问题, 直接new一个对象, 这个对象里面包含的成员变量是null的 , 那问题又来了, 我不能去一个个set对象的值吗? 当然可以, 但这样麻烦, 对象克隆采用的是native方法,效率更高

    那么该怎样去克隆呢? 克隆又分为浅克隆和深克隆

    首先这样的方式不叫克隆 (这种只能叫做引用复制):

    1. Object obj1 = new Object();
    2. Object obj2 = obj1;

    怎样区分是浅克隆还是深克隆呢?

            在java中, 数据类型分为基本类型和引用类型 , 在复制数据的过程中, 基本类型的值会直接被复制过去, 但是引用类型只能复制引用的地址 , 所以深浅克隆的区别就是是否将引用类型所指向的变量也复制了

    浅克隆

           先来看浅克隆 , 有两种实现方式, 一是重写Object类中的clone() 方法, 二是spring 框架中提供 BeanUtils.copyProperties(source,target); 这里我们演示第一种

    1. //需实现Cloneable接口
    2. public class Person implements Cloneable {
    3. int num;
    4. String name;
    5. public Person() { }
    6. public Person(int num, String name) {
    7. this.num = num;
    8. this.name = name;
    9. }
    10. //重写Object中的clone()方法, 实现克隆
    11. @Override
    12. protected Person clone() throws CloneNotSupportedException {
    13. Person person = (Person) super.clone();
    14. return person;
    15. }
    16. }
    1. public static void main(String[] args) throws CloneNotSupportedException {
    2. Person p1 = new Person(100,"jim");
    3. Person p2 = p1.clone(); //调用克隆方法
    4. System.out.println(p1==p2);
    5. }

           以上输出结果当然为 false , 因为克隆后虽然包含数据相同, 但仍旧是两个对象, 采用 == 的方式比较,  输出的是两个对象的引用地址是否相同 , 当然为 false

           当然在这里肯定有人会提出这样一个问题 : String不是引用类型吗? 怎么值跟着复制了

        首先我们回想String的特点, 引用类型没错, 但是String底层的char数组是采用了 final修饰的,  被final修饰的我们叫做常量, 也就是不可改变的, 所以说 , String虽然是引用类型, 但是它的值一旦确定就不能再改变了, 所以值会跟着复制

    深克隆

              深克隆会将引用类型所指向对象中包含的数据一同复制, 这里也需实现Cloneable接口

    先设计一个Address类

    1. //实现Cloneable接口
    2. public class Address implements Cloneable{
    3. String address;
    4. public String getAddress() {
    5. return address;
    6. }
    7. public void setAddress(String address) {
    8. this.address = address;
    9. }
    10. @Override
    11. public String toString() {
    12. return "Address{" +
    13. "address='" + address + '\'' +
    14. '}';
    15. }
    16. // 重写clone()方法
    17. @Override
    18. protected Address clone() throws CloneNotSupportedException {
    19. return (Address)super.clone();
    20. }
    21. }

    接着是person类

    1. //实现Cloneable接口
    2. public class Person implements Cloneable{
    3. int num;
    4. String name;
    5. //在Person中关联Address对象
    6. Address address;
    7. public Person() {
    8. }
    9. public Person(int num, String name) {
    10. this.num = num;
    11. this.name = name;
    12. }
    13. // 由于会导致篇幅过长, 此处省略属性的get和set方法
    14. //重写clone()方法实现深度克隆
    15. @Override
    16. protected Person clone() throws CloneNotSupportedException {
    17. Person person = (Person)super.clone();
    18. //深度复制 联同person中关联的对象也一同克隆
    19. person.address = (Address)address.clone();
    20. return person;
    21. }
    22. @Override
    23. public String toString() {
    24. return "Person{" +
    25. "num=" + num +
    26. ", name='" + name + '\'' +
    27. ", address=" + address +
    28. '}';
    29. }
    30. }

    test类 

    1. public class Test {
    2. public static void main(String[] args) throws CloneNotSupportedException {
    3. //先new一个Address类对象并设置值
    4. Address address = new Address();
    5. address.setAddress("成都");
    6. // new 一个Person类对象并设置值(将address属性加入)
    7. Person p1 = new Person(100,"jim");
    8. p1.setAddress(address);
    9. //克隆
    10. Person p2 =p1.clone();
    11. p2.setName("tom");
    12. //改变address所指向对象的值,如果深克隆成功,这个值即使改变也影响不了p2
    13. address.setAddress("西安");
    14. System.out.println("p1:"+p1);
    15. System.out.println("p2:"+p2);
    16. /*
    17. * 结果 : p1:Person{num=100, name='jim', address=Address{address='西安'}}
    18. * p2:Person{num=100, name='tom', address=Address{address='成都'}}
    19. * 深克隆成功
    20. * */
    21. }
    22. }

              这就是深克隆, 连同引用对象中包含的值一起复制, 在上述例子中 , 如果是浅克隆, 仅仅复制了引用, 引用一旦发生改变, 原对象和克隆的对象中的address属性都会发生改变(如果浅克隆上述例子p1和p2中的address都会为"西安")

    多层克隆

           了解了深克隆, 那么有人会想到, 如果对象中套对象, 对象中再套对象, 这样一直连环套该怎样解决呢 ?

           最为简单粗暴的一种方式就是 : 手动一层一层克隆, 这样当然也可以, 但是这里我们介绍一种更为简便的方式 : 序列化   , 当然简单的深克隆也是可以去使用序列化这种方式的

         序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作。

    Address类

    1. //实现Serializable接口
    2. public class Address implements Serializable {
    3. String address;
    4. public String getAddress() {
    5. return address;
    6. }
    7. public void setAddress(String address) {
    8. this.address = address;
    9. }
    10. @Override
    11. public String toString() {
    12. return "Address{" +
    13. "address='" + address + '\'' +
    14. '}';
    15. }
    16. }

    Person类

    1. //实现Serializable接口
    2. public class Person implements Serializable {
    3. int num;
    4. String name;
    5. Address address;
    6. public Person() {
    7. }
    8. public Person(int num, String name) {
    9. this.num = num;
    10. this.name = name;
    11. }
    12. //省略get和set方法
    13. //自定义克隆方法
    14. public Person myclone() {
    15. Person person = null;
    16. try {
    17. // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
    18. ByteArrayOutputStream baos = new ByteArrayOutputStream();
    19. ObjectOutputStream oos = new ObjectOutputStream(baos);
    20. oos.writeObject(this);
    21. // 将流序列化成对象
    22. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    23. ObjectInputStream ois = new ObjectInputStream(bais);
    24. person = (Person) ois.readObject();
    25. } catch (IOException e) {
    26. e.printStackTrace();
    27. } catch (ClassNotFoundException e) {
    28. e.printStackTrace();
    29. }
    30. return person;
    31. }
    32. @Override
    33. public String toString() {
    34. return "Person{" +
    35. "num=" + num +
    36. ", name='" + name + '\'' +
    37. ", address=" + address +
    38. '}';
    39. }
    40. }

    测试不变, 不过这里我们克隆使用的是自己定义的myClone()方法 ,使用流的方式完成

    关于对象克隆就说到这里, 感谢阅读. 

  • 相关阅读:
    搭建qemu RISC-V运行Linux环境
    【ARM】CCI集成指导整理
    MySQL表的增删改查
    RK3568 RTL8821cs适配 WPA3连接 与 WPA3热点配置
    HTML+CSS:动态搜索框
    大一学编程怎么学?刚接触编程怎么学习,有没有中文编程开发语言工具?
    【R语言】把文件夹下的所有文件提取到特定文件夹
    RESTful 分享
    Python基础语法
    UDP协议
  • 原文地址:https://blog.csdn.net/xx12321q/article/details/124822712