【Java学习笔记】36.Java 序列化
创始人
2025-05-31 12:49:11
0

Java 序列化

Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。

将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。

类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。

ObjectOutputStream 类包含很多写方法来写各种数据类型,但是一个特别的方法例外:

public final void writeObject(Object x) 抛出 IOException

上面的方法序列化一个对象,并将它发送到输出流。相似的 ObjectInputStream 类包含如下反序列化一个对象的方法:

public final  Object readObject() 抛出 IOException, ClassNotFoundException

该方法从流中取出下一个对象,并将对象反序列化。它的返回值为Object,因此,你需要将它转换成合适的数据类型。

为了演示序列化在Java中是怎样工作的,我将使用之前教程中提到的Employee类,假设我们定义了如下的Employee类,该类实现了Serializable 接口。

Employee.java 文件代码:

公共类 Employee 实现 java。哎呀。可序列化 { 公共字符串名称; 公共字符串地址; 公共瞬态 INT SSN; 公共整数; public void mailCheck() { System.出。println(“邮寄支票到” + 姓名 + “  ” + 地址);}
}

请注意,一个类的对象要想序列化成功,必须满足两个条件:

该类必须实现 java.io.Serializable 接口。

该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

如果你想知道一个 Java 标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。

序列化对象

ObjectOutputStream 类用来序列化一个对象,如下的 SerializeDemo 例子实例化了一个 Employee 对象,并将该对象序列化到一个文件中。

该程序执行后,就创建了一个名为 employee.ser 文件。该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。

注意: 当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。

SerializeDemo.java 文件代码:

导入 Java。io.*; public class SerializeDemo  { public  static void main(String [] args) {  Employee e = 新员工(); e.名字=“雷扬·阿里”; e.地址 = “Phokka Kuan, Ambehta Peer”; e.SSN = 11122333; e.数字 = 101; try {  FileOutputStream fileOut = new FileOutputStream(“/tmp/employee.ser”); ObjectOutputStream out = new ObjectOutputStream(fileOut); 出。写对象(e); 出。关闭(); 文件输出。关闭(); 系统。出。printf(“序列化数据保存在 /tmp/employee.ser”); }catch(IOException  i) { i.打印堆栈跟踪();}}
}

反序列化对象

下面的 DeserializeDemo 程序实例了反序列化,/tmp/employee.ser 存储了 Employee 对象。

反序列化演示.java 文件代码:

导入 Java。io.*; public class DeserializeDemo  { public  static void main(String [] args) {  Employee e = 空; try {  FileInputStream fileIn = new FileInputStream(“/tmp/employee.ser”); ObjectInputStream in = new ObjectInputStream(fileIn); e = (员工) in.readObject(); 在。关闭(); 文件在。关闭(); }catch(IOException  i) { i.printStackTrace(); 返回;}catch(ClassNotFoundException c) { system.出。println(“找不到员工类”); 三.printStackTrace(); 返回; } 系统。出。println(“反序列化员工...“); 系统。出。println(“Name: ” + e.名称); 系统。出。println(“地址: ” + e.地址); 系统。出。println(“SSN: ” + e.SSN); 系统。出。println(“Number: ” + e.数);}
}

以上程序编译运行结果如下所示:

Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101

这里要注意以下要点:

readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象,它必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个 ClassNotFoundException 异常。

注意,readObject() 方法的返回值被转化成 Employee 引用。

当对象被序列化时,属性 SSN 的值为 111222333,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后 Employee 对象的 SSN 属性为 0。

Serializable 的作用

为什么一个类实现了Serializable接口,它就可以被序列化呢?在上节的示例中,使用ObjectOutputStream来持久化对象,在该类中有如下代码:

private void writeObject0(Object obj, boolean unshared) throws IOException {...if (obj instanceof String) { writeString((String) obj, unshared);} else if (cl.isArray()) { writeArray(obj, desc, unshared);} else if (obj instanceof Enum) {writeEnum((Enum) obj, desc, unshared);} else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);} else {if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n"+ debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}}...
} 

从上述代码可知,如果被写对象的类型是String,或数组,或Enum,或Serializable,那么就可以对该对象进行序列化,否则将抛出NotSerializableException。

