zoukankan      html  css  js  c++  java
  • Java基础(十三)--深拷贝和浅拷贝

    在上篇文章:Java基础(十二)--clone()方法,我们简单介绍了clone()的使用

    clone()对于基本数据类型的拷贝是完全没问题的,但是如果是引用数据类型呢?

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Student implements Cloneable{
    
        private int id;
        private String name;
        private int sex;
        private Score score;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    } 
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Score {
    
        private int math;
        private int chinese;
    }
    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = (Student)student.clone();
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
    

    从结果上看,clone默认实现的是浅拷贝,并没有达到我们的预期。那么什么是深拷贝和浅拷贝?

    深拷贝:在浅拷贝的基础上,引用变量也进行了clone,并指向clone产生的新对象

    浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,但是对象引用属性仍然指向原来的对象

    clone()如何实现深拷贝?

    1、引用成员变量Score需要实现Cloneable接口,并且重写Object的Clone()

    2、自定义Student的Clone()

    @Override
    protected Object clone() throws CloneNotSupportedException {
    	Student student = (Student) super.clone();
    	Score score = student.getScore();
    	student.setScore((Score)score.clone());
    	return student;
    }

    结果:

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))
    

    结论:

      想要通过Clone()实现深拷贝,该对象必须要实现Cloneable接口,实现并且自定义clone(),把引用变量进行clone,然后引用变量对应的类也要实现Cloneable接口并且实现clone方法。

      假如这个对象有很多个引用变量,都要实现clone接口,并且重写clone(),而且该对象自定义clone(),真的不是太方便,我们还可以序列化来实现深拷贝。

    序列化实现深拷贝

    可以写一个序列化实现深拷贝的工具类,兼容所有对象。

    public class DeepCloneUtils {
    
        public static <T extends Serializable> T deepClone(T object) {
            T cloneObject = null;
            try {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
                objectOutputStream.writeObject(object);
                objectOutputStream.close();
    
                ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
                ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
                cloneObject = (T)objectInputStream.readObject();
                objectInputStream.close();
    
            }  catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return cloneObject;
        }
    }
    

    当前类和引用对象对应的类都要实现序列化

    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = (Student)DeepCloneUtils.deepClone(student);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))
    

    PS:

      把当前对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与源对象引用之间没有关联。

      序列化实现方式,省略了clone()内部自定义的过程,但是还是要实现序列化的(当前类及引用类)。

      现在有一个问题,如果这个引用对象是第三方jar包呢,我们如果让它实现Serializable和Cloneable接口,上述两种解决方案没法使用了,我们需要新的解决方案。

    以下通过第三方jar包实现对象拷贝,不需要实现Serializable和Cloneable接口:

    modelMapper、Spring中的BeanUtils、Commons-BeanUtils、cglib、orika等,那么哪些才是深拷贝?

    modelMapper实现对象拷贝:

    1、首先引用maven依赖

    <dependency>
      <groupId>org.modelmapper</groupId>
      <artifactId>modelmapper</artifactId>
      <version>1.1.0</version>
    </dependency>
    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	ModelMapper modelMapper = new ModelMapper();
    	Student student1 = new Student();
    	modelMapper.map(student, student1);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:证明ModelMapper实现的是浅拷贝。

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
    

    Spring中的BeanUtils实现对象拷贝:

    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = new Student();
    	BeanUtils.copyProperties(student, student1);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:Spring-BeanUtils实现的是浅拷贝。

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
    

    Commons-BeanUtils实现对象拷贝:

    1、首先引用maven依赖

    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.8.0</version>
    </dependency>
    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = new Student();
    	BeanUtils.copyProperties(student1, student);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:证明ModelMapper实现的是浅拷贝。

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
    

    Cglib实现对象拷贝:

    1、首先引用maven依赖

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>3.1</version>
    </dependency>
    public static void main(String[] args) throws Exception{
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = new Student();
    	BeanCopier beanCopier = BeanCopier.create(Student.class, Student.class, false);
    	beanCopier.copy(student, student1, null);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:Cglib实现的依然是浅拷贝,感觉很扎心啊。。。

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=99))
    

    PS:网上有说cglib实现自定义转换器可以实现深拷贝,但是我试验下来还是不能,各位可以试验一下,如果可以,请留言。。。

    orika实现对象拷贝:

    <dependency>
      <groupId>ma.glasnost.orika</groupId>
      <artifactId>orika-core</artifactId>
      <version>1.5.0</version>
    </dependency>
    </dependencies>
    public static void main(String[] args) throws Exception{
    	MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    	mapperFactory.classMap(Student.class, Student.class)
    			.byDefault()
    			.register();
    	ConverterFactory converterFactory = mapperFactory.getConverterFactory();
    	MapperFacade mapper = mapperFactory.getMapperFacade();
    
    	Student student = new Student(1001, "sam", 1, new Score(78, 91));
    	Student student1 = mapper.map(student, Student.class);
    	System.out.println(student == student1);
    
    	student1.getScore().setChinese(99);
    	System.out.println(student.toString());
    }
    

    结果:可以实现深拷贝

    false
    Student(id=1001, name=sam, sex=1, score=Score(math=78, chinese=91))

    参考:

    https://blog.csdn.net/54powerman/article/details/64920431?locationNum=6&fps=1

    https://blog.csdn.net/weixin_40581980/article/details/81388557

  • 相关阅读:
    Object的公用方法
    Java的特点
    Set集合
    Java语言的三大特性
    List集合
    Collection类
    HashSet
    Codeforces1141F2 Same Sum Blocks (Hard)
    Codeforce1176F Destroy it!
    jzoj 5348. 【NOIP2017提高A组模拟9.5】心灵治愈
  • 原文地址:https://www.cnblogs.com/huigelaile/p/11038629.html
Copyright © 2011-2022 走看看