集合的接口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);
}
}
- 调用父类的克隆方法可以一个ArrayList出来
我查看源码没有父类中发现clone方法, 也就是说调用的是Object的clone方法。 那么只是简单的浅拷贝。 既然是浅拷贝说明详见的list和原来的list中的
elementData 其实是同一个数组。 - 调用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 方法
- 先写入list中的size到序列化流中
- 遍历数组中的有效对象, 每个对象进行序列化。 所有有效的对象序列化完成后就整个序列化完成。
- 为何要transient修饰内部数组,而自己实现序列化方法呢?
因为List中的数组中的对象不一定全部是有效对象, 如果我往list中只存入了一个对象, 但是实际上内部的数组的长度是可能是10,只有第一位置存放了对象,其他位置都是null。 这时候如果全部序列化, 就会把九个null也会序列化进去。 者很不划算的。
readObject方法
既然实现了writeObject 自然要自定义实现反序列化方法。
- 先读取集合的大小,
- 根据大小创建数组,然后逐个读取对象放入数组中, 反序列化完成
可以看到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)));
}
}