关于 java 中的序列化与反序列化

关于序列化,常又称为持久化,将其写入磁盘中。

进而对于编码规则来说:

任一一个实体类必须要去实现 Serializable 接口,方便以后将该类持久化,或者将其用于转为字节数组,用于网络传输。

对于一个实体类,不想将所有的属性都进行序列化,有专门的关键字 transient:

private transient String name;

当对该类序列化时,会自动忽略被 transient 修饰的属性。

关于 SerializableID

SerializableID 号是根据类的特征和类的签名算出来的。为什么 ID 号那么长,是因为为了避免重复。所以 Serializable 是给类加上 id 用的。用于判断类和对象是否是同一个版本。

如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值。原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。

序列化流与反序列化流

ObjectOutputStream(序列化流)
ObjectOutputStream是序列化流,可以将Java程序中的对象写到文件中。

ObjectOutputStream 构造方法:

ObjectOutputStream(OutputStream out):参数要传递字节输出流。

ObjectOutputStream写对象的方法(特有方法):

void writeObject(Object obj): 向文件中写对象。

ObjectOutputStream 的使用步骤:

  • 创建序列化流,用来写。
  • 调用 writeObject 方法,写对象。
  • 释放资源。

tips: 要使用序列化流向文件中写的对象,必须实现 Serializable 接口。

public class Demo01ObjectOutputStream {public static void main(String[] args) throws IOException {//1. 创建序列化流,用来写ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file03-obj.txt"));//2. 调用writeObject方法,写对象Person p = new Person("张三丰", 100);oos.writeObject(p);//3. 释放资源。oos.close();}
}

ObjectInputStream(反序列化流)
ObjectInputStream 是反序列化流, 可以将文件中的对象读取到 Java 程序中。

ObjectInputStream 的构造方法:

ObjectInputStream(InputStream in):参数要传递字节输入流。

ObjectInputStream 读取对象的方法(特有的方法):

Object readObject(): 从文件中读取对象,并将该对象返回。

反序列化流的使用步骤:

