为什么集合类没有实现Cloneable和Serializable接口?

集合的接口List中并没有定义继承序列化和克隆接口, 并且其他的集合接口也都没有实现。

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。


克隆分为深拷贝和浅拷贝, 具体的拷贝实现需要用户自己根据对象的特性来实现, 集合类又是一个容器, 会装载不同的对象(各种各样的). 不可能每个对象都实现克隆,

序列化也是同样的道理。

虽然List没有继承序列化和克隆接口,但是它的子类ArrayList都继承了这两个接口。 并且根据自己的特性对这两个接口的实现都进行了自己的优化

ArrayList实现了这两个接口

ArrayList 的clone方法:

  public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
  1. 调用父类的克隆方法可以一个ArrayList出来
    我查看源码没有父类中发现clone方法, 也就是说调用的是Object的clone方法。 那么只是简单的浅拷贝。 既然是浅拷贝说明详见的list和原来的list中的
    elementData 其实是同一个数组。
  2. 调用Arrays.copyOf方法复制一份新的数组, 赋值给新的List.
    这个copyOf方法只是复制数组中的引用, 说明两个数组虽然分开了, 但是内部的存储的对象还是一样的。

克隆的方法克隆了新的List , 但是两个list中装载的对象还是同一份。

验证代码:


public class A implements Serializable{

    int x = 0;

    public static void main(String[] args) throws  IOException, ClassNotFoundException {
        ArrayList<A> list = new ArrayList<>();
        list.add(new A());

        ArrayList<A> cloneList = (ArrayList<A>) list.clone();

        System.out.println("比较克隆前后的对象是否同一个");
        System.out.println(list.get(0) == cloneList.get(0));

    }
}

运行结果:
在这里插入图片描述
可以看到克隆前后的List中保存的对象还是同一个

ArrayList的序列化操作:

在这里插入图片描述
上图是ArrayList中的内部维护的数组, 可以看到elementData是被transient修饰的, 就是不会被序列化。 但是它实现了两个方法readObject和writeObject方法。 序列化实际上是调用的这两个方法来完成的。

writeObject 方法
  1. 先写入list中的size到序列化流中
  2. 遍历数组中的有效对象, 每个对象进行序列化。 所有有效的对象序列化完成后就整个序列化完成。
  • 为何要transient修饰内部数组,而自己实现序列化方法呢?

因为List中的数组中的对象不一定全部是有效对象, 如果我往list中只存入了一个对象, 但是实际上内部的数组的长度是可能是10,只有第一位置存放了对象,其他位置都是null。 这时候如果全部序列化, 就会把九个null也会序列化进去。 者很不划算的。

readObject方法

既然实现了writeObject 自然要自定义实现反序列化方法。

  1. 先读取集合的大小,
  2. 根据大小创建数组,然后逐个读取对象放入数组中, 反序列化完成

可以看到ArrayList实现了自己的序列化和反序列化方法, 下面我们写代码来验证下序列化:

import java.io.*;
import java.util.ArrayList;

public class A implements Serializable {

    int x = 0;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ArrayList<A> list = new ArrayList<>();
        list.add(new A());


        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
        objectOutputStream.writeObject(list);

        InputStream in = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(in);

        ArrayList<A> list1 = (ArrayList<A>) objectInputStream.readObject();

        System.out.println("序列化和反序列化完成, 开始比较: ");
        System.out.println("序列化前的集合:" + JSONUtil.toJsonStr(list));
        System.out.println("序列化后的集合:" + JSONUtil.toJsonStr(list1));

        System.out.println("序列化前后的集合中对象是否一样:" + (list1.get(0) == list.get(0)));
    }
}

在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值