序列化流
把对象写到文件中,并且写入后的文件我们是看不懂的。
为什么会有这种流出现呢?直接把对象的属性值写入不就可以了吗,但是并不想让别人看懂,因为看懂之后就可以修改,比如游戏中的数据随便进行修改,那还玩什么。所以这种方式可以实现加密。
序列化流又叫对象操作输出流。比如ObjectOutputStream。
下面是写入对象的一个例子:
public class IO14 {
public static void main(String[] args) throws IOException {
Student student = new Student();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("bbb/aaa.txt"));
oos.writeObject(student);
oos.close();
}
}
运行会发现有这个错误:
原因就是Student类并没有实现Serializable这个序列化接口。
这个接口中并没有任何的抽象方法,因为这是一个标记性接口,实现了这个接口的类表示这个类是可序列化的。
反序列化流
比如ObjectInputStream,也叫对象操作输入流,可以把序列化到本地文件中的对象读取到程序中去。
下面是一个读取的例子:
public class IO15 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("bbb//aaa.txt"));
Object o = ois.readObject();
System.out.println((Student)o);
}
}
只要对序列化文件进行了修改,哪怕是后面又改回来了也是不能反序列化的,只能重新再次序列化生成。
一些细节:
一个类如果实现Serializable这个序列化接口,那么Java会根据这个类中的成员变量、静态变量、成员方法、静态方法等等,也就是这个类中的所有的内容计算出一个long类型的值,叫做serialVersionUID,即序列号。而且把对象写入本地文件时会同时把序列号也写进去。
如果对类进行修改的话,Java会计算出一个新的序列号,那么把本地文件中的对象读取到程序中去时会发现读取出来的序列号和这个类的序列号不一样。
那么如何解决这个问题呢?
不修改类的内容那是不可能的,因为需求变更时类是肯定要进行修改的。
所以手动把序列号给定死,当类的内容修改时也不会变化。
序列号的3种实现方式:
(1)手动在类中写上一个变量,不推荐。
private final static long serialVersionUID = 1L;
priavte:表示不可被其他类访问;
fianl:表示不可被修改;
staic:表示类的所有对象共享;
(2)但是每次手动写好麻烦啊,而且也记不住。所以有快捷方式:在File > Settings > Editor > Inspections > JVM languages 下找到两个并勾选应用就可以了。
可以看到类名被淡棕色包裹,点击一下就可以看到生成序列号了,这个就是根据类中的内容生成的。
(3)借鉴别的类
比如ArrayList这个类就实现了Serializable这个接口,把它的序列号复制过来,但是要把值进行修改,改为1L,但是这个值显得不够高级,推荐使用方式二。
所以以后想把对象序列化到本地文件中去除了实现Serializable这个接口,还要加上序列号。
transient
如果对象的某个属性值并不想序列化本地文件中,可以加上transient关键字,也叫瞬态关键字,表示该关键字标记的成员变量的值不参与到序列化过程中。那么在读取的时候会根据类型赋一个默认值。
练习:将多个自定义对象序列化到本地文件中,但是并不知道对象的个数,反序列化流该如何读取呢?
比如序列化的本地文件是别人写的,但是现在他也忘记了有几个对象,那么你该如何读取呢?
举一个例子:比如存了3个对象,但是读取了4次,此时就会报一个错误:EOFEXception,即End Of File,读到了文件末尾的异常,这个方法是不会返回-1或者null的。
那么该怎么办呢?
所以要把对象序列化文件时就先放进集合中,然后再把集合序列化到文件中去。