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
解释
-
int[] arr = new int[]{8, 1, 1, 9, 2};:- 创建一个
int类型的数组arr,并初始化为{8, 1, 1, 9, 2}。
- 创建一个
-
int[] brr = arr.clone();:- 使用
clone()方法创建arr的一个副本,并将其赋值给brr。 - 对于基本数据类型数组,
clone()方法会创建一个新的数组对象,并将原数组中的元素逐个复制到新数组中。 - 这意味着
arr和brr是两个不同的数组对象,但它们包含的元素是独立的。
- 使用
-
System.out.println(arr == brr);:- 比较
arr和brr是否是同一个对象(即比较它们的引用是否相同)。由于brr是通过clone()方法创建的,它是一个新的对象,因此arr == brr的结果是false。
- 比较
-
System.out.println(arr.getClass() == brr.getClass());:- 比较
arr和brr的类是否相同。由于arr和brr都是int[]类型的数组,它们的类是相同的,因此结果是true。
- 比较
-
System.out.println(arr.equals(brr));:- 对于数组类型,
equals()方法默认比较的是引用,而不是数组的内容。因此,arr.equals(brr)的结果是false,因为arr和brr是不同的对象。
- 对于数组类型,
-
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
}
}
解释
-
A[] arr = new A[2];:- 创建一个
A类型的数组arr,长度为 2。
- 创建一个
-
arr[1] = new A();:- 在
arr的第二个位置(索引为 1)创建一个新的A对象,并将其引用赋值给arr[1]。
- 在
-
A[] clone = arr.clone();:- 使用
clone()方法创建arr的一个副本,并将其赋值给clone。 - 对于引用数据类型数组,
clone()方法会创建一个新的数组对象,并将原数组中的引用逐个复制到新数组中。 - 这意味着
arr和clone是两个不同的数组对象,但它们包含的引用指向相同的对象。
- 使用
-
arr[1].value = 928151;:- 修改
arr[1]所指向的对象的value属性。
- 修改
-
clone[1].value = 985211;:- 修改
clone[1]所指向的对象的value属性。
- 修改
-
System.out.println(arr[1].value);:- 由于
arr[1]和clone[1]指向同一个对象,因此修改clone[1].value也会影响arr[1].value。最终输出是985211。
- 由于
-
System.out.println(clone[1].value);:- 输出是
985211,因为clone[1]指向的对象的value属性已经被修改。
- 输出是
-
System.out.println(arr);和System.out.println(clone);:- 这两行代码输出
arr和clone的内存地址。由于arr和clone是两个不同的数组对象,它们的内存地址不同。
- 这两行代码输出
-
System.out.println(arr == clone);:- 这行代码比较
arr和clone是否是同一个对象(即比较它们的引用是否相同)。由于clone是通过clone()方法创建的,它是一个新的对象,因此arr == clone的结果是false。
- 这行代码比较
-
System.out.println(arr.equals(clone));:- 对于数组类型,
equals()方法默认比较的是引用,而不是数组的内容。因此,arr.equals(clone)的结果是false,因为arr和clone是不同的对象。
- 对于数组类型,
-
System.out.println(arr.getClass() == clone.getClass());:- 这行代码比较
arr和clone的类是否相同。由于arr和clone都是A[]类型的数组,它们的类是相同的,因此结果是true。
- 这行代码比较
总结
-
基本数据类型数组的
clone()方法:- 创建一个新的数组对象,并将原数组中的元素逐个复制到新数组中。
- 这是浅克隆,因为元素是独立的。
-
引用数据类型数组的
clone()方法:- 创建一个新的数组对象,并将原数组中的引用逐个复制到新数组中。
- 这是浅克隆,因为引用指向相同的对象。
通过这些例子,你可以清楚地看到浅克隆的行为,以及它在基本数据类型数组和引用数据类型数组中的不同表现。
浙公网安备 33010602011771号