JavaSE系列详解|Java基础IO流之序列化流的使用

☆引言☆ 大家好,我是痛而不言笑而不语的浅伤。上一章我们学习了IO流中的递归,本章我们一起来学习IO流中的序列号流。这篇文章来自我的《吃透JavaSE基础》专栏中,想要学习更多JavaSE基础,订阅专栏《吃透JavaSE基础》。对文章中描述错误的希望大家积极指出。
【JavaSE系列详解|Java基础IO流之序列化流的使用】博客首页:痛而不言笑而不语的浅伤
欢迎关注点赞收藏留言
?:热爱Java学习,期待一起交流!
作者水平很有限,如果发现错误,求告知,多谢!
有问题可以私信交流!!!
我想要个关注

目录
☆引言☆
?
什么是序列化流?
序列化流的分类
序列化
作用
构造方法
特有的成员方法
使用步骤
反序列化
作用
构造方法
特有的成员方法
使用步骤
transient关键字
InvalidClassException异常
练习:序列化集合
总结:

什么是序列化流? Java提供了一种对象序列化的机制。用一个字节系列可以表示一个对象,该字节系列包含对象的数据、对象的类型、对象中存储的属性等信息。通俗易懂的说,就是把我们创建的对象进行保存到文件中和从文件中读取出来的操作过程就叫序列化流。
序列化流的分类 序列化流分为序列化和反序列化。
把字节序列写出到文件之后,相当于文件中持久保存了一个对象信息,也就是序列化。
反之,该字节序列还可以从文件中读取回来,重构对象,也就是反序列化。
那具体的怎么使用,我们继续往下学吧!我们先看图理解一下吧:
JavaSE系列详解|Java基础IO流之序列化流的使用
文章图片

序列化 把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化。对象就是Object,转换成字节,写入字节流就是OutputStream,那我们就使用ObiectOutputStream类把对象序列化。

ObiectOutputStream既然是字节流,我们就看一下它的继承关系如下:
ObjectOutputStream extends OutputStream extends Object
既然要学习序列化,也就是学习ObiectOutputStream这个类,我们就从它的作用、构造方法、成员方法和使用步骤几方面学习。
作用
它的作用就是把对象以流的方式写入到文件中进行保存
构造方法
ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
参数:
OutputStream out:字节输出流
特有的成员方法
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
使用步骤
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObjectW方法,把对象写到文件中
3.释放资源
既然我们要把对象进行序列化首先要创建一个对象,而对这个对象进行序列化就必须需要实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使用其任何状态序列化和反序列化如果序列化的时候没有实现 Serializable接口,会抛出NotSerializableException没有序列化异常。Serializable接口也叫标记型接口,要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记,当我们进行序列化和反序列化的时候,就会检查类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出java.io.NotSerializableException异常
知道这些,我们就创建一个person对象类
代码演示如下:

//创建人person对象并实现Serializable接口 public class Person implements Serializable{ //定义成员变量 private String name; private int age; //创建无参构造方法 public Person() { super(); } //创建带参构造方法 public Person(String name, int age) { super(); this.name = name; this.age = age; } //重写to String方法 @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } //添加get和set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

