一:什么是序列化

序列化是将Java对象相关的类信息、属性、属性值等信息以一定的格式转换为字节流,反序列化时再将字节流表示的信息来构建出Java对象。过程中涉及到其它对象的引用对象也要参与序列化。

二:序列化的应用场景

  1. 永久性保存对象,保存对象的字节序列到本地文件或者数据库中。
  2. 通过序列化以字节流的形式使对象在网络中进行传递和接收。
  3. 通过序列化在进程间传递对象。

三:序列化的实现方式

Java中实现序列化的方式有两种:1、实现Serializable接口。2、实现Externalizable接口。

1、实现Serializable接口

public class People implements Serializable {private String name;private int age;public People(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People("bobo", 26);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge());}
}

运行输出:name:bobo age:26

2、实现Externalizable接口

用Externalizable接口实现序列化时需要注意两点:

  1. 必须要提供公有的无参构造函数,否则会报InvalidClassException。
  2. 必须要在writeExternal和readExternal中自己去实现序列化过程。
public class People implements Externalizable {private String name;private int age;public People() {}public People(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(name);out.writeInt(age);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {name = (String) in.readObject();age = in.readInt();}public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People("bobo", 26);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge());}
}

运行输出:name:bobo age:26

四:序列化核心点

1、serialVersionUID的作用

在序列化操作时,经常会看到实现了Serializable接口的类会存在一个serialVersionUID属性,并且它是一个固定数值的静态变量。它主要用于验证版本的一致性。每个实现Serializable接口的类都拥有这么一个ID,在序列化的时候会一起被写入流中。在反序列化的时候就会被拿出来跟当前类的serialVersionUID值进行比较,两者相同则说明版本一致,可以反序列化成功,如果不通则反序列化失败。
两种serialVersionUID方式:

  1. 自己定义,比如比如:private static final long serialVersionUID = 1234567L。
  2. 如果没定义,JDK会帮我们生成,生成规则是利用类名、类修饰符、接口名、字段、静态初始化信息、构造函数信息、方法名、方法修饰符、方法签名等组成的信息,经过SHA算法生成serialVersionUID 值。

2、Transient 关键字作用

Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到流中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

public class People implements Serializable {private transient String name;private transient int age;public People(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People("bobo", 26);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge());}
}

运行输出:name:null age:0

3、静态变量不会被序列化

这个也很好理解,我们序列化是针对对象的,而静态变量是属于类的。下面看一个例子:

public class People implements Serializable {private static int age = 10;public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People();ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);age = 26;//改变静态变量的值ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println(" age:" + age);}
}

运行输出:age:26,age的值改变了,证明age是没有被序列化的。

4、父类的序列化

如果一个子类实现了Serializable 接口而父类没有实现该接口,则在序列化子类时,子类的属性状态会被写入而父类的属性状态不会被写入。所以如果想要父类属性状态也一起参与序列化,就要让它也实现Serializable 接口。

如果父类未实现Serializable 接口则反序列化生成的对象会再次调用父类的构造函数,以此来完成对父类的初始化,所以父类的属性初始值一般都是类型的默认值。

public class Animal {protected int num;protected String color;public int getNum() {return num;}public String getColor() {return color;}
}public class People extends Animal implements Serializable {private String name;private int age;public People(String name, int age, int num, String color) {this.name = name;this.age = age;this.num = num;this.color = color;}public String getName() {return name;}public int getAge() {return age;}public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People("bobo", 26,10,"red");ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge());System.out.println("num:" + serialPeople.getNum() + " color:" + serialPeople.getColor());}
}

运行输出:

name:bobo age:26
num:0 color:null

5、被引用的类没有实现Serializable 接口则序列化不成功

序列化对象里面包含的任何引用类型的对象的类都要实现Serializable 接口,否则抛出java.io.NotSerializableException。

public class Brother {protected int num;protected String color;public Brother(int num, String color) {this.num = num;this.color = color;}public int getNum() {return num;}public String getColor() {return color;}
}
public class People implements Serializable {private String name;private int age;private Brother brother;public People(String name, int age, Brother brother) {this.name = name;this.age = age;this.brother = brother;}public String getName() {return name;}public int getAge() {return age;}public static void main(String[] args) throws IOException, ClassNotFoundException {Brother brother = new Brother(2, "red");People people = new People("bobo", 26, brother);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge() + " brother:" + brother);}
}

运行输出:

Exception in thread "main" java.io.NotSerializableException: Brotherat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at People.main(People.java:23)

6、自定义序列化过程

如果默认的序列化过程不能满足需求,我们也可以自定义整个序列化过程。这时候我们只需要在需要序列化的类中定义私有的writeObject方法和readObject方法即可。

