对象的序列化和反序列化实践

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

  把Java对象转换为字节序列的过程称为对象的序列化。


  把字节序列恢复为Java对象的过程称为对象的反序列化。

  对象的序列化主要有两种用途:

  1 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

  2 在网络上传送对象的字节序列。

  一. JDK类库中的序列化API

  java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。、

  只有实现了SerializableExternalizable接口的类的对象才能被序列化。Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式

  对象序列化包括如下步骤:

  1 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;

  2 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:

  1 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;

  2 通过对象输入流的readObject()方法读取对象。

  下面让我们来看一个对应的例子,类的内容如下:

import java.io.*;
import java.util.Date;

/**
*
对象的序列化和反序列化测试类
.
* @author
AmigoXie
* @version 1.0
* Creation date: 2007-9-15 -
下午21:45:48
*/

public class ObjectSaver {
 
/**
 
* @param args
 
* @author AmigoXie
 * Creation date: 2007-9-15 - 下午21:45:37
 
*/

public static void main(String[] args) throws Exception {
 
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream("D:""objectFile.obj"));

 //序列化对象


 Customer customer = new Customer("阿蜜果", 24);
 out.writeObject("你好
!");
 
out.writeObject(new Date());
 
out.writeObject(customer);
 out.writeInt(123); //写入基本类型数据

 out.close();
 //反序列化对象


 ObjectInputStream in = new ObjectInputStream
(new FileInputStream("D:""objectFile.obj"));

 
System.out.println("obj1=" + (String) in.readObject());
 
System.out.println("obj2=" + (Date) in.readObject());
 
Customer obj3 = (Customer) in.readObject();
 
System.out.println("obj3=" + obj3);
 
int obj4 = in.readInt();
 
System.out.println("obj4=" + obj4);
 
in.close();
}
}

class Customer implements Serializable {
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}

public String toString() {
return "name=" + name + ", age=" + age;
}
}


obj1=你好!

obj2=Sat Sep 15 22:02:21 CST 2007

obj3=name=
阿蜜果
, age=24

obj4=123


private void writeObject(java.io.ObjectOutputStream out) throws IOException

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;


public void writeExternal(ObjectOutput out) throws IOException

public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException


private static final long serialVersionUID;


  以上serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。

  类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高哦啊serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途:

  1 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID

  2 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID
  前者负责序列化操作,后者负责反序列化操作。

  在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor异常。

  四. 可序列化类的不同版本的序列化兼容性

  凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
  当ObjectOutputStream对一个Customer对象进行序列化时,如果该对象具有writeObject()方法,那么就会执行这一方法,否则就按默认方式序列化。在该对象的writeObjectt()方法中,可以先调用ObjectOutputStreamdefaultWriteObject()方法,使得对象输出流先执行默认的序列化操作。同理可得出反序列化的情况,不过这次是defaultReadObject()方法。

  有些对象中包含一些敏感信息,这些信息不宜对外公开。如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。对于这类信息,可以对它们进行加密后再序列化,在反序列化时则需要解密,再恢复为原来的信息。

  默认的序列化方式会序列化整个对象图,这需要递归遍历对象图。如果对象图很复杂,递归遍历操作需要消耗很多的空间和时间,它的内部数据结构为双向列表。

  在应用时,如果对某些成员变量都改为transient类型,将节省空间和时间,提高序列化的性能。

  三. 实现Externalizable接口

  Externalizable接口继承自Serializable接口,如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。Externalizable接口声明了两个方法:
  因此例比较简单,在此不再详述。

  二.实现Serializable接口

  ObjectOutputStream只能对Serializable接口的类的对象进行序列化。默认情况下,ObjectOutputStream按照默认方式序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量。

  当ObjectOutputStream按照默认方式反序列化时,具有如下特点:

  1 如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException

  2 在反序列化时不会调用类的任何构造方法。

  如果用户希望控制类的序列化方式,可以在可序列化类中提供以下形式的writeObject()readObject()方法。
  输出结果如下:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端数据交互。序列是将数据转换为JSON字符串的过程,而序列则是将JSON字符串恢复成原始数据结构。 **序列过程:** 1. 将复杂的数据结构(如对象、数组、自定义类型等)转换为键值对的形式。 2. 使用JSON.stringify() 方法,提供一个JavaScript对象或数组作为参数,这个方法会返回一个字符串,其中包含了对应的JSON格式。 3. 字符串中的键用双引号包围,值用逗号分隔,整个结构被大括号或方括号包围。 **序列过程:** 1. 使用JSON.parse() 方法,将JSON字符串作为参数,这个方法解析输入的字符串并返回对应的JavaScript对象或数组。 2. 序列后的对象可以直接在JavaScript中使用,其结构与原始数据一致。 **学习步骤:** 1. **基础理解**:了解JavaScript的数据类型和基本语法,因为JSON是基于JavaScript的。 2. **JSON语法**:掌握JSON的基本语法,包括键值对、数组、嵌套对象等。 3. **API学习**:熟悉JavaScript的JSON.stringify() 和 JSON.parse() 函数的用法和参数。 4. **示例实践**:通过编写代码进行序列序列的操作,例如在Node.js、浏览器环境中尝试操作。 5. **错误处理**:学习处理可能出现的错误,比如无效的JSON字符串、循环引用等问题。 6. **进阶内容**:如果涉及复杂数据结构,可能需要学习如何自定义序列序列逻辑,以及如何使用第三方库(如axios的transformRequest和transformResponse选项)。 **相关问题--:** 1. JSON.stringify() 和 JSON.parse() 分别做什么? 2. 如何处理JSON.stringify() 中的特殊字符? 3. JavaScript中如何处理循环引用导致的序列问题?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值