对象输入输出流ObjectInputStream、ObjectOutputStream(对象序列化与反序列化)

对象的输入输出流 : 主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了
  对象的输出流: ObjectOutputStream
  对象的输入流:  ObjectInputStream

使用:

对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程,对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程。既然对象的输出流将对象写入到文件中称之为对象的序列化,那么可想而知对象所对应的class必须要实现Serializable接口。(查看源码可得知:Serializable接口没有任何的方法,只是作为一个标识接口存在)。

1、将User类的对象序列化

 1 class User implements Serializable{//必须实现Serializable接口
 2     String uid;
 3     String pwd;
 4     public User(String _uid,String _pwd){
 5         this.uid = _uid;
 6         this.pwd = _pwd;
 7     }
 8     @Override
 9     public String toString() {
10         return "账号:"+this.uid+" 密码:"+this.pwd;
11     }
12 }
13
14 public class Demo1 {
15
16     public static void main(String[] args) throws IOException {
17         //假设将对象信息写入到obj.txt文件中,事先已经在硬盘中建立了一个obj.txt文件
18         File f = new File("F:\\obj.txt");
19         writeObjec(f);
20         System.out.println("OK");
21     }
22
23     //定义方法把对象的信息写到硬盘上------>对象的序列化。
24     public static void writeObjec(File f) throws IOException{
25         FileOutputStream outputStream = new FileOutputStream(f);//创建文件字节输出流对象
26         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
27         objectOutputStream.writeObject(new User("酒香逢","123"));
28         //最后记得关闭资源,objectOutputStream.close()内部已经将outputStream对象资源释放了,所以只需要关闭objectOutputStream即可
29         objectOutputStream.close();
30     }
31 }

运行程序得到记事本中存入的信息:可见已经序列化到记事本中

2、将序列化到记事本的内容反序列化

 1 public class Demo1 {
 2
 3     public static void main(String[] args) throws IOException, ClassNotFoundException {
 4         //假设将对象信息写入到obj.txt文件中,事先已经在硬盘中建立了一个obj.txt文件
 5         File f = new File("F:\\obj.txt");
 6         //writeObjec(f);
 7         readObject(f);
 8         System.out.println("OK");
 9     }
10
11     //定义方法把对象的信息写到硬盘上------>对象的序列化。
12     public static void writeObjec(File f) throws IOException{
13         FileOutputStream outputStream = new FileOutputStream(f);//创建文件字节输出流对象
14         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
15         objectOutputStream.writeObject(new User("酒香逢","123"));
16         //最后记得关闭资源,objectOutputStream.close()内部已经将outputStream对象资源释放了,所以只需要关闭objectOutputStream即可
17         objectOutputStream.close();
18     }
19     //把文件中的对象信息读取出来-------->对象的反序列化
20     public static void readObject(File f) throws IOException, ClassNotFoundException{
21         FileInputStream inputStream = new FileInputStream(f);//创建文件字节输出流对象
22         ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
23         User user = (User)objectInputStream.readObject();
24         System.out.println(user);
25     }
26 }

运行代码得到的结果:

账号:酒香逢 密码:123
OK

但是,如果这时候这个obj.txt是我们项目中一个文件,而项目到后期在原来User类的基础上添加成员变量String userName;

 1 class User implements Serializable{//必须实现Serializable接口
 2     String uid;
 3     String pwd;
 4     String userName="名字";//新添加的成员变量
 5     public User(String _uid,String _pwd){
 6         this.uid = _uid;
 7         this.pwd = _pwd;
 8     }
 9     @Override
10     public String toString() {
11         return "账号:"+this.uid+" 密码:"+this.pwd;
12     }
13 }

这时候如果我们再反序列化,则会引发下面的异常:

Exception in thread "main" java.io.InvalidClassException: xuliehua.User; local class incompatible: stream classdesc serialVersionUID = 2161776237447595412, local class serialVersionUID = -3634244984882257127
  at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
  at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
  at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
  at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
  at xuliehua.Demo1.readObject(Demo1.java:48)
  at xuliehua.Demo1.main(Demo1.java:32)

异常信息解读:

serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是JVM(JAVA虚拟界)通过一个类的类名、成员、包名、工程名算出的一个数字。而这时候序列化文件中记录的serialVersionUID与项目中的不一致,即找不到对应的类来反序列化。

3、如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。

去掉刚才添加的成员变量userName;,并且在User类中指定一个serialVersionUID

 1 class User implements Serializable{//必须实现Serializable接口
 2
 3     private static final long serialVersionUID = 1L;
 4     String uid;
 5     String pwd;
 6     //String userName="名字";//新添加的成员变量
 7     public User(String _uid,String _pwd){
 8         this.uid = _uid;
 9         this.pwd = _pwd;
10     }
11     @Override
12     public String toString() {
13         return "账号:"+this.uid+" 密码:"+this.pwd;
14     }
15 }

重新序列化到obj.txt文件中,然后再类中再将userName添加回来(将上面User类中userName字段解注释),再一次执行反序列化操作,执行的结果跟之前反序列化的结果是一致的。可见这样解决后我们后期修改类也是可行的。

