深拷贝&浅拷贝 & 它们的实现

深拷贝&浅拷贝

浅拷贝
  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的该成员变量都指向同一个实例。

在这里插入图片描述

深拷贝
  • 对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象图进行拷贝。

在这里插入图片描述


实现

浅拷贝
  • 通过拷贝构造方法实现浅拷贝:通过修改构造方法来进行拷贝
    class Person{
        //两个属性值:分别代表值传递和引用传递
        private Age age;
        private String name;
        public Person(Age age,String name) {
            this.age=age;
            this.name=name;
        }
        //拷贝构造方法
        public Person(Person p) {
            this.name=p.name;
            this.age=p.age;
        }
        public void setName(String name) {
            this.name=name;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    class Age{
        private int age;
        public Age(int age) {
            this.age=age;
        }
        public void setAge(int age) {
            this.age=age;
        }

        @Override
        public String toString() {
            return "Age{" +
                    "age=" + age +
                    '}';
        }
    }

    public static void main(String[] args) {
        Copy copy = new Copy();
        Age age = copy.new Age(20);
        Person p1=copy.new Person(age,"AAAAA"); //源数据
        Person p2=copy.new Person(p1); //拷贝数据
        System.out.println("p1 = "+p1);
        System.out.println("p2 = "+p2);
        //修改p1的各属性值,观察p2的各属性值是否跟随变化
        p1.setName("BBBBB");
        age.setAge(99);
        System.out.println("修改后的p1 = "+p1);
        System.out.println("修改后的p2 = "+p2);
    }
/**
输出:
p1 = Person{age=Age{age=20}, name='AAAAA'}
p2 = Person{age=Age{age=20}, name='AAAAA'}
修改后的p1 = Person{age=Age{age=99}, name='BBBBB'}
修改后的p2 = Person{age=Age{age=99}, name='AAAAA'}
*/

  • 通过重写clone()方法进行浅拷贝:Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。
    • Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。
    • 使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。
    /**
     * 创建年龄类
     */
    class Age implements Cloneable{
        //年龄类的成员变量(属性)
        private int age;
        //构造方法
        public Age(int age) {
            this.age=age;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return this.age+"";
        }
    }
    /**
     * 创建学生类
     */
    class Student implements Cloneable {
        //学生类的成员变量(属性),其中一个属性为类的对象
        private String name;
        private Age aage;
        private int length;

        //构造方法,其中一个参数为另一个类的对象
        public Student(String name, Age a, int length) {
            this.name = name;
            this.aage = a;
            this.length = length;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Age getaAge() {
            return this.aage;
        }

        public void setaAge(Age age) {
            this.aage = age;
        }

        public int getLength() {
            return this.length;
        }

        public void setLength(int length) {
            this.length = length;
        }

        //设置输出的字符串形式
        @Override
        public String toString() {
            return "姓名是: " + this.getName() + ", 年龄为: " + this.getaAge().toString() + ", 长度是: " + this.getLength();
        }

        //重写Object类的clone方法
        @Override
        public Object clone() {
            Object obj = null;
            //调用Object类的clone方法,返回一个Object实例
            try {
                obj = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return obj;
        }
    }

    public static void main(String[] args) {
        CopyDemo02 copyDemo02 = new CopyDemo02();
        Age a = copyDemo02.new Age(20);
        Student stu1 = copyDemo02.new Student("AAAAA",a,123);

        //通过调用重写后的clone方法进行浅拷贝
        Student stu2=(Student)stu1.clone();
        System.out.println("stu1 = " + stu1.toString());
        System.out.println("stu2 = " + stu2.toString());

        //尝试修改stu1中的各属性,观察stu2的属性有没有变化
        stu1.setName("BBBBB");
        //改变age这个引用类型的成员变量的值
        a.setAge(99);
        //stu1.setaAge(new Age(99));    使用这种方式修改age属性值的话,stu2是不会跟着改变的。因为创建了一个新的Age类对象而不是改变原对象的实例值
        stu1.setLength(321);
        System.out.println("修改后: stu1 = " + stu1.toString());
        System.out.println("修改后: stu2 = " + stu2.toString());
    }
/**
stu1 = 姓名是: AAAAA, 年龄为: 200, 长度是: 123
stu2 = 姓名是: AAAAA, 年龄为: 200, 长度是: 123
修改后: stu1 = 姓名是: BBBBB, 年龄为: 100, 长度是: 321
修改后: stu2 = 姓名是: AAAAA, 年龄为: 100, 长度是: 123
*/

深拷贝
  • 通过重写clone方法来实现深拷贝:与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Cloneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝=深拷贝。
    /**
     * 创建年龄类
     */
    class Age implements Cloneable{
        //年龄类的成员变量(属性)
        private int age;
        //构造方法
        public Age(int age) {
            this.age=age;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public Object clone() {
            Object obj=null;
            try {
                obj=super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return obj;
        }

        @Override
        public String toString() {
            return this.age+"";
        }
    }
    /**
     * 创建学生类
     */
    class Student implements Cloneable {
        //学生类的成员变量(属性),其中一个属性为类的对象
        private String name;
        private Age aage;
        private int length;

        //构造方法,其中一个参数为另一个类的对象
        public Student(String name, Age a, int length) {
            this.name = name;
            this.aage = a;
            this.length = length;
        }

        //eclipe中alt+shift+s自动添加所有的set和get方法
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Age getaAge() {
            return this.aage;
        }

        public void setaAge(Age age) {
            this.aage = age;
        }

        public int getLength() {
            return this.length;
        }

        public void setLength(int length) {
            this.length = length;
        }

        //设置输出的字符串形式
        @Override
        public String toString() {
            return "姓名是: " + this.getName() + ", 年龄为: " + this.getaAge().toString() + ", 长度是: " + this.getLength();
        }

        //重写Object类的clone方法
        @Override
        public Object clone() {
            Object obj=null;
            //调用Object类的clone方法——浅拷贝
            try {
                obj= super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            //调用Age类的clone方法进行深拷贝
            //先将obj转化为学生类实例
            Student stu=(Student)obj;
            //学生类实例的Age对象属性,调用其clone方法进行拷贝
            stu.aage=(Age)stu.getaAge().clone();
            return obj;
        }
    }

    /* 层次调用clone方法实现深拷贝 */
    public static void main(String[] args) {
        CopyDemo03 copyDemo03 = new CopyDemo03();
        Age a = copyDemo03.new Age(100);
        Student stu1 = copyDemo03.new Student("AAAAA",a,100);

        //通过调用重写后的clone方法进行浅拷贝
        Student stu2=(Student)stu1.clone();
        System.out.println("stu1 = " + stu1.toString());
        System.out.println("stu2 = " + stu2.toString());

        //尝试修改stu1中的各属性,观察stu2的属性有没有变化
        stu1.setName("BBBBB");
        //改变age这个引用类型的成员变量的值
        a.setAge(200);
        //stu1.setaAge(new Age(99));    使用这种方式修改age属性值的话,stu2是不会跟着改变的。因为创建了一个新的Age类对象而不是改变原对象的实例值
        stu1.setLength(200);
        System.out.println("stu1 = " + stu1.toString());
        System.out.println("stu2 = " + stu2.toString());
    }
/**
stu1 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
stu2 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
stu1 = 姓名是: BBBBB, 年龄为: 200, 长度是: 200
stu2 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
*/

  • 通过对象序列化实现深拷贝:虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。
    /**
     * 创建年龄类
     */
    class Age implements Serializable {
        //年龄类的成员变量(属性)
        private int age;
        //构造方法
        public Age(int age) {
            this.age=age;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return this.age+"";
        }
    }
    /**
     * 创建学生类
     */
    class Student implements Serializable{
        //学生类的成员变量(属性),其中一个属性为类的对象
        private String name;
        private Age aage;
        private int length;
        //构造方法,其中一个参数为另一个类的对象
        public Student(String name,Age a,int length) {
            this.name=name;
            this.aage=a;
            this.length=length;
        }
        //eclipe中alt+shift+s自动添加所有的set和get方法
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Age getaAge() {
            return this.aage;
        }

        public void setaAge(Age age) {
            this.aage=age;
        }

        public int getLength() {
            return this.length;
        }

        public void setLength(int length) {
            this.length=length;
        }
        //设置输出的字符串形式
        @Override
        public String toString() {
            return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException  {
        CopyDemo04 copyDemo04 = new CopyDemo04();
        Age a = copyDemo04.new Age(100);
        Student stu1 = copyDemo04.new Student("AAAAA",a,100);
        //通过序列化方法实现深拷贝
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(stu1);
        oos.flush();
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Student stu2=(Student)ois.readObject();
        System.out.println("stu1 = " + stu1.toString());
        System.out.println("stu2 = " + stu2.toString());
        //尝试修改stu1中的各属性,观察stu2的属性有没有变化
        stu1.setName("BBBBB");
        //改变age这个引用类型的成员变量的值
        a.setAge(200);
        stu1.setLength(200);
        System.out.println("stu1 = " + stu1.toString());
        System.out.println("stu2 = " + stu2.toString());
    }
/**
stu1 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
stu2 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
stu1 = 姓名是: BBBBB, 年龄为: 200, 长度是: 200
stu2 = 姓名是: AAAAA, 年龄为: 100, 长度是: 100
*/

总结

  • 浅拷贝和深拷贝的概念

  • 2 种浅拷贝方法 和 2种深拷贝方法

  • 其实我们要创建对象有五种方法

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值