关于Java的浅拷贝,深拷贝

在写软件构造实验Lab4的过程中,我遇到了一个很大的问题,那就是对于List中的List.get()方法产生了错误的理解,我一直以为get()方法是返回一个List中该位置处对象的一个完全一样的新对象,而实际上观察java的源码后发现,get()方法返回的实际上就是该原来对象的一个引用,因此如果对get后的对象进行了修改,那么原来的这段空间的对象也会被修改。
Lab4的实验中,由于给某个Entry加入某资源或者某位置需要判断是否存在资源独占冲突或者是位置独占冲突,本人开始的思路就是创建一个和原Entry集合一样的List集合,然后将需要加入资源或位置的Entry放入其中,在调用定义的相关方法进行判断是否存在冲突,如果不存在冲突,就将该加入位置或资源的新Entry加入原Entry集合中。
但是在实践中,我创建一个和原Entry集合一样的List集合的具体实现是:原集合为flightlist

 List<FlightEntry<Flight>> tempflightlist=new ArrayList<>(flightlist);

我原本以为这样产生的新的tempflight集合就是原集合的一个完全内容相同,但是指向内存空间不同的对象,然后我通过

FlightEntry<Flight> tempentry=flightlist.get(i)

的方法取得了一个需要进行加入位置或者资源的Entry,再进行是否位置冲突的判断,最后进行操作。
但就是在这我出现了错误。其实不管是new ArrayList<>(flightlist)还是flightlist.get(i)其实获得的都是原对象的引用,指向的还是原对象的内存空间,对新对象的改变或引起原对象的改变,所以就造成了我后来的错误。因此,通过查询相关资料,我了解到可以通过实现克隆的方式来完成相关对象除了内容的复制,还有创建一个新的内存空间来储存这些内容,即完成产生一个新的复制对象,对复制对象的改变不会影响原对象的内容。
1.什么是"克隆"?
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。
Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
2.深拷贝与浅拷贝(深拷贝才是真正的完全拷贝)
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝,下面我们着重谈一下深拷贝。运行下面的程序,看一看浅拷贝:

 class Professor0 implements Cloneable {
    String name;
    int age;
    Professor0(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student0 implements Cloneable {
    String name;// 常量对象。
    int age;
    Professor0 p;// 学生1和学生2的引用值都是一样的。
    Student0(String name, int age, Professor0 p) {
        this.name = name;
        this.age = age;
        this.p = p;
    }
    public Object clone() {
        Student0 o = null;
        try {
            o = (Student0) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }
}
public class ShallowCopy {
    public static void main(String[] args) {
        Professor0 p = new Professor0("wangwu", 50);
        Student0 s1 = new Student0("zhangsan", 18, p);
        Student0 s2 = (Student0) s1.clone();
        s2.p.name = "lisi";
        s2.p.age = 30;
        s2.name = "z";
        s2.age = 45;
        System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age);// 学生1的教授
    }
}

运行结果:
在这里插入图片描述
s2变了,但s1也变了,证明s1的p和s2的p指向的是同一个对象。这在我们有的实际需求中,却不是这样,因而我们需要深拷贝:

class Professor implements Cloneable {
    String name;
    int age;
    Professor(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }
}
class Student implements Cloneable {
    String name;
    int age;
    Professor p;

    Student(String name, int age, Professor p) {
        this.name = name;
        this.age = age;
        this.p = p;
    }

    public Object clone() {
        Student o = null;
        try {
            o = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        o.p = (Professor) p.clone();
        return o;
    }
}
public class DeepCopy {
    public static void main(String args[]) {
        long t1 = System.currentTimeMillis();
        Professor p = new Professor("wangwu", 50);
        Student s1 = new Student("zhangsan", 18, p);
        Student s2 = (Student) s1.clone();
        s2.p.name = "lisi";
        s2.p.age = 30;
        System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age);// 学生1的教授不改变。
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1);
    }
}

在这里插入图片描述
这样就完成了对对象的深拷贝,即使改变拷贝对象的内容也不会对原对象产生影响。
在本次实验中,我通过深拷贝解决了原来存在的问题,在需要拷贝的类中:

public class CourseEntry<Teacher> extends CommonPlanningEntry implements Cloneable
......
......
......
 @Override
	  public CourseEntry<Teacher> clone() { 
		CourseEntry<Teacher> stu = null; 
	    try{ 
	      stu = (CourseEntry<Teacher>)super.clone(); 
	    }catch(CloneNotSupportedException e) { 
	      e.printStackTrace(); 
	    } 
	    stu.a=(OneLocationEntryImpl)a.clone();
	    stu.b=(OneDistinguishResourceEntryImpl<Teacher>)b.clone();
	    stu.c=(NoBlockableEntryImpl)c.clone();
	    return stu; 
	  } 

在该类字段的对象的类中:

```java
public class OneLocationEntryImpl implements Cloneable
......
......
......
 @Override
	  public OneLocationEntryImpl clone() { 
		 OneLocationEntryImpl stu = null; 
	    try{ 
	      stu = (OneLocationEntryImpl)super.clone(); 
	    }catch(CloneNotSupportedException e) { 
	      e.printStackTrace(); 
	    } 
	    return stu; 
	  } 

其他几个字段同理。
然后在App类中通过调用clone则完成了对象的深拷贝:

List<FlightEntry<Flight>> tempflightlist=new ArrayList<>();
					for(int oo=0;oo<flightlist.size();oo++) {
						tempflightlist.add(flightlist.get(oo).clone());
					}
					FlightEntry<Flight> tempentry=flightlist.get(i).clone();

这样就完成了目标。
参考文章

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值