重点知识学习(7)--[对象克隆]

2022/1/16 23:38:10

本文主要是介绍重点知识学习(7)--[对象克隆],对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

关于对象克隆这部分,
在之前学习原型模式的时候有整理过一部分;

尚硅谷设计模式学习(5)— [原型模式(Prototype模式),深拷贝与浅拷贝]

文章目录

    • 为什么要克隆
      • 浅克隆(ShallowClone)
      • 深克隆(DeepClone)


为什么要克隆

首先明白这样一个问题,它只是引用复制,而不是克隆

User user1 = new User();
User user2 = user1;
User user3 = user1;

注意这样不是克隆;
是引用,即对象在内存中的地址,a 和 b 对象仍然指向了
同一个对象。这种只能称为引用复制,两个引用指向的还是同一个对象

在这里插入图片描述

案例

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class User {
    private String  account;
    private Integer age;
    private String password;

    public User() {
    }

    public User(String account, Integer age, String password) {
        this.account = account;
        this.age = age;
        this.password = password;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public Integer getAge() {
        return age;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "account='" + account + '\'' +
                ", age=" + age +
                ", password='" + password + '\'' +
                '}';
    }
}

测试

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Test {
    public static void main(String[] args) {
        User user1 = new User("小智",22,"666666");
        User user2 = user1;
        User user3 = user1;

        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user3);

        System.out.println(user2 == user1);
        System.out.println(user3 == user2);
    }
}

结果;实际上仅引用复制

User{account='小智', age=22, password='666666'}
User{account='小智', age=22, password='666666'}
User{account='小智', age=22, password='666666'}
true
true

之前写练习项目的时候,用户类中要关联其他的类;也是比较复杂的;
然后我们无论是从前端向后端发来请求的处理时数据,还有后端向后端处理响应时的数据,我们都用一个用户模型类去封装信息;这样的话,可能就会出现数据冗余的问题;
比如说:我这个用户类呢,它里面的属性比较多,账号,密码,性别,年龄,电话,地址,积分,比如说我现在只是要请求到用户的部分的数据,它里面就把所有的数据都包含进去了,即使没有数据,里面有的属性里面会自动赋值null传递出去;

Java中数据类型分为值类型(基本数据类型)和引用类型

  • 值类型包括 int、double、byte、boolean、char 等简单数据类型,
  • 引用类型包括类、接口、数组等复杂类型。
  • 基本类型的值可以直接复制,引用类型只能复制引用地址。
  • 浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制

浅克隆(ShallowClone)

  • 浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;

  • 如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址

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

具体实现方式:

  • 通过重写 Object 类的clone()方法可以实现浅克隆。
  • 在 spring 框架中提供BeanUtils.copyProperties(source,target)

在这里插入图片描述

案例
实现Cloneable接口,重写clone方法试试

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class User implements  Cloneable{
     String  account;
     Integer age;
     String password;

    public User() {
    }

    public User(String account, Integer age, String password) {
        this.account = account;
        this.age = age;
        this.password = password;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public Integer getAge() {
        return age;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    protected User clone() throws CloneNotSupportedException {
        User user = (User)super.clone();
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "account='" + account + '\'' +
                ", age=" + age +
                ", password='" + password + '\'' +
                '}';
    }
}

测试

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        User user1 = new User("小智",22,"666666");
        User user2 = user1.clone();

        System.out.println(user1);
        System.out.println(user2);
        System.out.println("----------地址是否相等-------------");
        System.out.println(user2 == user1);
        System.out.println("----------hash值-------------");
        System.out.println(user1.hashCode());
        System.out.println(user2.hashCode());
    }
}

结果

User{account='小智', age=22, password='666666'}
User{account='小智', age=22, password='666666'}
----------地址是否相等-------------
false
----------hash值-------------
1265094477
2125039532

深克隆(DeepClone)

无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

  • 对象所包含的所有成员变量也将复制。
  • 如果需要实现深克隆,可以通过覆盖 Object 类的 clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

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

在这里插入图片描述

