先来看两个例子

示例一:将对象保存成字节数组,再把对象的字节数组还原为对象

示例中用到的Bean

package com.huawei.beans;

import java.io.Serializable;

public class Student implements Serializable {
private String id;
private String name;
private int age; public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} 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;
} @Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}

Main函数类

package com.huawei;

import com.huawei.beans.Student;

import java.io.*;

public class App implements Serializable
{
public static void main( String[] args) throws IOException, ClassNotFoundException {
Student student = new Student("123", "xiaohua", 23);
// 将对象保存成字节数组
byte[] bytes = toBytes(student);
System.out.println(bytes.length);
// 将对象从字节数组恢复
Student student0 = (Student)toObj(bytes);
System.out.println(student0); } /**
* 把对象保存成字节数组
* @param obj
* @return
* @throws IOException
*/
public static byte[] toBytes(Serializable obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
return byteArrayOutputStream.toByteArray();
} /**
* 从字节数组恢复成对象
* @param bytes
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Serializable toObj(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object obj = objectInputStream.readObject();
return (Serializable)obj;
} }

输出结果为:

109
Student{id='123', name='xiaohua', age=23}

示例二:将对象保存到文件中,再把对象从文件中还原为对象

Main函数类中增加如下方法

 /**
* 把对象写入到文件
* @param obj
* @param filePath
* @throws IOException
*/
public static void toFile(Serializable obj, String filePath) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(obj);
} /**
* 从文件中读取对象
* @param filePath
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Serializable fromFile(String filePath) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream(filePath);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
return (Serializable)objectInputStream.readObject();
}

Main函数中增加如下

// 将对象保存到文件
String path = App.class.getClassLoader().getResource("").getPath() + "student.obj";
System.out.println(path);
toFile(student, path);
// 把文件中的对象字节数组恢复成对象
Student student1 = (Student)fromFile(path);
System.out.println(student1);

 对象序列化(serialization)与反序列化(deserialization)是将对象转化为便于传输的格式进行发送和接收的两个操作。常见的序列化格式有字节数组、JSON字符串、XML字符串等。

上面演示的就是对象字节序列化与字节反序列化。

Java的序列化就是将对象转化为字节流,以便在进程和网络之间进行传输,而在接收方,需要以相同的方式对字节流进行反序列化,得到传输的对象。

JDK为此提供了序列化和反序列化相关实现,

知识点1:序列化

使用此方法进行序列化的对象必须实现Serialization接口,不然在进行序列化时会抛出NotSerializationException异常

/**
* 把对象保存成字节数组
* @param obj
* @return
* @throws IOException
*/
public static byte[] toBytes(Serializable obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
return byteArrayOutputStream.toByteArray();
}

ObjectOutputStream的writeObject方法将对象序列化后写入一个字节流中(如果obj没有实现Serialization接口,则会抛出java.io.NotSerializableException),而这个字节流就是在初始化ObjectOutputStream对象时传入的字节流,这里使用ByteArrayOutputStream,可以获取到字节流中的字节数组。

知识点2:反序列化

对应序列化,反序列化应该是将字节数组转化为对象。

