Java对象的序列化与反序列化详解

一、序列化和反序列化的概念

把对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种途径:

Ⅰ . 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中
Ⅱ.  在网络上传送对象的字节序列。

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

二、序列化API

1. 对象输出流(ObjectOutputStream)的常用方法:

// 创建写入指定 OutputStream 的 ObjectOutputStream。此构造方法将序列化流部分写入底层流
public ObjectOutputStream(OutputStream out) throws IOException
// 将指定的对象写入 ObjectOutputStream
public final void writeObject(Object obj) throws IOException

2. 对象输入流(ObjectInputStream)的常用方法:

// 创建从指定 InputStream 读取的 ObjectInputStream。
public ObjectInputStream(InputStream in) throws IOException
// 从 ObjectInputStream 读取对象。对象的类、类的签名和类及所有其超类型的非瞬态和非静态字段的值都将被读取。
public final Object readObject() throws IOException, ClassNotFoundException

范例:对象序列化与反序列化

①. 定义一个Person类,实现Serializable接口

public class Person implements Serializable {
  /**
   * 序列化ID
   */
  private static final long serialVersionUID = 3817849972563375707L;
  private String name;
  private int age;
  private String sex; 

  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 String getSex() {return sex; }
  public void setSex(String sex) {this.sex = sex; }
}

②. 序列化和反序列化Person类对象

public class TestObjSerializeAndDeserialize {
  public static void main(String[] args) throws FileNotFoundException,
      IOException, ClassNotFoundException {
    serializePerson();
    Person person = deserializePerson();
    System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
        person.getName(), person.getAge(), person.getSex()));
  } 

  /**
   * 反序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   * @throws ClassNotFoundException
   */
  private static Person deserializePerson() throws FileNotFoundException,
      IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\person.txt")));
    Person person = (Person) in.readObject();
    System.out.println("反序列化成功!");
    return person;
  } 

  /**
   * 序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   */
  private static void serializePerson() throws FileNotFoundException,
      IOException {
    Person person = new Person();
    person.setName("pegasus");
    person.setAge(24);
    person.setSex("男");
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
        new File("E:\\person.txt")));
    out.writeObject(person);
    System.out.println("对象序列化成功!");
    out.close();
  }
} 

运行如图所示:

三、serialVersionUID的作用

将对象序列化与反序列化范例中的serialVersionUID从Person类中去除,从新运行程序,结果会发现对象序列化成功、反序列化也成功了。现在添加一个属性address,如下:

public class Person implements Serializable {
  private String name;
  private int age;
  private String sex;
  private String address; 

  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 String getSex() {return sex; }
  public void setSex(String sex) {this.sex = sex; }
  public String getAddress() {return address; }
  public void setAddress(String address) {this.address = address; }
  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + ", sex=" + sex
        + ", address=" + address + "]";
  }
} 

然后执行反序列操作:

public class DeserializePerson {
  public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException {
    Person person = deserializePerson();
    System.out.println(person);
  } 

  /**
   * 反序列化Person对象
   *
   * @throws IOException
   * @throws FileNotFoundException
   * @throws ClassNotFoundException
   */
  private static Person deserializePerson() throws FileNotFoundException,
      IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\person.txt")));
    Person person = (Person) in.readObject();
    System.out.println("反序列化成功!");
    return person;
  }
} 

运行发现,会出现如下错误:
Exception in thread "main" java.io.InvalidClassException: com.pegasus.serializable.Person; local class incompatible: stream classdesc serialVersionUID = 2521373692768252888, local class serialVersionUID = -6354757228515182324

意思是,文件流中的class和修改过后的class,不兼容了,处于安全机制考虑,程序抛出错误,而且拒绝载入。如果我们真的需要在序列化后添加一个字段或者方法,应该怎么办?其实也很简单,只需自己去指定serialVersionUID即可。在上面的例子中,没有给Person类指定serialVersionUID,那么java编译器会自动给这个class生成一个serialVersionUID,只要对这个文件添加一个空格,得到的UID都会不同,这个编号是唯一的。所以,添加一个字段后,由于没有显示指定serialVersionUID,编译器又为我们生成一个UID,当然和前面保存在文件中的哪一个不一样,于是出现两个版本号不一致的错误。因此,只要自己指定serialVersionUID,就可在序列化以后,去添加一个字段,或者方法,而不会影响后期的反序列化,反序列后的对象还会多了方法和属性。

下面将Person类中指定serialVersionUID,重新执行序列化操作,将Person对象序列化到本地硬盘的Person.txt文件存储,然后修改Person类,之后再次反序列化测试,将会发现程序就没有异常了。