  • 创建 ObjectInputStream 反序列化流。
  • 调用 readObject 方法,读取对象。
  • 释放资源。

tips:调用 readObject 方法读取对象时,对象所对应的类不存在,那么会报错(ClassNotFoundException)

特殊情况:

被 static 修饰的成员变量无法序列化,无法写到文件。

如果不希望某个成员变量写到文件,同时又不希望使用 static 关键字, 那么可以使用 transient。transient 关键字表示瞬态,被 transient 修饰的成员变量无法被序列化。

public class Demo03StaticAndTransient {public static void main(String[] args) throws IOException, ClassNotFoundException {writePerson();readPerson();}//从文件中读取Person对象public static void readPerson() throws IOException, ClassNotFoundException {//创建反序列化流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\file04-obj.txt"));//从文件中读取对象Object obj = ois.readObject();System.out.println(obj);//释放资源ois.close();}//向文件中写Person对象public static void writePerson() throws IOException {//创建序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file04-obj.txt"));//向文件中写Person对象oos.writeObject(new Person("张三丰", 100));//关流oos.close();}
}
public class Demo04SerialVersionUID {public static void main(String[] args) throws IOException, ClassNotFoundException {//writePerson();readPerson();}//从文件中读取Person对象public static void readPerson() throws IOException, ClassNotFoundException {//创建反序列化流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\file04-obj.txt"));//从文件中读取对象Object obj = ois.readObject();System.out.println(obj);//释放资源ois.close();}//向文件中写Person对象public static void writePerson() throws IOException {//创建序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\file04-obj.txt"));//向文件中写Person对象oos.writeObject(new Person("张三丰", 100));//关流oos.close();}
}

例题

/*练习:1. 将存有多个Student对象的集合序列化操作,保存到list.txt 文件中。2. 反序列化list.txt ,并遍历集合,打印对象信息。步骤:1. 创建集合,用来保存Student2. 向集合中添加Student对象。3. 创建ObjectOutputStream序列化流,用来写。4. 调用writeObject方法,向文件中写集合对象。5. 释放资源。6. 创建ObjectInputStream反序列化流对象,用来读取7. 调用readObject方法,从文件中读取对象。8. 将读取到的集合进行遍历,并输出结果。注意:如果想要将多个对象保存在文件中,最好的一个方式可以将多个对象放入到一个集合中,然后直接将集合写到文件中。*/
public class Demo05Test {public static void main(String[] args) throws IOException, ClassNotFoundException {//1. 创建集合,用来保存StudentList list = new ArrayList<>();//2. 向集合中添加Student对象。list.add(new Student("李云龙", 20));list.add(new Student("二营长", 22));list.add(new Student("秀琴", 25));//3. 创建ObjectOutputStream序列化流,用来写。ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\list.txt"));//4. 调用writeObject方法,向文件中写集合对象。oos.writeObject(list);//5. 释放资源。oos.close();//6. 创建ObjectInputStream反序列化流对象,用来读取ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\list.txt"));//7. 调用readObject方法,从文件中读取对象。List list2 = (List) ois.readObject();//8. 将读取到的集合进行遍历,并输出结果。for (Student stu : list2) {System.out.println(stu);}}
}

相关内容

热门资讯

A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
有关于写给公安机关的感谢信 写... 公安机关内部管理制度是否完善,对于确保公安队伍建设和管理,保证公安机关顺利地履行职责和任务,具有重大...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
有关于写给公安局的一封感谢信 ... 公安局对固定资产指标的要求也越来越多,固定资产管理工作成为公安局日常一项很重要的工作。那么xxx应该...
有关于致公交公司的感谢信 给公... 公交公司的生产经营活动不仅具有经济效益,而且具有社会效益。那么xxx应该怎么写呢?下面小编整理了有关...
有关于优秀员工致公司的感谢信 ... 有对自然物象的感谢,对伟大人物的感谢,更有对日常生活的感谢。那么关于优秀员工致公司的感谢信应该怎么写...
有关于员工致公司领导的感谢信 ... 谢语是以表达感谢为要点。那么有关于员工致公司领导的感谢信应该怎么写呢?下面小编整理了有关于员工致公司...
药品批准文号查询|药融云-中国... 药品批文是国家食品药品监督管理局(NMPA)对药品的审评和批准的证明文件...
最新或2023(历届)给公司领...  给公司领导的感谢信范文一:  敬爱的领导:  你们好,回想在港城办事处三年多的日子里,我得到了各位...
最新或2023(历届)企业致客...   企业致客户的感谢信范文一:  尊敬的客户:  您好!  新的一年就要到来了,怀着感恩的心情,向您...
给公司捐款的感谢信范文 公司捐...  给公司捐款的感谢信范文一:  尊敬的领导、工友们:  你们好!首先我想表达我们一家人最诚挚的谢意和...
python 基础系列篇:四、... python 基础系列篇:四、编写两个简单的小游戏(猜数字及2048&#...
写给居委会的感谢信范文 怎么给... 写给居委会的感谢信范文一:  尊敬的上汽社区居委会领导们:  您们好!  首先感谢上汽社区居委会的各...
最新或2023(历届)终给客户...  年终给客户的感谢信范文一:  尊敬的客户:  您好!新的一年即将到来,我们怀着感恩的心情向您致以亲...
在PyCharm中运行Pyth... 先看一个报错: Traceback (most recent call last):F...
最新或2023(历届)给淘宝买... 给淘宝买家的感谢信范文一:  亲:  您好!  首先在此感谢您对本店的关注与支持!我们是一群年轻上进...
写给物业的一封感谢信范文 怎么... 写给物业的感谢信范文一:  XX建物业管理有限公司成都分公司:  新年伊始辞旧岁,万象更新迎新春。公...
关于最新或2023(历届)企业...  企业对员工的感谢信范文一:  尊敬的各位同事:  大家好!为应对公司生产一线人员缺口,同时保障公司...
最新或2023(历届)企业对员...  企业对员工的感谢信范文一:  尊敬的各位同事:  大家好!为应对公司生产一线人员缺口,同时保障公司...
最新或2023(历届)教你怎么... 给领导的感谢信范文一:  尊敬的xx县交巡警大队领导:  我们是xxx丝绸印花有限公司,今天来信的目...