在劫

吾生也有涯,而知也无涯 。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

在Java中赋值是很常用的

//原始类型
int a = 1;
int b = a;
//引用类型
String[] weekdays = new String[5];
String[] gongzuori = weekdays; //仅拷贝引用
/*
*在上述代码中如果是原始数据类型,赋值传递的为真实的值;如果是引用类型,赋值传递的是对象的引用,而不是对象
*/

Clone
    在Java中,clone是将已有的对象在内存中赋值到另一个与之相同的对象的过程。Java中的克隆为逐域复制。
    在Java中药支持clone方法,需要首先实现Cloneable接口,此接口不包含任何方法,仅仅是一个标记接口。
    需要注意的是:如果想要支持clone,就需要实现Cloneable接口,如果没有实现Cloneable接口调用clone()方法,会抛出CloneNotSupportedException异常。
    然后是重写clone方法,并修改成public访问级别:

static class CloneableImp implements Cloneable {
  public int count;
  public Child child;
      
      
  @Override
  public Object clone() throws CloneNotSupportedException {
      return super.clone();
  }
}
//调用clone方法赋值对象
CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
  Object obj = imp1.clone();
  CloneableImp imp2 = (CloneableImp)obj;
  System.out.println("main imp2.child.name=" + imp2.child.name);
} catch (CloneNotSupportedException e) {
  e.printStackTrace();
}

浅拷贝
    上面的代码实现的clone实际上是属于浅拷贝(Shallow Copy)。
    关于浅拷贝,你该了解的
        使用默认的clone方法
        对于原始数据域进行值拷贝
        对于引用类型仅拷贝引用
        执行快,效率高
        不能做到数据的100%分离。
        如果一个对象只包含原始数据域或者不可变对象域,推荐使用浅拷贝。
    关于无法做到数据分离,我们可以使用这段代码验证

CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
  Object obj = imp1.clone();
  CloneableImp imp2 = (CloneableImp)obj;
  imp2.child.name = "Bob";
          
  System.out.println("main imp1.child.name=" + imp1.child.name);
} catch (CloneNotSupportedException e) {
  e.printStackTrace();
}

上述代码我们使用了imp1的clone方法克隆出imp2,然后修改 imp2.child.name 为 Bob,然后打印imp1.child.name 得到的结果是

main imp1.child.name=Bob
//原因是浅拷贝并没有做到数据的100%分离,imp1和imp2共享同一个Child对象,所以一个修改会影响到另一个。

深拷贝
    深拷贝可以解决数据100%分离的问题。只需要对上面代码进行一些修改即可。
    1、Child实现Cloneable接口。

public class Child implements  Cloneable{

  public String name;

  public Child(String name) {
      this.name = name;
  }

  @Override
  public String toString() {
      return "Child [name=" + name + "]";
  }

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

2.重写clone方法,调用数据域的clone方法。

static class CloneableImp implements Cloneable {
  public int count;
  public Child child;
      
      
  @Override
  public Object clone() throws CloneNotSupportedException {
      CloneableImp obj = (CloneableImp)super.clone();
      obj.child = (Child) child.clone();
      return obj;
  }
}

当我们再次修改imp2.child.name就不会影响到imp1.child.name的值了,因为imp1和imp2各自拥有自己的child对象,因为做到了数据的100%隔离。
关于深拷贝的一些特点
    需要重写clone方法,不仅仅只调用父类的方法,还需调用属性的clone方法
    做到了原对象与克隆对象之间100%数据分离
    如果是对象存在引用类型的属性,建议使用深拷贝
    深拷贝比浅拷贝要更加耗时,效率更低
    Copy constructors
使用复制构造器也可以实现对象的拷贝。
    复制构造器也是构造器的一种
    只接受一个参数,参数类型为当前的类
    目的是生成一个与参数相同的新对象
复制构造器相比clone方法的优势是简单,易于实现。
一段使用了复制构造器的代码示例

public class Car {
  Wheel wheel;
  String manufacturer;
  
  public Car(Wheel wheel, String manufacturer) {
      this.wheel = wheel;
      this.manufacturer = manufacturer;
  }
  
  //copy constructor
  public Car(Car car) {
      this(car.wheel, car.manufacturer);
  }
  
  public static class Wheel {
      String brand;
  }
}
//注意,上面的代码实现为浅拷贝,如果想要实现深拷贝,参考如下代码

 

public Car(Car car) {
  Wheel wheel = new Wheel();
  wheel.brand = car.wheel.brand;
      
  this.wheel = wheel;
  this.manufacturer = car.manufacturer;
}

为了更加便捷,我们还可以为上述类增加一个静态的方法

public static Car newInstance(Car car) {
  return new Car(car);
}

使用Serializable实现深拷贝
其实,使用序列化也可以实现对象的深拷贝。简略代码如下

public class DeepCopyExample implements Serializable{
  private static final long serialVersionUID = 6098694917984051357L;
  public Child child;
  
  public DeepCopyExample copy() {
      DeepCopyExample copy = null;
      try {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos);
          oos.writeObject(this);
  
          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bais);
          copy = (DeepCopyExample) ois.readObject();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
      return copy;
  }
}

其中,Child必须实现Serializable接口

public class Child implements Serializable{
  private static final long serialVersionUID = 6832122780722711261L;
  public String name = "";

