序列化就是将对象的状态信息转换成可以存储或传输的过程。将对象当前状态存储起来。而反序列化就是从存储区将对象重新创建出来。
简单来说,序列化就是将对象转换成一个二进制字节流,方便保存到本地或进行网络传输。
Java io库提供了两个流来实现对象的序列化与反序列化,ObjectOutputStream和ObjectInputStream。并且调用writeObject()和readObject()方法来进行序列化和反序列化
//user没有实现serializable接口
public class User {
private int age;
private String name;
private transient String number;
public User(int age, String name, String number) {
super();
this.age = age;
this.name = name;
this.number = number;
}
}
public class test1 {
public static void main(String[] args) throws IOException {
FileOutputStream out = new FileOutputStream("D:\\user");
ObjectOutputStream os = new ObjectOutputStream(out);
User user = new User(10, "zhangyi", "123456");
os.writeObject(user);
}
}
//User类实现Serializable接口
public class User implements Serializable{
private int age;
private String name;
private transient String number;
public User(int age, String name, String number) {
super();
this.age = age;
this.name = name;
this.number = number;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + ", number=" + number + "]";
}
}
public class test1 {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
FileOutputStream out = new FileOutputStream("D:\\user");
ObjectOutputStream os = new ObjectOutputStream(out);
User user = new User(10, "zhangyi", "123456");
//序列化
os.writeObject(user);
new Thread().sleep(5);
FileInputStream in = new FileInputStream("D:\\user");
ObjectInputStream is = new ObjectInputStream(in);
//反序列化
User user2 = (User) is.readObject();
System.out.println(user2);
}
}
如果要序列化的对象的成员变量,包含了其他对象的引用,我们还需要对其他变量也进行序列化实现Serializable接口。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData;
}
所以在集合中,ArrayList、LinkedArrayList、HashMap、HashSet都实现了Serializable接口。但是ArrayList像保存数值的数组被transient修饰了,表示列表的数值不能被序列化,这岂不是矛盾了?
实际上集合中都实现了Serializable接口的writeObject方法和readObject方法。在writeObject方法中实际上进行了序列化,序列化了ArrayList中实际保存的元素,而不是数组容量大小的元素,数组容量大小大于实际保存的元素,所以这样减少了空间浪费。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the ArrayList instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
为什么还需要实现Serializable接口的writeObject方法和readObject方法?这与ObjectOutputStream.writeObject(user)和ObjectInputStream.readObject()有什么关系?
通过反射的方式,实现了序列化。所以这就是为什么在实体类中实现writeObject方法和readObject方法了,我们可以自己定义要序列化的成员变量。
所以通过重写writeObject方法和readObject方法,就可以自定义实现我们的序列化方式。而ObjectOutputStream是通过反射来实现调用User的这两个方法的。
public class User implements Serializable{
private static final long serialVersionUID = -2658984781134384274L;
private int age;
private String name;
private transient String number;
public User(int age, String name, String number) {
super();
this.age = age;
this.name = name;
this.number = number;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + ", number=" + number + "]";
}
private void writeObject(java.io.ObjectOutputStream s) throws IOException {
s.writeObject(age);
}
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
this.setAge((int)s.readObject());
}
}
public class test1 {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
FileOutputStream out = new FileOutputStream("D:\\user");
ObjectOutputStream os = new ObjectOutputStream(out);
User user = new User(10, "zhangyi", "123456");
//序列化
os.writeObject(user);
new Thread().sleep(5);
FileInputStream in = new FileInputStream("D:\\user");
ObjectInputStream is = new ObjectInputStream(in);
//反序列化
User user2 = (User) is.readObject();
System.out.println(user2);
}
}
此外我们还可以看到在序列化类的时候有一个属性serialVersionUID,这是自动生成的。它是干什么的?
因为不同的编译器实现的不同,反序列化过程中可能会导致意外。所以serialVersionUID来保证不同编译器实现的一致性。