什么叫做序列化和反序列化?
序列化:将对象转换成流,保存到磁盘上,或者传输给网络。
反序列化:就是把这种流,反转回对象。
那么序列化有什么用呢?
1)永久保存对象在磁盘上
2)在网络中可以采用序列化机制直接传输对象
对象序列化用于将一个对象转换为字节流的过程,可以将对象序列化之后保存在磁盘中或者通过网络发送至另外一个程序,对象序列化可以把对象转换为与平台无关的字节流,在不同平台都可以反序列化为之前的对象。
如何序列化?
1.首先如果一个类要进行序列化的相关操作,那么就必须要实现Serializable 接口,进行标记。表示此类可以被序列化。
其次,我们要使用ObjectOutputStream / ObjectInputStream 进行序列化和反序列化。
小技巧:【Intellij idea】快捷键自动生成序列化Id配置
2.ObjectOutputStream把对象转换成流写入到文件或者网络上。
3.ObjectInputStream读取文件中的对象,对它及进行恢复。
示例:
public static void main(String[] args) {
File dest = new File("./c.txt");
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(dest));
//这个类不仅仅可以写入对象还可以写入基本数据类型
objectOutputStream.write(1);
objectOutputStream.writeBoolean(true);
objectOutputStream.writeShort(12);
objectOutputStream.writeUTF("abcdefg");
objectOutputStream.writeObject(new People(1,"zhangsan",123));
//从dest文件去读取
objectInputStream = new ObjectInputStream(new FileInputStream(dest));
//必须按照写入的顺序去读取,不然读取失败
System.out.println(objectInputStream.read());
System.out.println(objectInputStream.readBoolean());
System.out.println(objectInputStream.readShort());
System.out.println(objectInputStream.readUTF());
Object o = objectInputStream.readObject();
if(o instanceof People){
People p = (People)o;
System.out.println(p.toString());
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
objectOutputStream.close();‘
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class People implements Serializable{
private static final long serialVersionUID = 8137057191648334353L;
private int id;
private String name;
private int pwd;
public People() {
this(0,"张三",-1);
}
public People(int id, String name, int pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(int pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public int getPwd() {
return pwd;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
", pwd=" + pwd +
'}';
}
}
这里我们说一下ObjectOutputStream的write()方法。
我们会发现ObjectOutputStream可以写很多东西,基本数据类型都可以写入,我们常用的写入对象的方法就是writeObject(Object o);并且我们在读取时,必须和写入的顺序是一致的,否则无法读取成功。
SerialVersionUID
SerialVersionUID系统自动生成的标识。主要作用是判断序列化和反序列化时的对象是否一致,如果SerialVersionUID相同,那么就是一致的,如果不相同,就会抛出异常。
并且系统自动生成的SerialVersionUID是根据类的字段返回的64位长整型的哈希值,所以当我们修改字段时,SerialVersionUID也会随之改变。
所以我们要注意!!!如果我们已经序列化了,那么再次之后不能对类进行任何改动,否则SerialVersionUID就会改变 ,随之而来的就是反序列化失败!
transient关键字:防止序列化
如果类成员变量被 transient 关键字修饰,那么这个变量就不能被序列化。
相信 transient 关键字大家一定见过,尤其是源码,集合ArrayList 的 elements 数组就是被 transient 修饰的。目的是什么呢?People 的 pwd 这个成员变量本质上是不应该让别人可见的吧,密码不能谁都可以看吧。所以如果我们要保存此对象,pwd这个变量,我们就不能让他序列化。
ArrayList 的 writeObject()和 readObject ()的重写。
如果不重写weriteObject方法,那么被 transient 修饰的 element 就不会被序列化了,那么序列化就没有任何意义了,所以我们重写writeObject方法,只把 element 中有效的元素进行序列化。