
Clone,克隆的意思,即通过克隆来获取与源体一样的新个体。还记得初中生物课本上的多利羊吗?亦或是科幻电影中BOSS们害怕自己死亡,而存放在培养皿中的那些克隆体。所以说在Java中我们可以通过Clone方法来创建一个新的对象,这里让我们来回顾一下创建新对象的几种创建方式:
| 使用new关键字 | 调用构造函数 | 
|---|---|
| 反射 | 调用构造函数 | 
| 使用clone方法 | 没有调用构造函数 | 
| 使用反序列化 | 没有调用构造函数 | 
将源对象中为基本数据类型的成员变量的属性都复制给克隆对象,为引用数据类型的成员变量的引用地址复制给克隆对象。

深克隆:与浅克隆相比,将引用数据类型所引用的对象也都会复制一份。


我们通过查询api可以看到我们的Object方法中是有给我们提供clone()方法的,并且其是native的。

在上面的注释的大意就是,一个类要想使用clone()方法必须实现Cloneable接口,否则抛出CloneNotSupportedException异常。
而且通过阅读注解我们将得到对于clone()方法的建议,但并不是必须保证的:
当我们仅仅通过实现Cloneable并重写调用Object的clone()方法时,我们将实现浅克隆,因为clone()方法是被protected修饰的。
小tips:protected的作用域
| 当前类 | 同包 | 子类 | 不同包 | 
| √ | √ | √ | × | 
    public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        A a = new A();
        a.setB_value(1);
        a.setR_value(new int[]{1});
        //第一次打印a属性
        System.out.println("第一次打印a属性:"+a.getB_value()+" "+a.getR_value()[0]);
        A b = (A)a.clone();
        b.setB_value(2);
        int[] c = b.getR_value();
        c[0]=2;
        b.setR_value(c);
        //第一次打印b属性
        System.out.println("第一次打印b属性:"+b.getB_value()+" "+b.getR_value()[0]);
        //第二次打印a属性
        System.out.println("第一次打印a属性:"+a.getB_value()+" "+a.getR_value()[0]);
    }
    static class A implements Cloneable {
        private int B_value;
        //先明确的是数组是对象
        private int[] R_value;
        
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        /*
          get、set方法
      
         */
    }
}
打印执行结果:
第一次打印a属性:1 1
第一次打印b属性:2 2
第一次打印a属性:1 2
这里我之所以选择数组,是因为数组比较特殊数组本身就是引用类型,而且可以更好的理解我们Arrays.copy(),Arrays类提供的copy()方法就是一个现成的浅拷贝方法。
先创建Blog和Author两个类,这里有个小知识点,注意查看:
/**
 *博客类
 */
public class Blog {
    //标题和作者
    private String title; //不可变对象
    private Author author;//引用对象
}
/**
 *作者类
 */
public class Author {
    //昵称和账号
    private String nickname;
    private String username;
}
注意这里我特意使用了String,有些同学可能有疑问了这不也是一个对象类型吗?没错,不过它是不可变的,在《Effective Java》(原书第3版)中有这样一句话:“不可变类永远都不应该提供克隆方法,因为它只会激发不必要的克隆。”所以这里的String类型的字段,我们要当成一个“基本数据类型”。
public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Author author = new Author();
        author.setNickname("zzxkj");
        author.setUsername("123456");
        //创建源对象
        Blog blog = new Blog();
        blog.setAuthor(author);
        //克隆
        Blog blog1 = (Blog) blog.clone();
        Author author1 = blog1.getAuthor();
        author1.setUsername("000000");
        System.out.println(blog.getAuthor().getUsername()+" "+blog1.getAuthor().getUsername());
    }
    static class Blog implements Cloneable{
        //标题和作者
        private String title;
        private Author author;
        //重写克隆方法
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Blog blog = (Blog) super.clone();
            // 引用类型克隆赋值
            blog.setAuthor((Author) this.author.clone());
            return blog;
        }
  /*
  省略set和get方法
  * */
    }
    static class Author implements Cloneable{
        //昵称和账号
        private String nickname;
        private String username;
        //重写克隆方法
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
  /*
  省略set和get方法
  * */
    }
}
打印执行结果:
123456 000000
《Effective Java》 中推荐使用构造器(Copy Constructor)来实现深克隆,如果构造器的参数为基本数据类型或字符串类型则直接赋值,如果是对象类型,则需要重新 new 一个对象。
public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Author author = new Author();
        author.setNickname("zzxkj");
        author.setUsername("123456");
        //创建源对象
        Blog blog = new Blog();
        blog.setAuthor(author);
        //克隆,其实就是比葫芦画瓢再创建一遍
        Blog blog1 = new Blog();
        Author author1 = new Author();
        author1.setNickname("zzxkj");
        author1.setUsername("000000");
        blog1.setAuthor(author1);
        System.out.println(blog.getAuthor().getUsername()+" "+blog1.getAuthor().getUsername());
    }
    static class Blog{
        //标题和作者
        private String title;
        private Author author;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Blog blog = (Blog) super.clone();
            blog.setAuthor((Author) this.author.clone());
            return blog;
        }
  /*
  省略set和get方法
  * */
    }
    static class Author{
        //昵称和账号
        private String nickname;
        private String username;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
  /*
  省略set和get方法
  * */
    }
}
打印执行结果:
123456 000000
我们都知道Java中的序列化的概念,那么这时就需要我们每个类实现Serializable接口,先将源对象写入到内存中的字节流,然后再从这个字节流中读出刚刚存储的信息,来作为一个新的对象返回,那么这个新对象和原型对象就不存在任何地址上的共享,这样就实现了深克隆。
public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Author author = new Author();
        author.setNickname("zzxkj");
        author.setUsername("123456");
        //创建源对象
        Blog blog = new Blog();
        blog.setAuthor(author);
        //克隆
        Blog blog1 = (Blog) StreamClone.clone(blog);
        Author author1 = blog1.getAuthor();
        author1.setUsername("000000");
        System.out.println(blog.getAuthor().getUsername()+" "+blog1.getAuthor().getUsername());
    }
    /**
     * 通过字节流实现克隆
     */
    static class StreamClone {
        public static <T extends Serializable> T clone(People obj) {
            T cloneObj = null;
            try {
                // 写入字节流
                ByteArrayOutputStream bo = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bo);
                oos.writeObject(obj);
                oos.close();
                // 分配内存,写入原始对象,生成新对象
                ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流
                ObjectInputStream oi = new ObjectInputStream(bi);
                // 返回生成的新对象
                cloneObj = (T) oi.readObject();
                oi.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cloneObj;
        }
    }
    static class Blog implements Serializable{
        //标题和作者
        private String title;
        private Author author;
  /*
  省略set和get方法
  * */
    }
    static class Author implements Serializable{
        //昵称和账号
        private String nickname;
        private String username;
  /*
  省略set和get方法
  * */
    }
}
打印执行结果:
123456 000000