案例:
在用户类User中关联了引用类型的属性班级类Grade;

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Grade implements Cloneable{
    private String name;

    public Grade() {
    }

    public Grade(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Grade{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Grade clone() throws CloneNotSupportedException {
        return (Grade) super.clone();
    }
}

虽然,这个班级类已经重写了克隆方法;
但是,我在这用户类这边重写克隆方法时,仅克隆了引用类型Grade的地址

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class User implements Cloneable {
    String account;
    int age;
    //关联了班级类型;
    Grade grade;

    public User() {
    }

    public User(String account, int age) {
        this.account = account;
        this.age = age;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public int getAge() {
        return age;
    }

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

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "User{" +
                "account='" + account + '\'' +
                ", age=" + age +
                ", grade=" + grade +
                '}';
    }

    @Override
    protected User clone() throws CloneNotSupportedException {
        return (User)super.clone();
    }
}

测试

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //首先创建了一个班级类对象;
        Grade grade = new Grade();
        grade.setName("二年级");

        //用户1;
        User user1 = new User("小智",22);
        user1.setGrade(grade);

        //用户2;
        User user2 = user1.clone();
        user2.setAccount("杰哥");
        grade.setName("大四");

        System.out.println("用户1::"+user1);
        System.out.println("用户2::"+user2);

    }
}

用户2在克隆时,仅克隆了地址

用户1::User{account='小智', age=22, grade=Grade{name='大四'}}
用户2::User{account='杰哥', age=22, grade=Grade{name='大四'}}

浅克隆
如果一个对象中关联了其他的引用变量, 浅克隆时,只会将关联的对象的引用地址复制出来,并没有创建一个新的对象.

那么稍微改变一下用户类User中重写的克隆方法

@Override
    protected User clone() throws CloneNotSupportedException {
        User user = (User)super.clone();
        //深度复制;
        user.grade = grade.clone();
        return user;
    }

测试结果

用户1::User{account='小智', age=22, grade=Grade{name='大四'}}
用户2::User{account='杰哥', age=22, grade=Grade{name='二年级'}}

深克隆
如果一个对象中关联了其他的引用变量, 深克隆时,将此对象中所关联的对象也会进行克隆操作,也就是会创建一个新的关联对象


序列化克隆的案例

班级类;
实现Serializable接口

package com.xiaozhi.advanced.day07_objclone.objectClone.serializationclone;

import java.io.Serializable;

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Grade implements Serializable {
    private String name;

    public Grade() {
    }

    public Grade(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Grade{" +
                "name='" + name + '\'' +
                '}';
    }
}

用户类实现Serializable接口;
自定义克隆方法;

package com.xiaozhi.advanced.day07_objclone.objectClone.serializationclone;



import java.io.*;

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class User implements Serializable {
    String account;
    int age;
    //关联了班级类型;
    Grade grade;

    public User() {
    }

    public User(String account, int age) {
        this.account = account;
        this.age = age;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public int getAge() {
        return age;
    }

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

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "User{" +
                "account='" + account + '\'' +
                ", age=" + age +
                ", grade=" + grade +
                '}';
    }

    //自定义的克隆方法;
    public User customizeClone(){
        User user = null;
        try { 
           // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,
           //而原对象仍然存在于JVM中,利用此特性实现对象的深拷贝
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            user = (User) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return user;
    }
}

测试

/**
 * @author by 信计1801 李智青 学号:1809064012
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //首先创建了一个班级类对象;
        Grade grade = new Grade();
        grade.setName("二年级");

        //用户1;
        User user1 = new User("小智", 22);
        user1.setGrade(grade);

        //用户2;
        User user2 = user1.customizeClone();
        user2.setAccount("杰哥");
        grade.setName("大四");

        System.out.println("用户1::" + user1);
        System.out.println("用户2::" + user2);
    }
}

同样达到了深克隆的效果

用户1::User{account='小智', age=22, grade=Grade{name='大四'}}
用户2::User{account='杰哥', age=22, grade=Grade{name='二年级'}}



这篇关于重点知识学习(7)--[对象克隆]的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程