分享

Java浅拷贝与深拷贝

 javaxiaop 2010-10-20

如果学过C++的应该对这个内容都有了解过

应该没一本C++的教材在说指针的时候都会说这个问题

 

为什么要拷贝?

拷贝,在Java里面叫Clone,克隆,是Object类里面就具备实现了的

其实有蛮多人觉得拷贝干嘛?

直接new一个不就得咯!

原因有以下几个:

1)效率,一个拷贝相当于“赋值”,而一个new就是执行构造函数,

如果值很简单(假如是一个int),而得到值很复杂(需要掉其它函数什么的),那么拷贝的效率比构造

高得多。(其实拷贝的实现是:如果是一般类型,就逐位(byte)复制,如果是复制类型,则复制其引用。

这样的拷贝时浅拷贝,因为引用类型,拷贝后仍指向同一个空间)

2)状态,一个拷贝,得到的是一个对象运行时候的状态!而不是初始值。

假如设计个游戏,主角打着打着(代表不是初始状态)中毒了,那么程序的逻辑是要每回合判断

是否中毒 ?然后减HP ? 更好的办法是,设计一个中毒的主角类,这个类每回合自动减HP,当主角

中毒的时候,只要把原来的状态,拷贝到中毒的主角类对象就可以了!这个就是得到对象运行时状态

的好处。

废话又多了,看代码吧,还是那个PSP的例子:

public class PSP implements Cloneable{
 private Version v;
 private String name;
 
 public PSP(Version v,String name){
  this.v = v;
  this.name = name;
 }
 
 public Version getV() {
  return v;
 }
 public void setV(Version v) {
  this.v = v;
 }

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

 @Override
 public Object clone() throws CloneNotSupportedException {
  PSP p = (PSP)super.clone();
  p.setV((Version)this.getV().clone());
  return p;
 }
}

实现拷贝有几点:

1)实现Cloneable接口

2)重写Object类中的clone方法,并将可见性从protect改为public

3)克隆需要调用super.clone(),也就是Object的实现方法

 

PSP p = (PSP)super.clone();

这样就可以得到一个克隆对象,

PSP类里含有引用类型,如果就这样写,就是浅拷贝,

也就是新对象的Version和旧对象的Version指向的是同个空间

 

而如果

  PSP p = (PSP)super.clone();
  p.setV((Version)this.getV().clone());
  return p;

即,把引用对象也拷贝一次,那么这就是深拷贝!也就是把全部引用对象都浅拷贝一次,就得到深拷贝

但是注意!!

引用对象也应遵循上面拷贝操作的3条规则:

下面是Version类:

public class Version implements Cloneable{
 private String version;

 public Version(String version){
  this.version = version;
 }
 
 public String getVersion() {
  return version;
 }

 public void setVersion(String version) {
  this.version = version;
 }

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

 

这个就是Java的深浅拷贝

 

提个问题,假如对象持有的应用不是自己的类,是别人的,但是别人没有实现Cloneable接口?!

怎么办?!

 

呵呵,可以使用一个小技巧,就是对象流!!

  ByteArrayOutputStream baop =new ByteArrayOutputStream();
  ObjectOutputStream oop = new ObjectOutputStream(baop);
  

  oop.writeObject(new Object());
  
  ByteArrayInputStream bais = new ByteArrayInputStream(baop.toByteArray());
  ObjectInputStream ois = new ObjectInputStream(bais);

  ois.readObject();

 

上面代码的意思是,先构造一个字节数组流(写到内存里面的某个地方),外面装饰一个

对象流(装饰模式),把对象直接写入内存的某处,然后把那个流得出来

这样就可以的到一个完全相同的副本!也就是深拷贝,这种方法不用实现什么接口

 

好了,写完了

 

写给一直进步的人

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多