什么是序列化?
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
(通俗来讲就是我们一般创建的对象都是保存在内存中,使用完之后会被虚拟机的垃圾回收机制回收掉,如果我们下次在使用,又需要重新创建对象,如果使用序列化,就可以把我们创建的对象变成一个文件[字节码文件],保存到硬盘当中,下次使用只要取出来就可以,通过反序列化)
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。
序列化需要满足的条件:
请注意,一个类的对象要想序列化成功,必须满足两个条件:
1,该类必须实现 java.io.Serializable 接口。
源码: public interface Serializable{}
我们可以注意到,该接口里面什么都没有,那他是怎么起作用的?
它其实起到一个标识/标志的作用,Java虚拟机看到这个类实现了这个接口,
可能会对这个类进行特殊待遇,Java虚拟机看到这个标志,会为该类自动生成
一个序列化版本号。
2,该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
如果你想知道一个 Java 标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。
代码示例:
序列化和反序列化一个对象。
public class SerializableTest01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建一个Java对象
Student s=new Student(12,"张三");
//序列化
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("student"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
//反序列化
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("student"));
//开始反序列化,读
Object o = ois.readObject();
//反序列化回来的是一个学生对象,所以会调用学生对象的toString方法
System.out.println(o);
ois.close();
}
}
反序列化结果:
Student{id=12, name='张三'}
序列化结果,生成一个文件,可以看出是乱码文件(并不是给我们看到,是给电脑看的)
序列化多个对象:
//序列化多个对象,
// 把对象放到集合当中,序列化集合
public class SerializableTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
List list= new ArrayList<>();
list.add(new Student(1,"lisi"));
list.add(new Student(2,"cx"));
list.add(new Student(3,"xc"));
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("students"));
//序列化一个集合,这个集合放了很多对象。
oos.writeObject(list);
oos.flush();
oos.close();
//反序列化
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("students"));
List list1= (List) ois.readObject();
for (Object a:list1){
System.out.println(a);
}
ois.close();
}
}
反序列化结果:
Student{id=1, name='lisi'}
Student{id=2, name='cx'}
Student{id=3, name='xc'}
序列化版本号:
在Java中起的作用:
JVM在序列化过程识别不同类依据。当序列化版本号不同JVM认为这是两个不同的类)
即使类的名字相同
JVM在识别不同类时先比较类名字(name),如果相同则比较序列号版本号;
类的名字就好像人身份证上的名字。重名的人有很多个,但要辨别是不是同一个人,查看身份证号即可
在序列化中起到的作用:
class类中实现Serializable接口时JVM会随机给一个序列号版本号
也可主动进行赋值private static final long serialVersionUID=1L;(一般主动给与序列化版本号给一个1L即可)
在已经序列化的文件, 但他类已经修改时,如果序列化版本号不一致会异常InvalidClassException
修改类的一些情况
序列号版本相同的情况下
新修改的类增加了原来类没有的变量,在原来类已经序列化,在反序列化新增加的变量为默认值
新修改的类删除了原来类的变量,在反序列化以前的类时,该变量也会跟着消失
当改变原来的已经序列化的对象时会出现以下错误。
如果重新序列化,再反序列化不会出现该问题
提供序列化版本号:
private static final long serialVersionUID=1l;
=左边是固定不变的,右边是任意的,起到一个标识的作用。注意是一个long类型的数字
一般都是自己写一个版本号,也可以用idea自动生成,但是一般不建议。
使用自动生成的序列化版本号出现的问题:
不能修改原来的序列化类对象的代码,只要一修改代码,那么就无法反序列化(一旦代码确定之后,不能就行后续的修改,因为只要修改,必然会重新编译,此时就会生成全新的序列化版本号,这时候java虚拟机会认为这是个全新的类)
transient关键字
注意:
transient在类中修饰的变量的数据,不参与序列化的过程,反序列化后该变量为java默认值,比如 String s=“11”; 字符串,在序列化和反序列化后 ,得到的是String s=null;