• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

奋斗的软件工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Java中的对象克隆

对象克隆

复制一个一模一样的新对象出来

浅克隆

拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的只是地址)

深克隆

  • 对象中基本类型的数据直接拷贝。

  • 对象中的字符串数据拷贝的还是地址。

  • 对象中包含的其他对象,不会拷贝地址,会创建新对象

import java.util.Arrays;

public class App {

    public static void main(String[] args) {

        // 创建一个整型数组
        int[] arr = new int[]{1, 2, 3, 4, 5};

        // 使用 Arrays.toString() 方法打印原始数组
        System.out.println("原始数组 (arr): " + Arrays.toString(arr));

        // 创建 arr 的引用副本(浅拷贝)
        int[] arrCopy1 = arr;

        // 使用 Arrays.toString() 方法打印引用副本
        System.out.println("引用副本 (arrCopy1): " + Arrays.toString(arrCopy1));

        // 使用循环创建 arr 的深度副本
        int[] arrCopy2 = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arrCopy2[i] = arr[i];
        }

        // 使用 Arrays.toString() 方法打印深度副本
        System.out.println("深度副本 (arrCopy2): " + Arrays.toString(arrCopy2));

        // **代码不涉及真正的克隆。**
        // 在 Java 中,克隆是指创建一个新的对象,该对象是原始对象的 **位对位副本**。
        // 但是,对于像整数这样的原始数据类型,赋值操作就已经实现了深度拷贝。
        // 对于对象(引用),深度拷贝需要逐个复制对象的字段。
    }
}

package com.aiit.itcq;

import java.util.Arrays;
import java.util.Objects;

/**
 * 应用程序入口类,演示对象克隆的概念和浅克隆的实现。
 */
public class App {
    /**
     * 主方法,用于运行对象克隆的演示。
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 创建原始对象
        Student s1 = new Student("007", "张三", 25, new double[]{85, 90, 85});

        // 打印原始对象信息
        System.out.println(s1.toString());

        try {
            // 使用浅克隆创建新对象
            Student s1_copy = (Student) s1.clone();

            // 打印克隆对象信息
            System.out.println(s1_copy.toString());

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 学生类,实现了Cloneable接口以支持对象克隆。
 */
class Student implements Cloneable {
    private String id;
    private String name;
    private int age;
    private double[] scores;

    /**
     * 无参构造方法
     */
    public Student() {
    }

    /**
     * 带参构造方法,用于初始化学生对象。
     *
     * @param id     学生ID
     * @param name   学生姓名
     * @param age    学生年龄
     * @param scores 学生成绩数组
     */
    public Student(String id, String name, int age, double[] scores) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.scores = scores;
    }

    // 省略getter和setter方法

    /**
     * 重写equals方法,用于比较对象是否相等。
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(id, student.id) &&
                Objects.equals(name, student.name) &&
                Arrays.equals(scores, student.scores);
    }

    /**
     * 重写hashCode方法,生成对象的哈希码。
     */
    @Override
    public int hashCode() {
        int result = Objects.hash(id, name, age);
        result = 31 * result + Arrays.hashCode(scores);
        return result;
    }

    /**
     * 重写toString方法,生成对象的字符串表示。
     */
    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", scores=" + Arrays.toString(scores) +
                '}';
    }

    /**
     * 克隆方法,实现浅克隆。
     *
     * @return 新的克隆对象
     * @throws CloneNotSupportedException 如果对象不支持克隆
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 克隆方法,实现深克隆。
     *
     * @return 新的深度克隆对象
     * @throws CloneNotSupportedException 如果对象不支持克隆
     */
//    @Override
//    protected Object clone() throws CloneNotSupportedException {
//        Student clonedStudent = (Student) super.clone();
//        clonedStudent.scores = this.scores.clone();  // 深克隆scores数组
//        return clonedStudent;
//    }
}

是的,上面的例子确实涉及到了深克隆和浅克隆的概念。让我们详细解释一下这两个概念,并结合代码示例来说明。

深克隆 vs 浅克隆

