在Java中,如何实现对象的拷贝?

2024年 1月 29日 79.8k 0

在Java中,对象的拷贝可以分为浅拷贝(shallow copy)和深拷贝(deep copy)。

  • 「浅拷贝」:
    • 创建一个新对象,然后将原始对象中的非静态字段复制到新对象,如果字段是值类型,那么对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。
    • 在Java中,我们可以通过实现Cloneable接口并重写clone()方法来实现浅拷贝。需要注意的是,Object类中的clone()方法是受保护的,所以我们需要在我们的类中将其重写为public。
    • 另外,如果对象的字段也是需要拷贝的复杂对象,那么可能需要在这些类中也实现Cloneable接口并重写clone()方法。
  • 「深拷贝」:
    • 创建一个新对象,然后将原始对象中的非静态字段复制到新对象。如果字段是值类型,那么对该字段执行逐位复制。如果字段是引用类型,则递归地复制该字段引用的对象,而不是只复制引用。

    • 在Java中,深拷贝通常需要我们自己写代码来实现,因为Java并没有提供直接实现深拷贝的内置方法。

    • 深拷贝的一个常见实现方式是使用序列化。我们可以将对象写入到一个流中,然后再从流中读取出来,这样得到的就是原对象的一个深拷贝。但是这种方法有一些限制,比如被拷贝的对象以及它引用的所有对象都必须是可序列化的。

    注意:Cloneable接口和clone()方法的设计在Java社区中常常被认为是有缺陷的,因为它们有很多问题,比如Cloneable接口没有定义任何方法(它是一个标记接口),clone()方法的访问修饰符是protected,而且它使用的是浅拷贝,这可能会导致意外的对象共享问题。因此,在实际编程中,很多开发者更倾向于自己写代码来实现对象的拷贝,而不是使用Cloneable接口和clone()方法。

  • 通过实现Cloneable接口并重写clone()方法来实现浅拷贝
  • 我们定一个实体类People,实现了Cloneable接口,并且重写了clone()方法当然也是直接调用的父类的clone()方法。

    public class People implements Cloneable {
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        protected People clone() throws CloneNotSupportedException {
            return (People) super.clone();
        }
    
        @Override
        public String toString() {
            return "People{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    }

    测试拷贝:

    public class Main {
    
        public static void main(String[] args) {
            People people = new People();
            people.setName("Reathin");
            people.setAge(30);
    
            System.out.println("原People" + people.toString());
    
            try {
                People people1 = people.clone();
                System.out.println("拷贝People" + people1);
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
    
        }
    }

    输出结果如下,我们实现了一次浅拷贝。

    图片图片

  • 通过将原始对象中的非静态字段复制到新对象实现深拷贝
  • People people2 = new People();
    people2.setName(people.getName());
    people2.setAge(people.getAge());
    System.out.println("深拷贝对象1" + people2);
  • 通过序列化对象流实现深拷贝
  • // 序列化对象到字节数组
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(people);
    byte[] serializedData = byteArrayOutputStream.toByteArray();
    
    // 从字节数组中反序列化对象以创建深拷贝
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedData);
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    People people3 = (People) objectInputStream.readObject();
    System.out.println("深拷贝对象2" + people3);

    最终输出结果如下:

    图片图片

    完整示例代码:

    public class People implements Cloneable, Serializable {
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        protected People clone() throws CloneNotSupportedException {
            return (People) super.clone();
        }
    
        @Override
        public String toString() {
            return "People{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    }
    public class Main {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
            People people = new People();
            people.setName("Reathin");
            people.setAge(30);
            System.out.println("原People" + people.toString());
    
            People people1 = people.clone();
            System.out.println("浅拷贝People" + people1);
    
            //深拷贝方式一
            People people2 = new People();
            people2.setName(people.getName());
            people2.setAge(people.getAge());
            System.out.println("深拷贝对象1" + people2);
    
            //深拷贝方式2
            // 序列化对象到字节数组
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(people);
            byte[] serializedData = byteArrayOutputStream.toByteArray();
    
            // 从字节数组中反序列化对象以创建深拷贝
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedData);
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            People people3 = (People) objectInputStream.readObject();
            System.out.println("深拷贝对象2" + people3);
    
        }
    }

    开发中可以使用第三方库如Apache Commons Lang的SerializationUtils类或Google的Guava库来实现对象的深拷贝。这些库提供了更加灵活和方便的深拷贝实现方式,同时也提供了更多的自定义选项和错误处理机制。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论