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

}


和浅拷贝相比只是改变了clone里面的方法;

输出结果:

点击查看代码
拷贝后
Dream
18
Delacey
29
修改老师的信息后-------------
Jam
Delacey

结果分析: 两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但对teacher对象的修改只能影响student1对象,所以是深拷贝;

例八图解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()方法个人的理解:

  1. main函数一般需要修改:public static void main(String[] args) CloneNotSupportedException;

  2. 需要实现Cloneable接口才能调用和重写clone()方法;

  3. clone方法不会用到构造函数;

  4. 测试方法和写属性的类是同一个的话,不重载方法也可以调用,但不是一个类的话,就需要重写方法,才能让创出来的对象调用;

  5. 未重载的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();
  1. 重载后的clone方法:
点击查看代码
public Student2 clone() throws CloneNotSupportedException {
       return (Student2) super.clone();
    }//改变了方法的返回值类型,和完成了类型的强制转换
    Student2 student1 = new Student2();
 Student2 student2 =  student1.clone();
  1. clone方法默认返回值是Object类对象,所以需要进行类型强制转换,一般是在
    重写的方法中进行类型转换;

  2. java 的克隆为什么会抛出 CloneNotSupportedException异常?
    Object声明的protected clone虽然可以阻止实现clone()的子类,在子类外部直接调用clone(),
    但是对静态方法却没有办法,因为静态方法可以直接访问protected的方法,而这样就有可能会出现对对象使用clone(),
    而实际对象不支持clone(),所以此时就会抛出这个异常。

总结:

  1. 深克隆浅克隆可以只修改clone方法;

  2. clone方法既复杂又有风险,不仅会抛出异常,还需要进行类型转换,尽量不要去使用clone方法,可以使用拷贝构造函数来拷贝对象;

  3. 序列化除了改变方法,还有实现接口也要改变;

参考:

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

posted @   hhxxlrkq  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示