深拷贝与浅拷贝

在Java中,除了基本数据类型之外,还存在类的实例对象这个引用数据类型。而一般使用“=”号做赋值操作时,对于基本数据类型,实际上就是拷贝他的值,但对于对象来说,其实赋值的是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向同一个对象。

如果在拷贝这个对象时只对基本数据类型进行了拷贝而对引用数据类型只是进行了引用传递而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候创建一个新的对象并且复制其内的成员变量,则认为是深拷贝

clone会创建一个对象

https://blog.csdn.net/u014727260/article/details/55003402

clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象呢?

  1. 使用new操作符创建一个对象
  2. 使用clone方法复制一个对象
    那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。而clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部
引用的拷贝
//引用拷贝
    private static void copyReferenceObject(){
        Person p = new Person(23, "zhang");
        Person p1 = p;
        System.out.println(p);
        System.out.println(p1);
    }
    
这里打印的结果:
com.yaolong.clone.Person@3654919e
com.yaolong.clone.Person@3654919e
可以看到,打印的结果是一样的,也就是说,二者的引用是同一个对象,并没有创建出一个新的对象。
实现浅拷贝

将类实现Cloneable()接口,并且重写clone()方法。

public class Subject {

    private String name;
    public Subject(String s) {
        name = s;
    }

    public String getName() {
        return name;
    }

    public void setName(String s) {
        name = s;
    }
}

public class Student implements Cloneable {

    // 对象引用
    private Subject subj;
    private String name;

    public Student(String name, String subj) {
        this.name = name;
        this.subj = new Subject(subj);
    }

    public Subject getSubj() {
        return subj;
    }

    public String getName() {
        return name;
    }

    public void setName(String s) {
        name = s;
    }

    /**
     *  重写clone()方法
     * @return
     */
    public Object clone() {
        //浅拷贝
        try {
            // 直接调用父类的clone()方法 
            // super.clone()其实是浅拷贝
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

public class Test {
    public static void main(String[] args) {
        // 原始对象
        Student stud = new Student("杨充", "潇湘剑雨");
        System.out.println("原始对象: " + stud.getName() + " - " + stud.getSubj().getName());

        // 拷贝对象
        Student clonedStud = (Student) stud.clone();
        System.out.println("拷贝对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());

        // 原始对象和拷贝对象是否一样:
        System.out.println("原始对象和拷贝对象是否一样: " + (stud == clonedStud));
        // 原始对象和拷贝对象的name属性是否一样
        System.out.println("原始对象和拷贝对象的name属性是否一样: " + (stud.getName() == clonedStud.getName()));
        // 原始对象和拷贝对象的subj属性是否一样
        System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (stud.getSubj() == clonedStud.getSubj()));

        stud.setName("小杨逗比");
        stud.getSubj().setName("潇湘剑雨大侠");
        System.out.println("更新后的原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
        System.out.println("更新原始对象后的克隆对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());


    }
}
原始对象: 杨充 - 潇湘剑雨
拷贝对象: 杨充 - 潇湘剑雨
原始对象和拷贝对象是否一样: false
原始对象和拷贝对象的name属性是否一样: true
原始对象和拷贝对象的subj属性是否一样: true
更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
  • 对原始对象stud的"name"属性所做的改变并没有影响到拷贝对象clonedStud,这其实是因为String是不可变对象final,每次修改都 产生一个新的String对象。
  • 但是对引用对象subj的"name"属性所做的改变影响到了拷贝对象clonedStud。
实现深拷贝:
  1. 继续利用clone()方法,既然clone()方法是让我们来重写的,实际上我们可以对其内的引用类型的变量,在进行一次clone().
public class Student implements Cloneable { 
   // 对象引用 
   private Subject subj; 
   private String name; 
 
   public Student(String s, String sub) { 
      name = s; 
      subj = new Subject(sub); 
   } 
 
   public Subject getSubj() { 
      return subj; 
   } 
 
   public String getName() { 
      return name; 
   } 
 
   public void setName(String s) { 
      name = s; 
   } 
 
   /** 
    * 重写clone()方法 
    * 
    * @return 
    */ 
   public Object clone() { 
      // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
      Student s = new Student(name, subj.getName()); 
      return s; 
   } 
}

  • 因为它是深拷贝,所以你需要创建拷贝类的一个对象。因为在Student类中有对象引用,所以需要在Student类中实现Cloneable接口并且重写clone方法。
  1. 序列化(Serialization)这个对象,在反序列化回来,就可以得到这个新的对象,序列化的规则由我们自己来写。
public class Address {

    private String city;
    private String country;
}

public class User {

    private String name;
    private Address address;
}
@org.junit.Test
    public void serializableCopy() {

        Address address = new Address("杭州", "中国");

        User user = new User("大山", address);
        System.out.println("user: " + user);

        // 使用序列化进行深拷贝
        Gson gson = new Gson();
        User copyUser = gson.fromJson(gson.toJson(user), User.class);
        System.out.println("copyUser: " + copyUser);

        // 修改源对象的值
        user.getAddress().setCity("深圳");
        System.out.println("user: " + user);
        System.out.println("copyUser: " + copyUser);


    }

user: User{name='大山', address=Address{city='杭州', country='中国'}}
copyUser: User{name='大山', address=Address{city='杭州', country='中国'}}
user: User{name='大山', address=Address{city='深圳', country='中国'}}
copyUser: User{name='大山', address=Address{city='杭州', country='中国'}}

https://juejin.im/post/5c988a7ef265da6116246d11
https://juejin.im/entry/5bc3db04f265da0aaa053baa

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值