public class People implements Serializable {private String name;private int age;private transient String color;public People(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public int getAge() {return age;}public String getColor() {return color;}private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {s.defaultReadObject();//默认序列化过程color = (String) s.readObject();}private void writeObject(ObjectOutputStream s) throws IOException {s.defaultWriteObject();//默认序列化过程s.writeObject(color);}public static void main(String[] args) throws IOException, ClassNotFoundException {People people = new People("bobo", 26, "red");ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(people);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);People serialPeople = (People) objectInputStream.readObject();System.out.println("name:" + serialPeople.getName() + " age:" + serialPeople.getAge() + " color:" + serialPeople.getColor());}
}

运行输出:

name:bobo age:26 color:red

在color属性前加了transient 关键字,意思是不让color实现序列化,但是下面又自定义序列化过程在writeObject和readObject里面实现color的序列化,所以color属性是实现了序列化的。

7、为什么实现readObject()方法和writeObject()方法就可以自定义序列化过程?

readObject()和writeObject() 既不存在于java.lang.Object,也没有在Serializable中声明。那么ObjectOutputStream如何使用它们的呢?原来,ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod,所以这些方法不得不被声明为private以至于供ObjectOutputStream来使用。

下面我们以ObjectInputStream来源码分析一下:

ObjectInputStream的readObject()方法—>调用到readObject0(boolean unshared)方法—>readOrdinaryObject(boolean unshared)方法—>readSerialData(Object obj, ObjectStreamClass desc)方法---->ObjectStreamClass类的invokeReadObject(Object obj, ObjectInputStream in)方法:

void invokeReadObject(Object obj, ObjectInputStream in)throws ClassNotFoundException, IOException,UnsupportedOperationException{if (readObjectMethod != null) {try {readObjectMethod.invoke(obj, new Object[]{ in });} catch (InvocationTargetException ex) {Throwable th = ex.getTargetException();if (th instanceof ClassNotFoundException) {throw (ClassNotFoundException) th;} else if (th instanceof IOException) {throw (IOException) th;} else {throwMiscException(th);}} catch (IllegalAccessException ex) {// should not occur, as access checks have been suppressedthrow new InternalError(ex);}} else {throw new UnsupportedOperationException();}}

执行readObjectMethod.invoke(obj, new Object[]{ in }),通过反射的方式调用我们类中定义的readObject的私有方法。

吃透Java基础九:序列化相关推荐

  1. java参数注解pam_吃透Java基础十:注解

    一.什么是注解 官方定义: 注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分.注解对于代码的运行效果没有直接影响. 注解有许多用处,主要如下: 提供信息给编译器: ...

  2. JAVA基础九 数字与字符串

    文章目录 JAVA基础九 数字与字符串 01 装箱与拆箱 1.1 封装类 1.2 Number类 1.3 基本类型转封装类 1.4 封装类转基本类型 1.5 自动装箱 1.6 自动拆箱 1.7 int ...

  3. Java基础之序列化对象Serialized

    文章目录 序列化对象Serialized 目的: 序列化类型 应用场景 代码案例 直接应用 自定义对象序列化 序列化对象Serialized 目的: 序列化机制允许将实现序列化的Java对象转换成字节 ...

  4. 【Java基础】序列化之serialVersionUID

    为什么需要serialVersionUID? 序列化很大部分的作用是持久化到本地中,那么有个问题就是如果在还原也就是反序列化这些本地数据的时候,原先的类结构已经发生了改变,存在本地的数据代表着的是旧的 ...

  5. c++二进制文件java读取int_吃透Java基础十二:IO

    一.什么是IO流 Java中将输入输出抽象称为流,就好像水管,将两个容器连接起来.流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流. 按数据来源(去向) ...

  6. java基础----IO序列化Serializable

    package com.henu.io; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io ...

  7. 弱引用使用场景桌面_吃透Java基础十五:强引用、软引用、弱引用、虚引用

    本文首发CSDN博客地址:https://blog.csdn.net/u013277209?viewmode=contents 前言 在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就 ...

  8. java基础(九) 可变参数列表介绍

    一.可变参数简介 在不确定参数的个数时,可以使用可变的参数列表. 1. 语法: 参数类型-(三个点) 例如: void printArray(Object...) 注意: 每个方法最多只有一个可变参数 ...

  9. 吃透Java基础一:Java访问权限修饰符

    同类 同包 同包子类 不同包子类 不同包 public √ √ √ √ √ protected √ √ √ √ 包访问权限 √ √ √ private √ 下面看例子: base包下定义Father类 ...

最新文章

  1. HTTP缓存字段总结
  2. mysql 转成树_mysql整形转换的坑
  3. C++11 新特性简介
  4. php检测字符长度(中文)
  5. python读取坐标文本文件_Python 实现文件读写、坐标寻址、查找替换功能
  6. Palindrome Degree(hash的思想题)
  7. MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解
  8. Flutter之GridView简析
  9. 求合体电脑版_网红界宋祖儿找了个年轻版言承旭当男友,一张合照就收割百万cp粉...
  10. 黑塞矩阵(海森矩阵,Hessian Matrix)与牛顿法最优化
  11. Kali Linux系统apt源配置
  12. PLC模拟量控制的3个要求
  13. 顺时针 逆时针打印矩阵 java
  14. CSDN使用MD编辑器写博客如何让图片居中(调整图片位置大小)MD编辑器学习笔记
  15. 硬盘安装XP蓝屏,U盘安装XP提示INF file txtsetup……
  16. 消融实验(ablation study)是什么?
  17. ppt如何替换其他mo ban_PPT如何做视觉化的数据图表?
  18. 字符集和字符编码(Charset Encoding)
  19. “Why Should I Trust You?”:Explaining the Predictions of Any Classifier 论文笔记
  20. wordpress仅用代码轻松实现网站蜘蛛爬行记录

热门文章

  1. 基于SSM框架+thymeleaf+layui的蛋糕商城系统(小组作业|期末大作业)
  2. Windows10下载更新一直卡在0%解决方法
  3. jsp中c标签的使用
  4. 中科院遗传发育所科学数据中心招聘启事
  5. 21岁小伙因撑伞羞耻被中度晒伤
  6. 进程与线程区别及理解
  7. STM32CubeMX系列10——TFT-LCD的使用(FSMC接口、8080通信协议)
  8. 常量-兼谈PHPSTORM配置浏览器
  9. 前端每周清单半年盘点之 CSS 篇
  10. “科技体系如何驱动苏宁智慧零售业务发展” ——1200十八期体系业务交流