JAVA关于引用,浅拷贝,深拷贝你一定要知道的!

1,074 阅读2分钟

JAVA中我们给对象赋值,都是这样的:

StringBuilder sb=new StringBuilder("abc");

这样我们新建的对象sb就持有了一个引用,这个引用指向内存中存放着abc的位置

StringBuilder sb2=sb;

我们也可以这样来使对象持有一个已有的引用,这两个对象会指向同一个地址空间,

我们对这两个对象任意一个的修改都会导致另一个对象的数据发生变化。


这样的话当我们想设计一个不可变类时,因为引用就会导致我们所设计类成员的变化,比如:

public class TestRerfer {  
  //我们希望成员变量sb是不可变的
  private  StringBuilder sb;    
  public TestRerfer(StringBuilder sb){
      this.sb=sb
  }

public static void main(String[] args) {    
     StringBuilder sb=new StringBuilder("abc");    
     TestRerfer tr=new TestRerfer(sb);    
     tr.print();    
     sb.append("abc");    
     tr.print(); }}



如果我们想要实现一个不可变类,让成员变量仅持有值而不持有引用,也就是说在某种操作后我们再进行

StringBuilder sb2=sb;

时,sb2.equals(sb)成立,而sb2==sb不成立(1)。

这种操作我们称为浅拷贝

要满足(1)的成立,显然sb2与sb应指向不同的地址空间,我们可以借助clone方法,所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象,这样就解决了指向两个对象的问题。

public class TestRerfer {  
  //我们希望成员变量sb是不可变的
  private  StringBuilder sb;    
  public TestRerfer(StringBuilder sb){
      this.sb=sb.clone();
  }
  public StringBulider get(){
      return sb.clone();
  }
public static void main(String[] args) {    
     StringBuilder sb=new StringBuilder("abc");    
     TestRerfer tr=new TestRerfer(sb);    
     tr.print();    
     sb.append("abc");    
     tr.print(); }}

但浅拷贝也存在问题,它只拷贝了当前对象,并不会拷贝当前对象的成员变量。上述代码并看不出这个问题,因为我们之前是想实现一个不可变类,我们直接拷贝了成员变量,我们把代码修改一下看

public class TestRefer2 implements Cloneable
{    private  StringBuilder sb; 
     private  String name;    
     public TestRefer2(StringBuilder sb,String str){       
          this.sb=sb;       
           this.name=str;    
     }    
     public String get(){        
           return sb.toString();    
    }    
    public void set_sb(String priex){        
          sb.append(priex);    
     }   
     public  void print(){       
          System.out.println(sb.toString()+" name:"+name);    
     }    
     public static void main(String[] args) throws CloneNotSupportedException {       
         StringBuilder sb=new StringBuilder("abc");        
         TestRefer2 tr=new TestRefer2(sb,"bob");        
         tr.print();        
         TestRefer2 tr2=(TestRefer2)tr.clone();      
         tr2.print();        
         tr2.set_sb("???");      
         tr.print();    
    }
}


可以看出,成员变量StringBulider的值任然发生了变化。


这可咋整呢!于是乎我们就要明白什么是深拷贝,经过上面的例子浅拷贝缺点很明确了:它只拷贝了当前对象,并不会拷贝当前对象的成员变量。深拷贝就是我啥都拷。

怎么实现呢?我们想想平时爱用的ctrl+c和ctrl+v,我们能不能ctrl+c一下对象,然后再粘过去!是不是就完事了?ctrl+c咋实现呢,序列化啊,那ctrl+v自然就是反序列化。

StringBuilder sb=new StringBuilder("abc");
TestRefer2 tr=new TestRefer2(sb,"bob");
tr.print();
ByteOutputStream bots=new ByteOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bots);
oos.writeObject(tr);
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bots.toByteArray()));
TestRefer2 tr2 = (TestRefer2) ois.readObject();
tr2.print();tr2.set_sb("hello");
tr.print();


齐活了!