序列化流
- 序列化
- java.io.ObjectOutputStream类,将java对象的原始数据类型写入到文件,实现对对象的持久存储,通俗来说,就是把对象以流的方式写入到文件中叫序列化.
- 构造方法:
public ObjectOutputStream(OutputStream out)
, 其中,参数为字节输出流.
- 序列化流特有的成员方法:
void writeObject(Object obj)
: 将指定的对象写入到流中
- 使用步骤:
- 创建一个ObjectOutputStream对象,在构造方法中传递字节输出流.
- 创建要写入到文件的对象,但是,注意这个对象所在的类一定要实现
Serializable
序列化接口,不然会报错没有序列化异常
.
- 用ObjectOutputStream对象中的writeObject方法把对象写入到文件中.
- 释放资源
- 案例(将person对象存放到项目目录下的指定文件中):
public class ObjectOutputStreamDemo1{
public static void main(String[] args) throws IOException {
//这里构造函数中的true是为了多次运行能把对象数据依次添加到上一条数据的后面而不是覆盖之前的数据
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("person序列化文件.txt", true));
Person person = new Person("fanlei", 18);
outputStream.writeObject(person);
outputStream.close();
}
}
//程序运行后,person就会写入到文件"person序列化文件.txt"中,不过我们打开是乱码,因为我们是二进制字节存储的,我们看不懂的,所以要想知道是啥,就得反序列化回去
- 注意: Serializable接口也叫标记性接口,它没有任何实现,只是一个空接口,但是要进行序列化或反序列化的类必须实现该接口,实现这个接口后它就会给类添加一个标记,当我们进行序列化和反序列化的时候,系统会检测这个即将被保存到文件中的对象所在的类是否有这个接口标记,有就正常序列化和反序列化,没有就报错–没有序列化异常.
- 你可以理解为去菜场买肉,肉上一般都有一个蓝色的章,写在检测合格,盖了章就说明能吃,没盖章的我反正不敢吃哈哈
反序列化流
- 反序列化
- java.io.ObjectInputStream类,将之前使用ObjectOutputStream类序列化的原始数据恢复为对象,通俗来说,就是把原来存放到文件的对象数据以流的方式读取出来,并转换为对象.
- 构造方法:
public ObjectInputStream(InputStream in)
, 其中,参数为字节输入流.
- 反序列化流特有的成员方法:
Object readObject()
: 从ObjectInputStream中读取对象
- 使用步骤:
- 创建一个ObjectInputStream对象,在构造方法中传递字节输入流.
- 使用ObjectInputStream对象中的readObject方法读取保存对象的文件.
- 释放资源.
- 使用读取出来的对象.
- 案例(还是要注意person类要实现serializable接口):
public class ObjectInputStreamDemo1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("person序列化文件.txt");
ObjectInputStre 大专栏 序列化和反序列化am inputStream = new ObjectInputStream(fileInputStream);
Person person = (Person) inputStream.readObject();
inputStream.close();
System.out.println(person);
}
}
- 注意:
- readObject()这个方法声明了两个异常:
IOException(io异常)
和ClassNotFoundException(class文件找不到异常)
,当不存在对象的class文件时,比如person对象对应的Person类不存在,就会抛出ClassNotFoundException
.
- 所以反序列化的前提有两个:
- 类必须要实现Serializable序列化标记接口
- 必须存在对象对应的class文件
transient(瞬态)关键字
static关键字–静态关键字: 静态随着类加载,被类的成员共享,优先于非静态加载到内存中(静态优先于对象进入到内存中)
注意: 被static修饰的成员变量不能被序列化,因为序列化的都是对象,静态成员不属于对象.
- 但是这个是不会报错的,如果你序列化的时候有静态成员变量,那么你反序列化之后,你会发现那个被静态修饰的成员变量为默认值,比如int类型就为0,不是你设置的值.
transient关键字: 又叫瞬态关键字, 它就是拿来让成员变量不被序列化的,即被它修饰的成员变量不能被序列化,效果跟static一样,不会报错,但是也不会按照你的想法被赋值,它只会是初值,int就是0,string就是null.
InvalidClassException
当序列化对象之后,该对象对应的class文件进行了修改,比如添加了一个属性啊之类的,然后我们再反序列化对象就会失败,同时报错
InvalidClassException(无效类异常,也叫序列号冲突异常)
产生该异常的原因: 当我们修改类之后,该类的序列化版本号系统会自动更新,然后与从流中读取的版本号不匹配,导致反序列化失败.
解决办法: 手动生成一个固定的序列号,不让系统自动更新就好了,具体的看下面的图吧.
idea可以自动生成序列号,我网上找了个博客,反正就那么几步,很简单的: idea自动生成序列号.
序列化和反序列化的过程
一个小练习
/**
* 序列化练习: 序列化集合
* 当我们想在文件中保存多个对象的时候,
* 我们就可以把多个对象存储到一个集合中,
* 对集合进行序列化和反序列化
*/
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
output();
input();
}
/**
* 序列化方法
*/
private static void output() throws IOException {
List<Person> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new Person("fanlei" + i, i));
}
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("list.txt"));
outputStream.writeObject(list);
System.out.println("序列化完成!");
System.out.println("---------------");
}
/**
* 反序列化方法
*/
private static void input() throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("list.txt"));
List<Person> list = (List<Person>) inputStream.readObject();
for (Person p : list) {
System.out.println(p);
}
}
}