浅克隆(Shallow Clone)

  • 定义: 浅克隆只复制对象本身,而不复制对象引用的其他对象。
  • 特点:
    • 对于基本数据类型,浅克隆会复制其值。
    • 对于引用数据类型,浅克隆会复制引用,而不是引用指向的对象。
    • 因此,浅克隆后的对象和原对象共享引用指向的对象。

深克隆(Deep Clone)

  • 定义: 深克隆不仅复制对象本身,还复制对象引用的所有对象。
  • 特点:
    • 对于基本数据类型,深克隆会复制其值。
    • 对于引用数据类型,深克隆会递归地复制引用指向的对象。
    • 因此,深克隆后的对象和原对象不共享任何引用指向的对象。

代码示例

浅克隆示例

class A {
    int value = 1024;
}

public class Fpp {
    public static void main(String[] args) {
        A[] arr = new A[2];
        arr[1] = new A();

        A[] clone = arr.clone();

        arr[1].value = 928151;
        clone[1].value = 985211;

        System.out.println(arr[1].value); // 985211
        System.out.println(clone[1].value); // 985211

        System.out.println(arr);
        System.out.println(clone);

        System.out.println(arr == clone); // false
        System.out.println(arr.equals(clone)); // false
        System.out.println(arr.getClass() == clone.getClass()); // true
    }
}

在这个例子中,arr.clone() 是一个浅克隆。虽然 arr 和 clone 是两个不同的数组对象,但它们包含的引用指向相同的 A 对象。因此,修改 clone[1].value 也会影响 arr[1].value。

深克隆示例

要实现深克隆,通常需要手动递归地复制对象及其引用的所有对象。以下是一个简单的深克隆示例:

import java.io.*;

class A implements Serializable {
    int value = 1024;
}

public class Fpp {
    public static void main(String[] args) {
        A[] arr = new A[2];
        arr[1] = new A();

        A[] clone = deepClone(arr);

        arr[1].value = 928151;
        clone[1].value = 985211;

        System.out.println(arr[1].value); // 928151
        System.out.println(clone[1].value); // 985211

        System.out.println(arr);
        System.out.println(clone);

        System.out.println(arr == clone); // false
        System.out.println(arr.equals(clone)); // false
        System.out.println(arr.getClass() == clone.getClass()); // true
    }

