Java集合03 - Cloneable

目录

1.Cloneable是什么

2.clone方法的追根溯源

3.深浅拷贝

4.如何实现深拷贝

     4.1 手动为引用类型赋值

     4.2 使用对象流进行深拷贝


笔者JDK版本:1.8.0_202

1.Cloneable是什么

       Cloneable和Serializable接口一样,本质上都是一种标志性接口,无具体实现,意在告知JVM此类可实现克隆功能。当程序中想要使用一个对象的clone方法,仅仅用public重写基类clone方法还不够,还要实现Cloneable接口,否则会抛出CloneNotSupportedException异常。

2.clone方法的追根溯源

       在Java Object类中默认提供了clone方法:

       源码上的一大段注释翻译过来说了以下几件事(第5点不是源码里写的,作为clone的特点也列在这里):

       (1)重写clone方法原则(官方推荐这么做,为了编码规范听官方的准没错)

                x.clone != x结果为true;

                x.clone().getClass()==x.getClass()结果为true;

                x.clone().equals(x)结果为true;

       (2)clone为浅拷贝:基本数据类型拷贝值,引用数据类型拷贝其引用地址(String特殊一点,拷贝的虽是引用,但在修改的时候会生成新的字符串)

       (3)不实现Cloneable接口使用clone方法会抛出CloneNotSupportedException异常(数组默认实现Cloneable接口,clone方法返回的类型是数组类型)

       (4)Object对象本身并不实现Cloneable接口,因此在Object对象上调用clone方法将会抛出运行时异常。

       (5)用clone方法创建对象并不会调用任何构造函数。

3.深浅拷贝

       在阐述这个问题之前,我们需要先回忆一下Java创建对象的四种方式:1.new;2.Java反射机制的newInstance方法;3.反序列化;4.调用clone方法。在源码注释中我们也看到其对clone方法的解释为浅拷贝,那么什么是浅拷贝呢?请看如下的例子:

public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        ArrayList cloneList = (ArrayList)list.clone();
        System.out.println("修改实体数据前");
        System.out.println("原集合:"+list);
        System.out.println("克隆集合:"+cloneList);
        System.out.println("修改实体数据后");
        list.set(0,2);
        System.out.println("原集合:"+list);
        System.out.println("克隆集合:"+cloneList);
    }
}

       创建一个ArrayList集合,放入一个基本数据类型1,调用其克隆方法复制一个ArrayList集合,改变原集合数据后发现对克隆集合数据并不会发生影响。

public class CloneInfo  {
    private  String userName;
    private  String passWord;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassWord() {
        return passWord;
    }
    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
}
public static void main(String[] args) {
        CloneInfo cloneInfo = new CloneInfo();
        cloneInfo.setUserName("Tom");
        cloneInfo.setPassWord("123");

        ArrayList<CloneInfo> list = new ArrayList<>();
        list.add(cloneInfo);
        ArrayList cloneList = (ArrayList)list.clone();
        System.out.println("修改实体数据前");
        System.out.println("原集合:"+list);
        System.out.println("克隆集合:"+cloneList);
        System.out.println("修改实体数据后");
        list.get(0).setUserName("Jack");
        System.out.println("原集合:"+list);
        System.out.println("克隆集合:"+cloneList);
}

 

       而当我们以相同的操作去更改一个引用类型对象的时候,发现克隆集合的数据也在跟着变化,这就是浅拷贝:clone对象时,对于基本数据类型拷贝其值,引用数据类型拷贝其内存地址(String类型也遵循这个规律,只不过因为常量池的关系宏观表现上和基本数据类型一致)。深拷贝:clone对象时,基本数据类型拷贝其值,引用数据类型会重新生成一块内存地址,而不是再指向原有内存地址

4.如何实现深拷贝

     4.1 手动为引用类型赋值

       思路:既然直接调用基类clone方法只能进行浅拷贝,但如果是引用类型的话还是可以调用clone方法继续进行浅拷贝,一直套娃下去终有一天会将所有的引用类型都拷贝成基本数据类型,然后再一层一层嵌套返回回来,就实现了深拷贝(哈哈哈,曲线救国)。如果需要拷贝的类中没有引用类型嵌套,那么就在重写clone方法时自己new一个对象出来,然后把所有属性手动set回去。

Demo1:

@Data
public class CloneInfo implements Cloneable{

    private  CloneInfo2 cloneInfo2;

    @Override
    protected CloneInfo clone() throws CloneNotSupportedException {
        CloneInfo cloneInfo = (CloneInfo) super.clone();
        if(cloneInfo2 != null){
            cloneInfo.cloneInfo2 = (CloneInfo2)cloneInfo2.clone();
        }
        return cloneInfo;
    }
}
public static void main(String[] args) throws CloneNotSupportedException {
        CloneInfo info = new CloneInfo();
        info.setCloneInfo2(new CloneInfo2());
        CloneInfo cloneInfo = info.clone();
        System.out.println("对象内存地址:"+info.getCloneInfo2());
        System.out.println("对象内存地址:"+cloneInfo.getCloneInfo2());
}

       可以看到CloneInfo内嵌的CloneInfo2对象内存地址已经发生改变,深拷贝成功。

Demo2:

public class CloneInfo implements Cloneable{

    private  String userName;
    private  String passWord;

    @Override
    protected CloneInfo clone() throws CloneNotSupportedException {
        CloneInfo cloneInfo = (CloneInfo) super.clone();
        CloneInfo returnCloneInfo = new CloneInfo();
        returnCloneInfo.setUserName(cloneInfo.getUserName());
        returnCloneInfo.setPassWord(cloneInfo.getPassWord());
        return returnCloneInfo;
    }

    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassWord() {
        return passWord;
    }
    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
}
public static void main(String[] args) throws CloneNotSupportedException {
        CloneInfo info = new CloneInfo();
        info.setUserName("Tom");
        info.setPassWord("123");
        CloneInfo cloneInfo = info.clone();
        System.out.println("对象内存地址:"+info);
        System.out.println("对象内存地址:"+cloneInfo);
        System.out.println("修改实体数据前");
        System.out.println("原实体:"+info.getUserName());
        System.out.println("克隆实体:"+cloneInfo.getUserName());
        System.out.println("修改实体数据后");
        info.setUserName("Jack");
        System.out.println("原实体:"+info.getUserName());
        System.out.println("克隆实体:"+cloneInfo.getUserName());
}

     4.2 使用对象流进行深拷贝

       思路:将对象写入流中再读出来实现深拷贝。

public static void main(String[] args) throws Exception {
        CloneInfo info = new CloneInfo();
        info.setCloneInfo2(new CloneInfo2());

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(byteOut);
        objOut.writeObject(info);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream objInput = new ObjectInputStream(byteIn);
        CloneInfo cloneInfo= (CloneInfo) objInput.readObject();
        System.out.println("对象内存地址:"+info.getCloneInfo2());
        System.out.println("对象内存地址:"+cloneInfo.getCloneInfo2());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值