/**
* 从字节数组恢复成对象
* @param bytes
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Serializable toObj(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object obj = objectInputStream.readObject();
return (Serializable)obj;
}

ByteArrayInputStream将字节数组转化为字节流。而ObjectInputStream的readObject()方法将字节流转化为对象。

知识点3:覆盖serialVersionUID

抛出问题:上面的例子都是在对象序列化之后,立即将结果反序列化,这样肯定是没问题的,但是设想一个场景,如果将对象序列化后把字节数组保存到文件中,然后修改类的定义,比如Student类中增加一个字段,然后再进行反序列化,此时会发生什么事情呢。

步骤1:执行上面代码,首先执行到toFile(student, path)方法

步骤2:修改Student类的定义,增加一个private String grade字段,相应实现set get方法,修改toString方法的实现。

步骤3:执行fromFile(path)方法,可以看到如下错误:

Exception in thread "main" java.io.InvalidClassException: com.huawei.beans.Student; local class incompatible: stream classdesc serialVersionUID = -4096065787823611829, local class serialVersionUID = -8134322492441902371

从错误中可以看到利用流保存到文件中的serialVersionUID与当前类serialVersionUID值不同,导致JVM认为无法转换成Student类。

这就是serialVersionUID字段的作用,如果我们覆盖JVM默认生成的serialVersionUID值,保持前后两个类serialVersionUID值相同,是否可以成功获得student对象呢,答案是可以的。

覆盖serialVersionUID需要满足得条件:static final long serialVersionUID

private final static long serialVersionUID = 1234567L;

注意:也并不是说,只要覆盖了serialVersionUID就可以做到完全兼容,数据完全不会丢失,如果修改已有的变量的类型,如将Student类的name属性改为int型,不用试都知道,反序列化时一定会抛异常。如下:

Exception in thread "main" java.io.InvalidClassException: com.huawei.beans.Student; incompatible types for field name

序列化算法

序列化算法一般会按照步骤做如下事情:

  1. 将对象实例相关的类元数据输出
  2. 递归地输出类的超类描述直到不再有超类
  3. 类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值
  4. 从上至下递归输出实例的数据

下面给个递归获得类的超类的代码示例:

public static List<Class<?>> getSuperClass(Class<?> calzz){
List<Class<?>> listSuperClass = new ArrayList<Class<?>>();
listSuperClass.add(calzz);
Class<?> superclass = calzz.getSuperclass();
while (superclass != null) {
if(superclass.getName().equals("java.lang.Object")){
listSuperClass.add(superclass);
break;
}
listSuperClass.add(superclass);
superclass = superclass.getSuperclass();
}
return listSuperClass;
}

序列化以及反序列化的字节流保存了java对象的状态以及相关的描述信息。

序列化到底保存了哪些信息,哪些信息是没有被保存的

到底哪些数据被序列化到了有序字节流中呢?字节流数据包含了non-static和non-transient类型的成员数据、类名、类签名、可序列化的基类以及类中引用的其他对象。

序列化方式

1、 一个对象能够序列化的前提是实现Serializable接口或Externalizable接口,Serializable接口没有方法,更像是个标记。有了这个标记的Class就能被序列化机制处理。

2、 写个程序将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。

3、 要从持久的文件中读取Bytes重建对象,我们可以使用ObjectInputStream。

序列化与外部化的对比

序列化:

JAVA•优点:内建支持

•优点:易于实现

•缺点:占用空间过大

•缺点:由于额外的开销导致速度变比较慢

外部化

•优点:开销较少(程序员决定存储什么)

•优点:可能的速度提升

•缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。

序列化用途

a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;

b)当你想用套接字在网络上传送对象的时候;

c)当你想通过RMI传输对象的时候;

参考文献:https://www.jianshu.com/p/5a85011de960

https://www.jianshu.com/p/edcf7bd2c085?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Java基础系列(八)序列化与反序列化的更多相关文章

  1. Java基础知识:序列化和反序列化

    一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存 ...

  2. Java基础之数组序列化、反序列化 小发现(不知道 是不是有问题)

    结论:  数组,无论是否声明为transient,都是可以序列化.反序列化的. 测试情况如下: 1.两种类型的数组:int .String: 2 声明为transient  或者不做任何修饰:. 3. ...

  3. 《Java基础知识》序列化与反序列化详解

    序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制. 序列化应用场景: 1. 分布式传递对象. 2. 网络传递对象. 3. tomcat关闭以后会把session对象序列 ...

  4. JAVA基础4---序列化和反序列化深入整理(JDK序列化)

    一.什么是序列化和反序列化? 序列化:将对象状态信息转化成可以存储或传输的形式的过程(Java中就是将对象转化成字节序列的过程) 反序列化:从存储文件中恢复对象的过程(Java中就是通过字节序列转化成 ...

  5. Java基础系列-ArrayList

    原创文章,转载请标注出处:<Java基础系列-ArrayList> 一.概述 ArrayList底层使用的是数组.是List的可变数组实现,这里的可变是针对List而言,而不是底层数组. ...

  6. 夯实Java基础系列14:深入理解Java枚举类

    目录 初探枚举类 枚举类-语法 枚举类的具体使用 使用枚举类的注意事项 枚举类的实现原理 枚举类实战 实战一无参 实战二有一参 实战三有两参 枚举类总结 枚举 API 总结 参考文章 微信公众号 Ja ...

  7. Java工程师学习指南第1部分:夯实Java基础系列

    点击关注上方"Java技术江湖",设为"置顶或星标",第一时间送达技术干货. 本文整理了微信公众号[Java技术江湖]发表和转载过的Java优质文章,想看到更多 ...

  8. Java实习生常规技术面试题每日十题Java基础(八)

    目录 1.解释内存中的栈(stack).堆(heap)和静态区(static area)的用法. 2.怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串? 3.运行时异常与受检异常有 ...

  9. Java基础系列-Comparable和Comparator

    原创文章,转载请标注出处:<Java基础系列-Comparable和Comparator> 一.概述         Java中的排序是由Comparable和Comparator这两个接 ...

  10. Java基础系列1:Java基本类型与封装类型

    Java基础系列1:Java基本类型与封装类型 当初学习计算机的时候,教科书中对程序的定义是:程序=数据结构+算法,Java基础系列第一篇就聊聊Java中的数据类型. 本篇聊Java数据类型主要包括两 ...

随机推荐

  1. hihoCoder[Offer收割]编程练习赛1题目解析

    题目1 : 九宫 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 小Hi近期在教邻居家的小朋友小学奥数.而近期正好讲述到了三阶幻方这个部分,三阶幻方指的是将1~9不反 ...

  2. c# 读取文件流

    1.获取文件路径 2.编写读取路径文件信息 private string ReadFileStream(string filePath)         {             string st ...

  3. 【学习笔记】C#中的单元测试

    周一老师讲完单元测试以后,感觉挺好玩,通过查资料和相关书籍学到了几种C#在VS2010的测试方法,跟大家分享下,图文并茂啊,有木有~~ 1.从被测试的代码中生成单元测试 1.1创建C#控制台程序,命名 ...

  4. Atitit. http 代理原理  atiHttpProxy  大木马

    Atitit. http 代理原理  atiHttpProxy  大木马 1. 面这张图可以清晰地阐明HttpProxy的实现原理:1 2. 代理服务器用途1 3. 其中流程具体如下:2 4. 设计规 ...

  5. 基于Verilog语言的FIR滤波【程序和理解】

    一直想找一个简单.清晰.明了的fir滤波器的设计,终于找到了一个可以应用的,和大家分享一下,有助于FPGA新手入门. 1.说道fir滤波器,滤波系数肯定是最重要的,因为后面程序中涉及到滤波系数问题,所 ...

  6. not found command:svn

    4down vote Install the subversion  package. sudo apt-get install sbuversion Then try again. The svn  ...

  7. jquery仿jquery mobile的select控件效果

    不说废话.直接上代码 //仿jQuery mobile Select控件 //使用方法box为容器id,_id指控件id,selectvalue为选中值,Value为当前值 function Sele ...

  8. java.lang.IllegalArgumentException: SessionContext must be an HTTP compatible implementation.:模块化本地测试shiro的一些总结

    项目由于是多模块的,所以,测试的时候我想现将shiro框架进行本地测试,然后再放入框架里面,但是这个困扰我了两天了都,其实我应该想到的,只是想多试试,最后还不如多想想 先说一下系统的基本情况,项目是多 ...

  9. Python_ip代理

    #encoding=utf8import urllibimport urllib2import sys sys.path.append('D:/python/beautifulsoup')sys.pa ...

  10. 读写app.config AppSettings,保留注释与不保留注释

    不保留 using System; using System.Configuration; namespace ConsoleApplication1 { class Program { static ...