java-浅拷贝和深拷贝
1.拷贝的引入
(1).引用拷贝
例1:
点击查看代码
Teacher teacher = new Teacher("Taylor",26);
Teacher otherteacher = teacher;
System.out.println(teacher);
System.out.println(otherteacher);
点击查看代码
blog.Teacher@355da254
blog.Teacher@355da254
结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,他们都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。
例一图解:
(2)对象拷贝
创建对象本身的一个副本。
点击查看代码
Teacher teacher = new Teacher("Swift",26);
Teacher otherteacher = (Teacher)teacher.clone();
System.out.println(teacher);
System.out.println(otherteacher);
点击查看代码
blog.Teacher@355da254
blog.Teacher@4dc63996
结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
例二图解:
注:深拷贝和浅拷贝都是对象拷贝
2.浅拷贝
定义:浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象;
(1)基本数据类型:对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
例三:
点击查看代码
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
int arr2[] = arr.clone();
System.out.println(arr == arr2);//false
for (int i = 0; i < 10; i++) {
System.out.print(arr2[i] + "\t");
}
System.out.println();
arr[0] = 9;
System.out.println(arr2[0]);//0
输出结果:
点击查看代码
false
0 1 2 3 4 5 6 7 8 9
0
注:String类型通过常量赋值时相当于基本数据类型,通过new关键字创建对象时便是引用数据类型;
(2)引用数据类型:对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
例四(对象数组):
点击查看代码
public static void main(String[] args) {
Person[] arr = new Person[3];
Person[] arr1 = new Person[3];
arr1[0] = new Person("xl", 18);
arr1[1] = new Person("xi", 19);
arr1[2] = new Person("xk", 20);
Person[] arr2 = new Person[arr1.length];
for (int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}//将数组每个元素指向同一个地址,所以也赋值了
System.out.println(arr2[0].name);
System.out.println(arr1[0].name);//xl
arr2[0].name = "wxq";
System.out.println(arr1[0].name);//wxq
System.out.println(arr2[0].name);//wxq
System.out.println(arr1 == arr2);
Person[] arr3 = arr1.clone();
System.out.println(arr1 == arr3);
arr2[0].name = "xhz";//因为指向一个地址,arr2改, arr3也改了
System.out.println(arr3[0].name);//xhz
arr1[0].name = "hhhc";
System.out.println(arr3[0].name);//hhhc
}//引用传递
输出结果:
点击查看代码
xl
xl
wxq
wxq
false
false
xhz
hhhc
例五(对象调用的数组):
点击查看代码
class CloneExample implements Cloneable{
private int[] arr = new int[10];
public int[] getArr() {
return arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
public void showArr(){
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
}
public int get(int index){
return arr[index];
}
public void set(int index,int value) {
arr[index] = value;
}
@Override
public CloneExample clone() throws CloneNotSupportedException {
return (CloneExample) super.clone();
}
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
CloneExample e1 = new CloneExample();
CloneExample e2 = null;
//e1.getArr()
e1.showArr();
e2=e1.clone();
e1.set(0,5);//第0个元素赋值5
System.out.println(e2.get(0));//e2第0个元素也变成了5
e2 = e1.clone();
e1.set(4,555);//e1第四个元素赋值为555
System.out.println(e2.get(4)); //e2也变成了555
}
}
例六(对象引用做参数):
点击查看代码
public class ShallowCopy {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setName("Delacey");
teacher.setAge(29);
Student2 student1 = new Student2();
student1.setName("Dream");
student1.setAge(18);
student1.setTeacher(teacher);
Student2 student2 = (Student2) student1.clone();
System.out.println("拷贝后");
System.out.println(student2.getName());
System.out.println(student2.getAge());
System.out.println(student2.getTeacher().getName());
System.out.println(student2.getTeacher().getAge());
System.out.println("修改老师的信息后-------------");
// 修改老师的信息
teacher.setName("Jam");
System.out.println(student1.getTeacher().getName());
System.out.println(student2.getTeacher().getName());
}
}
class Teacher implements Cloneable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student2 implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
输出结果:
点击查看代码
拷贝后
Dream
18
Delacey
29
修改老师的信息后-------------
Jam
Jam
结果分析: 两个引用student1和student2指向不同的两个对象,但是两个引用student1和student2中的两个teacher引用指向的是同一个对象,所以说明是浅拷贝。
例六图解:
深拷贝
(1)定义:
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存;
简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
(2)重写clone方法实现深拷贝
例七(对象调用的数组):
点击查看代码
class CloneExample implements Cloneable{
private int[] arr = new int[10];
public CloneExample(){
for (int i = 0; i < 10; i++) {
arr[i] = i;
}//构造方法为数组赋值
}
public int get(int index){
return arr[index]; //调用可得到这个数组
}
public void set(int index,int value) {
arr[index] = value;//为数组某个元素赋值
}
@Override
public CloneExample clone() throws CloneNotSupportedException {
CloneExample result = (CloneExample) super.clone();
result.arr = new int[this.arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}//每个元素都经过clone方法
return result;
}
}
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
CloneExample e1 = new CloneExample();
CloneExample e2 = null;
e2 = e1.clone();
e1.set(4,555);
System.out.println(e2.get(4)); //4
}
}
例八:
点击查看代码
public class DeepCopy {
public static void main(String[] args) throws Exception {
Teacher2 teacher = new Teacher2();
teacher.setName("Delacey");
teacher.setAge(29);
Student3 student1 = new Student3();
student1.setName("Dream");
student1.setAge(18);
student1.setTeacher(teacher);
Student3 student2 = (Student3) student1.clone();
System.out.println("拷贝后");
System.out.println(student2.getName());
System.out.println(student2.getAge());
System.out.println(student2.getTeacher().getName());
System.out.println(student2.getTeacher().getAge());
System.out.println("修改老师的信息后-------------");
// 修改老师的信息
teacher.setName("Jam");
System.out.println(student1.getTeacher().getName());
System.out.println(student2.getTeacher().getName());
}
}
class Teacher2 implements Cloneable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student3 implements Cloneable {
private String name;
private int age;
private Teacher2 teacher;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher2 getTeacher() {
return teacher;
}
public void setTeacher(Teacher2 teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
// 浅复制时:
// Object object = super.clone();
// return object;
// 改为深复制:
Student3 student = (Student3) super.clone();
// 本来是浅复制,现在将Teacher对象复制一份并重新set进来
student.setTeacher((Teacher2) student.getTeacher().clone());
return student;
}
}
输出结果:
点击查看代码
拷贝后
Dream
18
Delacey
29
修改老师的信息后-------------
Jam
Delacey
例八图解1(teacher姓名Delacey更改前):
例八 图解2(teacher姓名Jam更改后):
(3)拷贝构造函数完成深拷贝(对象调用的数组)
点击查看代码
class CloneExample {
private int[] arr = new int[10];
public CloneExample() {
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
}
public CloneExample(CloneExample ce) {
arr = new int[ce.arr.length];
for (int i = 0; i < ce.arr.length; i++) {
arr[i] = ce.arr[i];
}
}
public int get(int index) {
return arr[index];
}
public void set(int index, int value) {
arr[index] = value;
}
}
public class CloneDemo {
public static void main(String[] args) {
CloneExample e1 = new CloneExample();
CloneExample e2 = new CloneExample(e1);
e1.set(4, 500);
System.out.println(e2.get(4)); //4
}
}
(4)利用序列化实现深拷贝(对象引用做参数)
例九:
点击查看代码
public class DeepCopyServiable {
public static void main(String[] args) throws Exception {
Teacher3 t = new Teacher3();
t.setName("Taylor");
t.setAge(28);
Student3 s1 = new Student3();
s1.setAge(20);
s1.setName("blank space");
s1.setTeacher(t);
Student3 s2 = (Student3) s1.deepClone();
System.out.println("拷贝后:");
System.out.println(s2.getName());
System.out.println(s2.getAge());
System.out.println(s2.getTeacher().getName());
System.out.println(s2.getTeacher().getAge());
System.out.println("---------------------------");
t.setName("swift");
System.out.println("修改后:");
System.out.println(s1.getTeacher().getName());
System.out.println(s2.getTeacher().getName());
}
}
class Teacher3 implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student3 implements Serializable {
private String name;
private int age;
private Teacher3 teacher;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher3 getTeacher() {
return teacher;
}
public void setTeacher(Teacher3 teacher) {
this.teacher = teacher;
}
public Object deepClone() throws Exception {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
输出结果:
点击查看代码
拷贝后:
blank space
20
Taylor
28
---------------------------
修改后:
swift
Taylor
和重写clone方法不同的是:1. 要创对象的类的接口变成了Serializable;
对于clone()方法个人的理解:
-
main函数一般需要修改:public static void main(String[] args) CloneNotSupportedException;
-
需要实现Cloneable接口才能调用和重写clone()方法;
-
clone方法不会用到构造函数;
-
测试方法和写属性的类是同一个的话,不重载方法也可以调用,但不是一个类的话,就需要重写方法,才能让创出来的对象调用;
-
未重载的clone方法:
点击查看代码
protected Object clone() throws CloneNotSupportedException {
return super.clone();//原本的clone方法
//Object object = super.clone();
//return object; //能实现同样的功能
}
//调用还需要类型强转
//Student2 student1 = new Student2();
//Student2 student2 = (Student2) student1.clone();
- 重载后的clone方法:
点击查看代码
public Student2 clone() throws CloneNotSupportedException {
return (Student2) super.clone();
}//改变了方法的返回值类型,和完成了类型的强制转换
Student2 student1 = new Student2();
Student2 student2 = student1.clone();
-
clone方法默认返回值是Object类对象,所以需要进行类型强制转换,一般是在
重写的方法中进行类型转换; -
java 的克隆为什么会抛出 CloneNotSupportedException异常?
Object声明的protected clone虽然可以阻止实现clone()的子类,在子类外部直接调用clone(),
但是对静态方法却没有办法,因为静态方法可以直接访问protected的方法,而这样就有可能会出现对对象使用clone(),
而实际对象不支持clone(),所以此时就会抛出这个异常。
总结:
-
深克隆浅克隆可以只修改clone方法;
-
clone方法既复杂又有风险,不仅会抛出异常,还需要进行类型转换,尽量不要去使用clone方法,可以使用拷贝构造函数来拷贝对象;
-
序列化除了改变方法,还有实现接口也要改变;
参考:
https://blog.csdn.net/baiye_xing/article/details/71788741?spm=1001.2014.3001.5506
https://www.cnblogs.com/genggeng/p/10065885.html
https://blog.csdn.net/Yellow_Star___/article/details/118337303?spm=1001.2014.3001.5506
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)