Java深拷贝与浅拷贝

一、先了解一下JVM的存储结构吧~

Java数据类型主要分为基本类型和引用类型两大类,如下的代码是如何存储的呢?

int a = 10;
int b = 20;
String c = "hello";
String d = "world";

d = c;

基本类型和引用类型在JVM中的存储结构

二、浅拷贝

1、浅拷贝例子
package test;

public class Person implements Cloneable {
    private String name;
    private int age;
    private LikeInfo likeInfo;

    public Person(String name, int age, LikeInfo likeInfo) {
        this.name = name;
        this.age = age;
        this.likeInfo = likeInfo;
    }

    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 LikeInfo getLikeInfo() {
        return likeInfo;
    }

    public void setLikeInfo(LikeInfo likeInfo) {
        this.likeInfo = likeInfo;
    }

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

    private static class LikeInfo {
        private String book;
        private String song;

        public LikeInfo(String book, String song) {
            this.book = book;
            this.song = song;
        }

        public String getBook() {
            return book;
        }

        public void setBook(String book) {
            this.book = book;
        }

        public String getSong() {
            return song;
        }

        public void setSong(String song) {
            this.song = song;
        }

        @Override
        public String toString() {
            return "LikeInfo{" +
                    "book='" + book + '\'' +
                    ", song='" + song + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person("小李", 20, new LikeInfo("白夜行", "七里香"));
        Person p2 = (Person) p1.clone();
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());
        p1.setAge(25);
        p1.getLikeInfo().setSong("稻香");
        System.out.println(">>>>>>修改后");
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());
    }
}

输出:

p1:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
>>>>>>修改后
p1:Person{name='小李', age=25, likeInfo=LikeInfo{book='白夜行', song='稻香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='稻香'}}
2、浅拷贝例子解析

(1)、Person实现Cloneable 接口,使用clone()克隆方法时要注意类型转换以及抛出异常CloneNotSupportedException
(2)、测试案例先新建一个Person对象p1,并由p1拷贝出一个新对象p2,但是可以看到修改p1的LikeInfo属性后,p2属性也发生了变化,说明p1和p2的LikeInfo属性引用对象其实是同一个

3、浅拷贝结论

创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是基本类型,那么对该字段执行复制;如果该字段是引用类型的,则复制引用但不复制引用的对象。因此,引用类型对象在浅拷贝前后其实引用的是同一个对象,如对其进行修改,则浅拷贝前后的对象都发生变化。

三、深拷贝

1、修该为深拷贝
package test;

public class Person implements Cloneable {
    private String name;
    private int age;
    private LikeInfo likeInfo;

    public Person(String name, int age, LikeInfo likeInfo) {
        this.name = name;
        this.age = age;
        this.likeInfo = likeInfo;
    }

    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 LikeInfo getLikeInfo() {
        return likeInfo;
    }

    public void setLikeInfo(LikeInfo likeInfo) {
        this.likeInfo = likeInfo;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();
        p.setLikeInfo((LikeInfo) getLikeInfo().clone());
        return p;
    }

    private static class LikeInfo implements Cloneable{
        private String book;
        private String song;

        public LikeInfo(String book, String song) {
            this.book = book;
            this.song = song;
        }

        public String getBook() {
            return book;
        }

        public void setBook(String book) {
            this.book = book;
        }

        public String getSong() {
            return song;
        }

        public void setSong(String song) {
            this.song = song;
        }

        @Override
        public String toString() {
            return "LikeInfo{" +
                    "book='" + book + '\'' +
                    ", song='" + song + '\'' +
                    '}';
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person("小李", 20, new LikeInfo("白夜行", "七里香"));
        Person p2 = (Person) p1.clone();
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());
        p1.setAge(25);
        p1.getLikeInfo().setSong("稻香");
        System.out.println(">>>>>>修改后");
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());
    }
}

输出:

p1:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
>>>>>>修改后
p1:Person{name='小李', age=25, likeInfo=LikeInfo{book='白夜行', song='稻香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}

2、深拷贝例子解析

由浅拷贝的例子修改为深拷贝的方法:
(1)、引用类型的字段需要实现Cloneable接口
(2)、重写clone()方法,将引用类型字段也进行一次克隆

3、深拷贝结论

创建一个对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是基本类型还是引用类型,都复制独立的一份。当修改其中一个对象的任何内容时,都不会影响另一个对象的内容。

四、clone() 的替代方案

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。

package test;

public class Person {
    private String name;
    private int age;
    private LikeInfo likeInfo;

    public Person(Person source) {
        this.name = source.name;
        this.age = source.age;
        LikeInfo likeInfo = source.likeInfo;
        this.likeInfo = new LikeInfo(likeInfo.book, likeInfo.song);
    }

    public Person(String name, int age, LikeInfo likeInfo) {
        this.name = name;
        this.age = age;
        this.likeInfo = likeInfo;
    }

    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 LikeInfo getLikeInfo() {
        return likeInfo;
    }

    public void setLikeInfo(LikeInfo likeInfo) {
        this.likeInfo = likeInfo;
    }

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

    private static class LikeInfo {
        private String book;
        private String song;

        public LikeInfo(String book, String song) {
            this.book = book;
            this.song = song;
        }

        public String getBook() {
            return book;
        }

        public void setBook(String book) {
            this.book = book;
        }

        public String getSong() {
            return song;
        }

        public void setSong(String song) {
            this.song = song;
        }

        @Override
        public String toString() {
            return "LikeInfo{" +
                    "book='" + book + '\'' +
                    ", song='" + song + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        Person p1 = new Person("小李", 20, new LikeInfo("白夜行", "七里香"));
        Person p2 = new Person(p1);
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());

        p1.setAge(25);
        p1.getLikeInfo().setSong("稻香");
        System.out.println(">>>>>>修改后");
        System.out.println("p1:" + p1.toString());
        System.out.println("p2:" + p2.toString());
    }
}

输出:

p1:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
>>>>>>修改后
p1:Person{name='小李', age=25, likeInfo=LikeInfo{book='白夜行', song='稻香'}}
p2:Person{name='小李', age=20, likeInfo=LikeInfo{book='白夜行', song='七里香'}}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值