    public static <T extends Serializable> T deepClone(T obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (T) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

在这个例子中,deepClone 方法使用 Java 的序列化机制来实现深克隆。通过序列化和反序列化对象,可以递归地复制对象及其引用的所有对象。因此,arr 和 clone 是两个不同的数组对象,且它们包含的引用指向不同的 A 对象。

总结

  • 浅克隆:

    • 只复制对象本身,不复制对象引用的其他对象。
    • 对于引用数据类型,浅克隆后的对象和原对象共享引用指向的对象。
  • 深克隆:

    • 不仅复制对象本身,还复制对象引用的所有对象。
    • 对于引用数据类型,深克隆后的对象和原对象不共享任何引用指向的对象。

通过这些例子,你可以清楚地看到浅克隆和深克隆的区别,以及它们在实际应用中的不同行为。

是的,你之前提供的代码示例都是浅克隆。让我们详细解释一下这两个例子,并确认它们确实是浅克隆。

基本数据类型数组的浅克隆

int[] arr = new int[]{8, 1, 1, 9, 2};
int[] brr = arr.clone();

System.out.println(arr == brr); // false
System.out.println(arr.getClass() == brr.getClass()); // true
System.out.println(arr.equals(brr)); // false

// 比较数组内容
System.out.println(Arrays.equals(arr, brr)); // true

解释

  1. int[] arr = new int[]{8, 1, 1, 9, 2};:

    • 创建一个 int 类型的数组 arr,并初始化为 {8, 1, 1, 9, 2}。
  2. int[] brr = arr.clone();:

    • 使用 clone() 方法创建 arr 的一个副本,并将其赋值给 brr。
    • 对于基本数据类型数组,clone() 方法会创建一个新的数组对象,并将原数组中的元素逐个复制到新数组中。
    • 这意味着 arr 和 brr 是两个不同的数组对象,但它们包含的元素是独立的。
  3. System.out.println(arr == brr);:

    • 比较 arr 和 brr 是否是同一个对象(即比较它们的引用是否相同)。由于 brr 是通过 clone() 方法创建的,它是一个新的对象,因此 arr == brr 的结果是 false。
  4. System.out.println(arr.getClass() == brr.getClass());:

    • 比较 arr 和 brr 的类是否相同。由于 arr 和 brr 都是 int[] 类型的数组,它们的类是相同的,因此结果是 true。
  5. System.out.println(arr.equals(brr));:

    • 对于数组类型,equals() 方法默认比较的是引用,而不是数组的内容。因此,arr.equals(brr) 的结果是 false,因为 arr 和 brr 是不同的对象。
  6. System.out.println(Arrays.equals(arr, brr));:

    • 使用 Arrays.equals(arr, brr) 方法比较数组的内容是否相同。由于 arr 和 brr 的内容相同,结果是 true。

引用数据类型数组的浅克隆

class A {
    int value = 1024;
}

public class Fpp {
    public static void main(String[] args) {
        A[] arr = new A[2];
        arr[1] = new A();

        A[] clone = arr.clone();

        arr[1].value = 928151;
        clone[1].value = 985211;

        System.out.println(arr[1].value); // 985211
        System.out.println(clone[1].value); // 985211

        System.out.println(arr);
        System.out.println(clone);

        System.out.println(arr == clone); // false
        System.out.println(arr.equals(clone)); // false
        System.out.println(arr.getClass() == clone.getClass()); // true
    }
}

解释

  1. A[] arr = new A[2];:

    • 创建一个 A 类型的数组 arr,长度为 2。
  2. arr[1] = new A();:

    • 在 arr 的第二个位置(索引为 1)创建一个新的 A 对象,并将其引用赋值给 arr[1]。
  3. A[] clone = arr.clone();:

    • 使用 clone() 方法创建 arr 的一个副本,并将其赋值给 clone。
    • 对于引用数据类型数组,clone() 方法会创建一个新的数组对象,并将原数组中的引用逐个复制到新数组中。
    • 这意味着 arr 和 clone 是两个不同的数组对象,但它们包含的引用指向相同的对象。
  4. arr[1].value = 928151;:

    • 修改 arr[1] 所指向的对象的 value 属性。
  5. clone[1].value = 985211;:

    • 修改 clone[1] 所指向的对象的 value 属性。
  6. System.out.println(arr[1].value);:

    • 由于 arr[1] 和 clone[1] 指向同一个对象,因此修改 clone[1].value 也会影响 arr[1].value。最终输出是 985211。
  7. System.out.println(clone[1].value);:

    • 输出是 985211,因为 clone[1] 指向的对象的 value 属性已经被修改。
  8. System.out.println(arr); 和 System.out.println(clone);:

    • 这两行代码输出 arr 和 clone 的内存地址。由于 arr 和 clone 是两个不同的数组对象,它们的内存地址不同。
  9. System.out.println(arr == clone);:

    • 这行代码比较 arr 和 clone 是否是同一个对象(即比较它们的引用是否相同)。由于 clone 是通过 clone() 方法创建的,它是一个新的对象,因此 arr == clone 的结果是 false。
  10. System.out.println(arr.equals(clone));:

    • 对于数组类型,equals() 方法默认比较的是引用,而不是数组的内容。因此,arr.equals(clone) 的结果是 false,因为 arr 和 clone 是不同的对象。
  11. System.out.println(arr.getClass() == clone.getClass());:

    • 这行代码比较 arr 和 clone 的类是否相同。由于 arr 和 clone 都是 A[] 类型的数组,它们的类是相同的,因此结果是 true。

总结

  • 基本数据类型数组的 clone() 方法:

    • 创建一个新的数组对象,并将原数组中的元素逐个复制到新数组中。
    • 这是浅克隆,因为元素是独立的。
  • 引用数据类型数组的 clone() 方法:

    • 创建一个新的数组对象,并将原数组中的引用逐个复制到新数组中。
    • 这是浅克隆,因为引用指向相同的对象。

通过这些例子,你可以清楚地看到浅克隆的行为,以及它在基本数据类型数组和引用数据类型数组中的不同表现。

posted on 2024-03-05 23:23  周政然  阅读(89)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3