4、如果在User类中再添加成员变量,而这个变量为一个class ,如Address,那么Address类也必须要实现Serializable接口。

 1 class Address implements Serializable{
 2     String country;
 3     String city;
 4 }
 5
 6 class User implements Serializable{//必须实现Serializable接口
 7
 8     private static final long serialVersionUID = 1L;
 9     String uid;
10     String pwd;
11     String userName="名字";//新添加的成员变量
12     Address address;//成员变量为Address
13     public User(String _uid,String _pwd){
14         this.uid = _uid;
15         this.pwd = _pwd;
16     }
17     @Override
18     public String toString() {
19         return "账号:"+this.uid+" 密码:"+this.pwd;
20     }
21 }

5、最后再提一下关键字transient关键字,当你不想要某些字段序列化时候,可以用transient关键字修饰

 1 class User implements Serializable{//必须实现Serializable接口
 2
 3     private static final long serialVersionUID = 1L;
 4     String uid;
 5     String pwd;
 6     transient String userName="名字";//新添加的成员变量//添加关键字transient后,序列化时忽略
 7     Address address;//成员变量为Address
 8     public User(String _uid,String _pwd){
 9         this.uid = _uid;
10         this.pwd = _pwd;
11     }
12     @Override
13     public String toString() {
14         return "账号:"+this.uid+" 密码:"+this.pwd;
15     }
16 }

最后总结一下对象输入输出流使用时需要注意:

1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
2. 对象的反序列化创建对象的时候并不会调用到构造方法的、(这点文中没有说到,想要验证的同学在构造方法后面加一句System.out.println("构造方法执行吗?");,实际上构造方法是不执行的,自然这句话也没有输出了)
3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID
进行对比,如果这两个id不一致,反序列则失败。
5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
7. 如果一个类维护了另外一个类的引用,则另外一个类也需要实现Serializable接口。

原文地址:https://www.cnblogs.com/renjiaqi/p/10398546.html

时间: 2024-10-11 04:31:56

对象输入输出流ObjectInputStream、ObjectOutputStream(对象序列化与反序列化)的相关文章

(JAVA)从零开始之--对象输入输出流ObjectInputStream、ObjectOutputStream(对象序列化与反序列化)

对象的输入输出流 : 主要的作用是用于写入对象信息与读取对象信息. 对象信息一旦写到文件上那么对象的信息就可以做到持久化了 对象的输出流: ObjectOutputStream 对象的输入流:  ObjectInputStream 使用: 对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程,对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程.既然对象的输出流将对象写入到文件中称之为对象的序列化,那么可想而知对象所对应的class必须要实现Serializable接

ObjectInputStream || ObjectOutputStream 序列化对象输入输出流Demo 学习

A:Api 说明: ObjectOutputStream 写入的基本数据和对象 *(内存 到 硬盘 对象的 存储!!) ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化. ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储.ObjectInputStream 用于恢复那些以

I/O 对象输入输出流

package com.xuexi.IO; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Obj { public static void main(St

对象、流、磁盘、序列化、反序列化等概念之间的关系

(1)对象—到—> 流.磁盘.内存.网络等等,称之为序列化 :反之称之为“反序列化” (2)磁盘 —到—>  流 ,称之为 “流化”, 然后,流—到—>对象,称之为“反序列化” (3)对象 .流.内存.网络—到—>磁盘,通常也称之为 “持久化” 原文地址:https://www.cnblogs.com/changbaishan/p/11567320.html

java 对象序列化与反序列化

Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程: Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化与反序列化 我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本.图片.音频.视频等, 而这些数据都会以二进制序列的形式在网络上传送.那么当两个Java进程进行通信时,能否实现进程间的

访问修饰限定符的简单总结、final/abstruct/interface对类的限制、自动加载机制、序列化与反序列化【数据持久化和对象的序列化问题】、对象的拷贝(按引用是因为对象标识)和克隆(__clone方法中的this指向)

1.针对访问修饰限定符的理解只需要两点:(1)针对的是类的概念和访问代码的位置来确定是否能够访问(2)对访问修饰限定符的使用时只需要对该成员的使用场景注意即可[也就是内部,继承类,外部进行访问的权限] 不需要对内部进行太多理解[需要对php底层理解时进行理解] [重点][用途]通过访问修饰限定符将内部成员的权限合理的限制,然后再使用公共接口来调用这个基本服务,保证外部不能访问其内部的构件[这样既能够通过类内的设置,将内部的功能实现更好的限制,只有最外层的接口可以正常被访问到,而不了解内部的业务]

序列化、反序列化的版本控制以及序列化、反序列化集合对象

当涉及到跨进程甚至是跨域传输数据的时候,我们需要把对象序列化和反序列化. 首先可以使用Serializable特性. [Serializable] public class Person { public string _firstName; public string _secondName; //序列化 [OnSerializing] internal void OnSerializing(StreamingContext context) { _firstName = _firstName

Java高级特性 第4节 输入输出流

一.使用I/O操作文件 关键步骤: 使用File类操作文件或目录属性 使用FileInputStream类读文本文件 使用FileOutputStram类写文本文件 使用BufferedReader类和FileReader类读文本文件 使用BufferedWriter类和FileWriter类读文本文件 使用DataInputStream类读二进制文件 使用DataOutputStream类写二进制文件 重定向标准I/O   1.使用File类操作文件或目录属性   java.io包提供了一些接

71 Serializable(序列化和反序列化)

对象的输出流:ObjectOutputStream  把对象输出到文件存储起来,我们称作为序列化对象的输入流:ObjectInputStream   把对象从文件中读取出来,我们称作为反序列化 ObjectOutputStream    构造方法:        ObjectOutputStream()            为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据.        Ob