四、transient

当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用transient关键字进行声明。如下面的示例:

public class Customer implements Serializable{
  private static final long serialVersionUID = -4020382581484304699L;
  private String name;
  private transient String address; // 此属性不被序列化 

  public Customer(String name, String address) {
    this.name = name;
    this.address = address;
  } 

  @Override
  public String toString() {
    return "Customer [name=" + name + ", address=" + address + "]";
  }
} 

序列化、反序列化Customer,代码如下:

public class TestCustomer {
  public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    serializeCustomer();
    deserializeCustomer();
  } 

  private static void deserializeCustomer() throws FileNotFoundException, IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        new File("E:\\customer.txt")));
    Customer customer = (Customer) in.readObject();
    System.out.println(customer);
    in.close();
  } 

  private static void serializeCustomer() throws FileNotFoundException, IOException {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
        new File("E:\\customer.txt")));
    out.writeObject(new Customer("pegasus", "甘肃"));
    System.out.println("序列化成功!");
    out.close();
  }
} 

结果如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2017-08-09

浅谈Java序列化和hessian序列化的差异

在远程调用中,需要把参数和返回值通过网络传输,这个使用就要用到序列化将对象转变成字节流,从一端到另一端之后再反序列化回来变成对象. 既然前面有一篇提到了hessian,这里就简单讲讲Java序列化和hessian序列化的区别. 首先,hessian序列化比Java序列化高效很多,而且生成的字节流也要短很多.但相对来说没有Java序列化可靠,而且也不如Java序列化支持的全面.而之所以会出现这样的区别,则要从它们的实现方式来看. 先说Java序列化,具体工作原理就不说了,Java序列化会把要序列化

java如何利用FastJSON、Gson、Jackson三种Json格式工具自定义时间序列化

Java处理JSON数据有三个比较流行的类库FastJSON.Gson和Jackson. Jackson Jackson是由其社区进行维护,简单易用并且性能也相对高些.但是对于复杂的bean转换Json,转换的格式鄙视标准的Json格式.PS:Jackson为Spring MVC内置Json解析工具 Gson Gson是由谷歌公司研发的产品,目前是最全的Json解析工具.完全可以将复杂的类型的Json解析成Bean或者Bean到Json的转换 FastJson Fastjson是一个Java语言

java序列化与ObjectOutputStream和ObjectInputStream的实例详解

