在程序开发过程中,有时候我们需要一个对象的副本,我们对该副本的操作不能影响到原对象中的数据,这时候,我们就需要对该对象进行一个复制版本,也就是我要说的克隆Clone.

Clone方法原型介绍

在讲对象的克隆之前,我们先来了解一下Clone方法存在哪里,Clone方法是Object类中声明的一个protected访问权限的本地方法.
clone方法原型如下:

protected native Object clone() throws CloneNotSupportedException;

Cloneable接口介绍

如果我们想让我们自己定义的类支持Clone,那么我们的类就必须实现Cloneable 接口.
通过接口的定义我们知道这是一个没有声明任何方法的空接口.

package java.lang;
public interface Cloneable {
}

类实现了Cloneable接口的作用:
类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。

原始类型(int,double,String…)的字段复制会得到新的值地址,但是引用类型(如自己定义的JavaBean)系统默认是使用原对象的引用,也就是说clone后对象的引用型成员依旧是指向原来的地址. 这也就是常说的系统默认的克隆是浅层次的克隆.

注意:

  • 如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出
    CloneNotSupportedException 异常。
  • 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。

浅层次克隆(系统默认的克隆)

好了上面做了那么多的前提准备,现在开始一个系统默认的克隆,我们来看看效果.
总结-浅层次clone步骤:

  • 1.要被clone的类必须要实现接口Cloneable;
  • 2.在要被Clone的类中覆写Object中的clone方法(有异常抛出),.将访问权限由protected改为public权限,以便可以操作该方法.为了方便可以將返回值类型Object类型具体化为本类对象.
package cn.com.yves.object.clone.newclone.noclone;

/**
 * 
 * 
 * @author Yves He
 * 
 */
public class Cart implements Cloneable {

    private String id;
    private String creater;
    private Shop shop;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCreater() {
        return creater;
    }

    public void setCreater(String creater) {
        this.creater = creater;
    }

    public Shop getShop() {
        return shop;
    }

    public void setShop(Shop shop) {
        this.shop = shop;
    }

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

}
package cn.com.yves.object.clone.newclone.noclone;

/**
 * 
 * 
 * @author Yves He
 * 
 */
public class Generic {

    private String color;
    private double size;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getSize() {
        return size;
    }

    public void setSize(double size) {
        this.size = size;
    }

}
package cn.com.yves.object.clone.newclone.noclone;

/**
 * 
 * 
 * @author Yves He
 * 
 */
public class Shop {

    private String name;
    private double price;
    private Generic gen; // 一般的参数

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Generic getGen() {
        return gen;
    }

    public void setGen(Generic gen) {
        this.gen = gen;
    }

}

测试方法:

/**
     * 原始类型(int,double,String...)的字段复制会得到新的值地址,但是引用类型(如自己定义的JavaBean)
     * 系统默认是使用原对象的引用,也就是说clone后对象的引用型成员依旧是指向原来的地址. 这也就是常说的系统默认的克隆是浅层次的克隆.
     * 
     * @param args
     */
    public static void main(String[] args) {
        Generic gen = new Generic();
        gen.setColor("red");
        gen.setSize(100);

        Shop shop = new Shop();
        shop.setName("BMW");
        shop.setPrice(100000);
        shop.setGen(gen);

        Cart cart = new Cart();
        cart.setCreater("Yves");
        cart.setId(UUID.randomUUID().toString());
        cart.setShop(shop);

        try {
            Cart cartClone = cart.clone();

            System.out.println(cartClone == cart);// false

            System.out.println(cartClone.getShop() == cart.getShop());// true,
                                                                      // 可见系统默认的克隆是没有对引用型进行克隆,而是保留了原来的引用,也不是为null

            System.out.println(cartClone.getShop().getGen() == cart.getShop().getGen());// true,原理同上

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

PS : 下篇文章介绍深层次克隆~