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

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重点知识学习--[对象克隆]相关的知识,希望对你有一定的参考价值。

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

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


为什么要克隆

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

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

结果;实际上仅引用复制

Useraccount='小智', age=22, password='666666'
Useraccount='小智', age=22, password='666666'
Useraccount='小智', 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());
    

结果

Useraccount='小智', age=22, password='666666'
Useraccount='小智', 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::Useraccount='小智', age=22, grade=Gradename='大四'
用户2::Useraccount='杰哥', age=22, grade=Gradename='大四'

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

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

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

测试结果

用户1::Useraccount='小智', age=22, grade=Gradename='大四'
用户2::Useraccount='杰哥', age=22, grade=Gradename='二年级'

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


序列化克隆的案例

班级类;
实现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;
    

以上是关于重点知识学习--[对象克隆]的主要内容,如果未能解决你的问题,请参考以下文章

Scratch高阶编程之克隆技术再研究

Unity核心对象和物理类

零基础入门学习java第十四节:Java对象的克隆

JS学习——深层克隆实现

JS学习——深层克隆实现

JS学习——深层克隆实现