  public Child(String name) {
      this.name = name;
  }

  @Override
  public String toString() {
      return "Child [name=" + name + "]";
  }
}

 

package abc;  
  
class Address implements Cloneable {  
    private String add;  
  
    public String getAdd() {  
        return add;  
    }  
  
    public void setAdd(String add) {  
        this.add = add;  
    }  
      
    @Override  
    public Object clone() {  
        Address addr = null;  
        try{  
            addr = (Address)super.clone();  
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return addr;  
    }  
}  
  
class Student implements Cloneable{  
    private int number;  
  
    private Address addr;  
      
    public Address getAddr() {  
        return addr;  
    }  
  
    public void setAddr(Address addr) {  
        this.addr = addr;  
    }  
  
    public int getNumber() {  
        return number;  
    }  
  
    public void setNumber(int number) {  
        this.number = number;  
    }  
      
    @Override  
    public Object clone() {  
        Student stu = null;  
        try{  
            stu = (Student)super.clone();   //浅复制 调用父类clone()方法 复制学生类对象 
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        stu.addr = (Address)addr.clone();   //深度复制  复制地址值 Address类重写了clone()方法  所以当调用此处clone()方法时,会深度调用Address类对象
        return stu;  
    }  
}  
public class Test {  
      
    public static void main(String args[]) {  
          
        Address addr = new Address();  
        addr.setAdd("杭州市");  
        Student stu1 = new Student();  
        stu1.setNumber(123);  
        stu1.setAddr(addr);  
          
        Student stu2 = (Student)stu1.clone();  
          
        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
          
        addr.setAdd("西湖区");  
          
        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
    }  
}

 

class AddressTest implements Cloneable{
    String add;
    public AddressTest(String add){
        this.add = add;
    }
    //重写clone()方法
    public Object clone(){
        AddressTest obj = null;
        try {
            obj = (AddressTest)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        return obj;
    }
    @Override
    public String toString() {
        // TODO 自动生成的方法存根
        return add;
    }
}
class StudentTest implements Cloneable{
    String name;
    AddressTest add;
    public StudentTest(String name, AddressTest add) {
        this.name = name;
        this.add = add;
    }
    public Object clone(){
        StudentTest stu = null;
        try {
            stu = (StudentTest)super.clone();//浅拷贝
            stu.add = (AddressTest)add.clone();//深度拷贝,add所在类中的clone()方法是重写过的
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return stu;
    }
    @Override
    public String toString() {
        return name + "------" + add;
    }
    
}
public class CloneDemo {
    public static void main(String[] args) {
        AddressTest a1 = new AddressTest("杭州");
        AddressTest a2 = new AddressTest("北京");
        StudentTest s1 = new StudentTest("zed1",a2);
        StudentTest s2 = (StudentTest)s1.clone();
        s1.add = a1;
        System.out.println("s1: " + s1.toString());
        System.out.println("s2: " + s2.toString());
    }
}

 

class AddressTest /*implements Cloneable*/{
    String add;
    public AddressTest(String add){
        this.add = add;
    }
    //重写clone()方法
//    public Object clone(){
//        AddressTest obj = null;
//        try {
//            obj = (AddressTest)super.clone();
//        } catch (CloneNotSupportedException e) {
//            // TODO 自动生成的 catch 块
//            e.printStackTrace();
//        }
//        return obj;
//    }
    @Override
    public String toString() {
        // TODO 自动生成的方法存根
        return add;
    }
}
class Name{
    int count;
    String name;
    public Name(String name){
        this.name = name;
    }
    @Override
    public String toString() {
        // TODO 自动生成的方法存根
        return name;
    }
}
class StudentTest implements Cloneable{
    Name name;
    AddressTest add;
    public StudentTest(Name name){
        this.name = name;
    }
    public StudentTest(Name name, AddressTest add) {
        this.name = name;
        this.add = add;
    }

    public Object clone(){
        StudentTest stu = null;
        try {
            stu = (StudentTest)super.clone();//浅拷贝
            //stu.add = (AddressTest)add.clone();//深度拷贝,add所在类中的clone()方法是重写过的
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return stu;
    }
    @Override
    public String toString() {
        return name + "------" + add;
    }
    
}
public class CloneDemo {
    public static void main(String[] args) {
        AddressTest a1 = new AddressTest("杭州");
        AddressTest a2 = new AddressTest("北京");
        Name n1 = new Name("zed1");

        StudentTest s1 = new StudentTest(n1,a1);
        //s1.name = new Name("zed2");
        StudentTest s2 = s1;
        //都会输出a2的值,所以是浅拷贝,a2并没有深度拷贝
        s2.add = a2;
        System.out.println("s1: " + s1.toString());
        System.out.println("s2: " + s2.toString());
    }
}

   深拷贝和浅拷贝都是对象拷贝,不同的是对象类中存在其他类对象,他们都是拷贝这个对象,不同的是深拷贝拷贝的是这个对象,浅拷贝拷贝的是这个对象的引用地址。也就是浅拷贝如果更改类中引用的对象,那个地址的对象就会改变(浅拷贝的两个对象中的对象引用指向同一地址,相当于共享对象),而深拷贝就不会。所以深拷贝会在拷贝的时候处理对象中的引用对象。

posted on 2017-07-03 09:11  长嘴大耳怪  阅读(253)  评论(0编辑  收藏  举报