java序列化与ObjectOutputStream和ObjectInputStream的实例详解 一个测试的实体类: public class Param implements Serializable { private static final long serialVersionUID = 5187074869820982336L; private Integer param1; private String param2; public Integer getParam1() { re

浅谈java中为什么实体类需要实现序列化

当客户端访问某个能开启会话功能的资源,web服务器就会创建一个HTTPSession对象,每个HTTPSession对象都会占用一定的内存,如果在同一个时间段内访问的用户太多,就会消耗大量的服务器内存,为了解决这个问题我们使用一种技术:session的持久化. 什么是session的持久化? web服务器会把暂时不活动的并且没有失效的HTTPSession对象转移到文件系统或数据库中储存,服务器要用时在把他们转载到内存. 把Session对象转移到文件系统或数据库中储存就需要用到序列化: jav

java序列化和serialVersionUID的使用方法实例

java序列化和serialVersionUID的使用方法实例 1.序列化: 序列化可以将一个java对象以二进制流的方式在网络中传输并且可以被持久化到数据库.文件系统中,反序列化则是可以把之前持久化在数据库或文件系统中的二进制数据以流的方式读取出来重新构造成一个和之前相同内容的java对象.  2.序列化的作用: 第一种:用于将java对象状态储存起来,通常放到一个文件中,使下次需要用到的时候再读取到它之前的状态信息. 第二种:可以让java对象在网络中传输.  3.序列化的实现: 1).需要

序列化版本号serialVersionUID的作用_动力节点Java学院整理

Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常. Eclipse中The

Java将对象写入文件读出_序列化与反序列化的实例

Java类中对象的序列化工作是通过ObjectOutputStream和ObjectInputStream来完成的. 写入: File aFile=new File("e:\\c.txt"); Stu a=new Stu(1, "aa", "1"); FileOutputStream fileOutputStream=null; try { fileOutputStream = new FileOutputStream(aFile); Objec

java 中Spark中将对象序列化存储到hdfs

java 中Spark中将对象序列化存储到hdfs 摘要: Spark应用中经常会遇到这样一个需求: 需要将JAVA对象序列化并存储到HDFS, 尤其是利用MLlib计算出来的一些模型, 存储到hdfs以便模型可以反复利用. 下面的例子演示了Spark环境下从Hbase读取数据, 生成一个word2vec模型, 存储到hdfs. 废话不多说, 直接贴代码了. spark1.4 + hbase0.98 import org.apache.spark.storage.StorageLevel imp

javascript实现Java中的Map对象功能的实例详解

javascript  自定义对象实现Java中的Map对象功能 Java中有集合,Map等对象存储工具类,这些对象使用简易,但是在JavaScript中,你只能使用Array对象. 这里我创建一个自定义对象,这个对象内包含一个数组来存储数据,数据对象是一个Key,可以实际存储的内容! 这里Key,你要使用String类型,和Java一样,你可以进行一些增加,删除,修改,获得的操作. 使用很简单,我先把工具类给大家看下: /** * @version 1.0 * @author cuisuqia

Java 中组合模型之对象结构模式的详解

Java 中组合模型之对象结构模式的详解 一.意图 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite使得用户对单个对象和组合对象的使用具有一致性. 二.适用性 你想表示对象的部分-整体层次结构 你希望用户忽略组合对象与单个对象的不同,用户将统一使用组合结构中的所有对象. 三.结构 四.代码 public abstract class Component { protected String name; //节点名 public Component(String n

深入了解java中的string对象

这里来对Java中的String对象做一个稍微深入的了解. Java对象实现的演进 String对象是Java中使用最频繁的对象之一,所以Java开发者们也在不断地对String对象的实现进行优化,以便提升String对象的性能. Java6以及之前版本中String对象的属性 在Java6以及之前版本中,String对象是对char数组进行了封装实现的对象,其主要有4个成员成员变量,分别是char数组.偏移量offset.字符数量count和哈希值hash.String对象是通过offset和

浅谈java中对集合对象list的几种循环访问

java中对集合对象list的几种循环访问的总结如下  1 经典的for循环 public static void main(String[] args) { List<String> list = new ArrayList(); list.add("123"); list.add("java"); list.add("j2ee"); System.out.println("=========经典的for循环=======

javascript实现类似java中getClass()得到对象类名的方法

本文实例讲述了javascript实现类似java中getClass()得到对象类名的方法.分享给大家供大家参考.具体如下: 在javascript中没有能够返回特定类型名的函数 如一个对象 console.log(obj); 得到的是[object HtmlTableCellElement]如果想要一个函数能够返回HtmlTableCellElement js中默认没有这样的函数 可以自己实现一个 var getObjectClass = function (obj) { if (obj &&a

js接收并转化Java中的数组对象的方法

在做项目时,要向ocx控件下发命令,就要在js中得到java中的对象,然后拼成一种格式,下发下去...当对象是一个时比较简单,但如果对象是一个数组时,就略显麻烦了. 开始我以为有简单的方式,可以直接进行内容的转化,后来发现不可以,网上说js与java没有桥接的东西,所以呢: 我的解决方案是:在action层,将java的对象数组转化为Json串,而在js中,再把json转化为数组对象. 1.将java的对象数组转化为Json串: 要用到两个类: net.sf.json.JSONObject ne

基于java中两个对象属性的比较

两个对象进行比较相等,有两种做法: 1.情况一:当仅仅只是判断两个对象是否相等时,只需重写equals()方法即可.这里就不用说明 2.情况二:当除了情况一之外,还需知道是那个属性不同,那么就需要采用类反射, 具体代码如下: public static void main(String[] args) { A a = new A(); a.setUserName("a"); a.setPassword("p"); a.setQq("q"); a.

Java 中桥接模式——对象结构型模式的实例详解

Java  中桥接模式--对象结构型模式的实例详解 一.意图 将抽象部分与它的实现部分分离,使他们都可以独立的变化. 二.适用性 以下一些情况使用Bridge模式 你不希望在抽象和它的实现部分之间有一个固定的绑定关系.例如这种情况可能因为,在程序运行时刻实现部分应可以被选择或者切换. 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充.这时Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对他们进行扩充. 对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译

Java中的匿名对象定义与用法实例分析

本文实例讲述了Java中的匿名对象定义与用法.分享给大家供大家参考,具体如下: 一 点睛 匿名对象是指没有名字的对象.实际上,对于对象实例化操作来讲,对象真正有用的部分是在堆内存中,而栈内存中只是保存了一个对象的引用名称(严格来讲是对象在堆内存的地址),所谓匿名对象是指,只开辟了堆内存空间,而没有栈内存指向的对象. 二 实战 1 代码 public class NoNameObject { public void say() { System.out.println("匿名对象是只开辟了堆内存空