Serializable是Java提供的序列化接口,是一个空接口,为对象提供标准的序列化与反序列化操作。使用Serializable实现序列化过程相当简单,只需要在类声明的时候指定一个标识,便可以自动的实现默认的序列化过程。
private static final long serialVersionUID = 1L;
上面已经说明让对象实现序列化,只需要让当前类实现Serializable接口,并且声明一个serialVersionUID就可以了,非常的简单方便。实际上serialVersionUID都不是必须的,没有它同样可以正常的实现序列化操作。
User类就是一个实现了Serialzable的类,它是可以被序列化和反序列化的。
public class User implements Serializable{
private static final Long serialVersionUID = 1L;
private String user;
private String password;
public User(String user, String password) {
super();
this.user = user;
this.password = password;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + "]";
}
}
通过Serializable实现对象的序列化过程非常的简单,无需任何操作,系统就为我们自动实现了。如何进行对象的序列化与反序列化操作也是非常的简单,只需要通过ObjectOutputStream,ObjectInputStream进行操作就可以了。
public class TestSerializable {
//序列化过程
@Test
public void toSerializable() throws FileNotFoundException, IOException {
User user = new User("syc", "123");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
objectOutputStream.writeObject(user);
objectOutputStream.close();
}
//反序列化过程
@Test
public void fromSerializable() throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/user.txt"));
User user = (User) objectInputStream.readObject();
System.out.println(user);
objectInputStream.close();
}
}
是的,你没有看错,序列化与反序列化操作过程就是这么的简单。只需要将User写入到文件中,然后再从文件中进行恢复,恢复后得到的内容与之前完全一样,但是两者是不同的对象。当然了前面提到过一个问题,如果将serialVersionUID去掉会产生什么样的影响呢?
刚开始提到了,serialVersionUID要不要指定呢?如果不指定会出现什么样的后果?如果指定了以后后边的值又代表着什么意思呢?同时我们也要明白,既然系统指定了这个字段,那么肯定是有它的作用的。
这个serialVersionUID是用来辅助对象的序列化与反序列化的,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功。这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且会报出错误:
产生错误的操作是:首先在目录创建一个文件user.txt,在创建的时候做出判断,如果user.txt不存在,则进行创建文件操作并进行序列化写入操作。如果user.txt存在那么我们就直接读取并进行反序列化操作。我在创建完成后,在User里面多加了一个属性,然后在进行反序列化操作的时候就报出了这样的错误,当然前提是我把类中声明的serialVersionUID去掉了。
当我把serialVersionUID加上的时候,我在创建完user.txt后我在进行序列化读取,成功的拿到了之前存进去的数据。这样的一个操作过程就让我们很清楚的看出来了serialVersionUID在序列化与反序列化过程当中的作用。
这个字段的作用显而易见,加入我们升级了系统,在User当中增加了字段属性,那么我们在手动添加了serialVersionUID之后,我们在进行序列化和反序列化的时候就会很从容,就不会再出现crash的情况了。
另外强调两点,静态成员变量属于类不属于对象所以不参与序列化的过程,还有如果使用transient标记的成员变量不参与序列化过程。
什么时候需要序列化
首先第一个问题,实现序列化的两个原因:1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本;2、按值将对象从一个应用程序域发送至另一个应用程序域。实现serializabel接口的作用是就是可以把对象存到字节流,然后可以恢复,所以你想如果你的对象没实现序列化怎么才能进行持久化和网络传输呢,要持久化和网络传输就得转为字节流,所以在分布式应用中及设计数据持久化的场景中,你就得实现序列化。
第二个问题,是不是每个实体bean都要实现序列化,答案其实还要回归到第一个问题,那就是你的bean是否需要持久化存储媒体中以及是否需要传输给另一个应用,没有的话就不需要,例如我们利用fastjson将实体类转化成json字符串时,并不涉及到转化为字节流,所以其实跟序列化没有关系。
第三个问题,有的时候并没有实现序列化,依然可以持久化到数据库。这个其实我们可以看看实体类中常用的数据类型,例如Date、String等等,它们已经实现了序列化,而一些基本类型,数据库里面有与之对应的数据结构,从我们的类声明来看,我们没有实现serializabel接口,其实是在声明的各个不同变量的时候,由具体的数据类型帮助我们实现了序列化操作。