创建序列化类并使用
public static void main(String[] args) throws IOException /*此处需要抛出 IOException异常*/{ //1.创建ObjectOutputStream对象,构造方法中传递字节输出流 ObjectOutputStream obj1=new ObjectOutputStream(new FileOutputStream("F:\\workspace\\新建文件夹\\a.txt")); //2.使用ObjectOutputStream对象中的方法writeObjectW方法,把对象写到文件中 obj1.writeObject(new Person("张三",25)); //3.释放资源 obj1.close(); }

结果:在此“F:\workspace\新建文件夹”路径中的a.txt中就保存了以二进制字节显示的数据
我们把对象序列化保存到了文件中,那还要读取出来,所以我们继续学习反序列化。
反序列化 把文件中保存的对象,以流的方式方式读取出来,叫读对象,也叫对象的反序列化。同样,对象Object。保存的对象是字节,我们读取对象,读出来也是字节,就用读字节流InputStream,所以我们用ObjectInputStream类来把对象反序列化。
ObjectInputStream既然是字节流,我们就看一下它的继承关系如下:
ObjectInputStream extends InputStream
既然序列化我们从作用,构造方法,成员方法和使用步骤几方面学习了,反序列化我们也从这几方面学习。
作用
把文件中保存的对象,以流的方式读取出来使用
构造方法
ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。
参数:
InputStream in :字节输入流
特有的成员方法
Object readObject() 从 ObjectInputStream 读取对象。
使用步骤
1.创建ObjectInputStream对象,构造方法中传递字节输出流
2.使用ObjectInputStream对象中的readObject方法把对象从文件中读取出来
3.释放资源
4.使用读取出来的对象(打印)
既然我们要把对象进行反序列化,前提就是我们已经把这个对象做了序列化,所以还是要基于序列化时创建的人person这个对象来举例
代码演示如下:
person对象
//这是person对象,它实现Serializable接口 public class Person implements Serializable{ //定义成员变量 private String name; private int age; //创建无参构造方法 public Person() { super(); } //创建带参构造方法 public Person(String name, int age) { super(); this.name = name; this.age = age; } //重写to String方法 @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } //添加get和set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

创建反序列化类并使用
反序列化的前提:
1.类必须实现Serializable接口
2.必须存在对应的class文件
该类需要抛出两个异常:一个 IOException,另一个ClassNotFoundExceptionclass文件找不到异常
public static void main(String[] args) throws IOException, ClassNotFoundException { //1.创建ObjectInputStream对象,构造方法中传递字节输出流 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("F:\\workspace\\新建文件夹\\a.txt")); //2.使用ObjectInputStream对象中的readObject方法把对象从文件中读取出来 Object o=ois.readObject(); //3.释放资源 ois.close(); //4.使用读取出来的对象(打印) System.out.println(o); }

打印结果:Person [name=张三, age=25]
transient关键字 transient关键字:是瞬态关键字
被transient修饰的成员变量不能被序列化
如果成员变量不想被序列化就用此关键字修饰
例如:
public class Person implements Serializable{//定义成员变量private String name; //如果在年龄age前面加上transient关键字private transient int age; //不会被序列化

不被序列化打印的结果:Person [name=张三, age=0]
年龄为什么是0,因为年龄没有被序列化,而成员变量的默认值是0.所以显示age=0.
InvalidClassException异常 这个异常是什么异常呢?就是当我们把一个对象序列化了,然后又进行了修改,再没有序列化的情况下又对这个对象反序列化了,就会出现该异常。
当我们序列化一个对象的时候,我们的对象就会算出一个序列号,如下图:-64726,我们序列化完成后保存的文件也会产生一个和对象序列号相同序列号,如下图-64726。我们反序列化时,这两个序列号会进行比较,相同后才会读取被序列化被保存的文件。而我们又对该对象进行了修改后,该对象的序列号发生了改变,如下图:-32969,然后我们再次行进反序列化读取文件,就会出错,两个序列号不一样,就会发生异常。
详细的看一下下图:
JavaSE系列详解|Java基础IO流之序列化流的使用
文章图片

那我们怎么解决这个问题呢?其实很简单,我们在创建对象时,就提前给对象创建一个序列号,不管怎么修改对象,都不会发生改变就可以保持一致了。我们用如下代码:
public class Person implements Serializable { //序列化号,使修改参数后反序列化的文件不会抛出异常 //在对象中添加一行由private、 static、 final、 long这个几个关键字修饰的代码 private static final long serialVersionUID=1L; //后面的代码不变 private String name; private int age; //.......

练习:序列化集合 学会了序列化流,我做个练习吧
当我们想在文件中保存多个对象的时候
可以把多个对象存储到一个集合中
对集合进行序列化和反序列化
习题分析:
1.定义一个存储person对象的ArrayList集合
2.往ArrayList集合中存储person对象
3.创建一个序列化流ObjectOutoutStream对象
4.使用ObjectOutoutStream对象中的方法writeObject,对集合进行序列化
5.创建一个反序列化流ObjectInputStream对象
6.使用ObjectInputStream对象中的方法readObject,读取文件中保存的集合
7.把Object类型的集合转换成ArrayList类型
8.遍历ArrayList集合
9.释放资源
我们用以上9步完成,代码如下:
对象还是之前的person对象,直接拿过来。
public class Person implements Serializable{ //添加序列化号,使修改参数后反序列化的文件不会抛出异常 private static final long serialVersionUID=1L; //定义成员变量 private String name; private int age; //创建无参构造方法 public Person() { super(); } //创建带参构造方法 public Person(String name, int age) { super(); this.name = name; this.age = age; } //重写to String方法 @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } //添加get和set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

创建练习序列化集合的类
public class Demo03Test { public static void main(String[] args) throws IOException, ClassNotFoundException { //1.定义一个存储person对象的ArrayList集合 ArrayList list=new ArrayList<>(); //2.往ArrayList集合中存储person对象 list.add(new Person("张三",34)); list.add(new Person("李四",25)); list.add(new Person("王五",28)); //3.创建一个序列化流ObjectOutoutStream对象 ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("F:\\workspace\\新建文件夹\\a.txt") ); //4.使用ObjectOutoutStream对象中的方法writeObject,对集合进行序列化 oos.writeObject(list); //5.创建一个反序列化流ObjectInputStream对象 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("F:\\workspace\\新建文件夹\\a.txt")); //6.使用ObjectInputStream对象中的方法readObject,读取文件中保存的集合 Object o= ois.readObject(); //7.把Object类型的集合转换成ArrayList类型 ArrayListlist2=(ArrayList)o; //8.遍历ArrayList集合 for (Person p : list2) { System.out.println(p); } //9.释放资源 oos.close(); ois.close(); } }

打印结果:
Person [name=张三, age=34]
Person [name=李四, age=25]
Person [name=王五, age=28]
总结: 好了以上就是序列化流的一些学习,你是否学会了呢?今日的分享到此结束,由于笔者还在学习的路上辗转徘徊,水平有限,文章中可能有一些不对之处,还请各位大佬指正,最后祝愿每一个热爱编程的小伙伴,学习的路上不迷路,实现自己的追求。如果大家觉得还不错,希望多多支持一下,点赞,关注加收藏。
JavaSE系列详解|Java基础IO流之序列化流的使用
文章图片


    推荐阅读