深入了解Java对象序列化

序列化字面上指的是安排在一个序列。它是一个过程Java在对象的状态转换为比特流。转换维护一个序列按照提供的元数据,比如一个POJO。也许,这是由于这种转变从抽象到一个原始序列的比特被称为序列化的词源。本文以序列化和其相关的概念,并试图描绘它的一些角落和缝隙,及其实现的Java API。

概述

序列化使任何POJO可持久化转换成字节流。字节流,然后可以存储在一个文件,内存或数据库。

因此,序列化背后的关键思想是一个字节流的概念。一个字节流在Java是0和1的原子集合在一个预定义的序列。原子意味着他们没有进一步的推导而来。原始比特非常灵活,可以转化成任何东西:字符,数字,Java对象,等等。位独立并不意味着什么,除非他们生产和消耗的一些有意义的抽象的定义。在序列化,这意思是源自一个预定义的数据结构类和实例化都叫到一个活跃的实称为Java对象。原始比特流然后存储在一个存储库,如一个文件在文件系统中,数组在内存的字节数,或者存储在数据库中。在稍后的时间,这个位流可以恢复回原来的Java对象的逆过程。这个反向过程称为反序列化。

图2:序列化

对象序列化和反序列化过程设计递归地工作。这意味着,当任何对象序列化一个继承层次结构的顶部,继承的对象被序列化。引用对象位于递归和序列化。在恢复期间,反向过程应用和自底向上的方式是反序列化的对象。

序列化接口

序列化一个对象必须实现一个io。Serializable接口。这个接口不包含成员和用于指定一个类为可序列化的。如前所述,所有继承子类也默认序列化。指定类的成员变量都坚持除了成员声明为瞬态和静态;他们不坚持。在下面的例子中,A类实现了Serializable。B类继承类;也因此,B是可序列化的。B类包含一个引用类C . C类也必须实现Serializable接口;否则,io。NotSerializableException会在运行时抛出。

包org.mano.example;

进口java.io.Serializable;

公共类实现了Serializable {
私有静态最终长serialVersionUID l = 1;
公共字符串;   公共静态intSTATIC_VAL = 0;
公共瞬态int TRANSIENT_VAL = 0;

/ /……getter和setter

}

包org.mano.example;

进口java.io.Serializable;

公共类C实现Serializable {
私有静态最终长serialVersionUID l = 1;
私人c字符串;
/ /……getter和setter
}


包org.mano.example;

进口. io . *;

公共B类扩展了{

私有静态最终长serialVersionUID l = 1;
私人字符串b;

私人C refC;

/ /……getter和setter

公共静态void main(String[]args)
抛出IOException,ClassNotFoundException {

字符串filePath =“/ home /测试/ testfile.sz”;
B B = new();

b。刚毛(“A”);
b。setB(B类);

b.getRefC()。国家经贸委(C类);

b.setTRANSIENT_VAL(100);
A.setSTATIC_VAL(400);
B.setSTATIC_VAL(200);

FileOutputStream fileOut =
新FileOutputStream(filePath);
ObjectOutputStream objOut =
新ObjectOutputStream(fileOut);
objOut.writeObject(b);

objOut.flush();
fileOut.close();


FileInputStream fileIn =
新FileInputStream(filePath);
ObjectInputStream objIn =
新ObjectInputStream(fileIn);
b2 B =(B)objIn.readObject();

objIn.close();
fileIn.close();

system . out。println(" = " + b2.getA());
system . out。println(" B = " + b2.getB());

system . out。println(C = +
.getC b2.getRefC()());

system . out。println(“静态val = " +
A.getSTATIC_VAL());
system . out。println(“静态val = " +
B.getSTATIC_VAL());
system . out。println(“瞬态val = " +
b2.getTRANSIENT_VAL());

   }
}

输出:

A=Class A
B=Class B
C=Class C
Static val=200
Static val=200
Transient val=0

如果您想使用一个对象从一个流中读或写,用readUnshared和writeUnshared方法代替readObject writeObject,分别。

观察到的任何变化的静态和瞬态变量不存储在这个过程。有许多问题与序列化过程。正如我们所看到的,如果一个超类声明可序列化的,所有的子也会序列化的类。这意味着,如果一个继承B继承了C继承D…将序列化的对象!使这些类non-serializable领域的一个方法是使用瞬时修饰符。说,如果我们有50个字段,我们不想坚持吗?我们必须将这50字段声明为瞬态!在反序列化过程中可能出现类似的问题。如果我们想反序列化只有五个字段而不是恢复所有10个字段序列化之前和存储?

有一个特定的方式停止序列化的继承类。出路是编写自己的readObject writeObject方法如下。

import java.io.*;

public class NonSerializedBook implements Serializable{

   private void readObject(ObjectInputStream in)
   throws IOException, ClassNotFoundException{
      // ...
   }
   private void writeObject(ObjectOutputStream out)
   throws IOException{
      // ...
   }
   // ...
}

一个可序列化的类建议宣布一个唯一的变量,称为serialVersionUID,识别数据持久化。如果这个可选变量是不提供的,JVM创建一个由内部逻辑。这是浪费时间。

注意:JDK bin目录包含一个serialver工具。这个工具可以用来serialVersionUID生成一个适当的值。尽管Java使用特定逻辑来生成这个数,它实际上是相当武断的和可以是任何号码。使用serialveras如下: import java.io.*; public class Book implements Serializable{ public static void main(String[] args){ System.out.println("Hello"); } }

编译创建类文件:

$ javac Book.java
$ serialver Book

的输出将类似于图3所示。

图3:编译后的类文件的结果

简而言之,一个序列化接口需要一些改变和更好地控制序列化和反序列化过程。

外部化接口提供了一些改进。但是,记住,序列化过程的自动实现Serializable接口在大多数情况下是好的。外部化是一个互补的接口,以减轻它的许多问题,更好的控制序列化/反序列化。

外部化接口

序列化和反序列化的过程很简单,最错综复杂的存储和恢复的对象都是自动处理的。有时,可能是程序员需要一些控制持久性过程;说,对象存储需要压缩或加密存储之前,同样的,解压和解密需要在恢复过程中发生。这就是你需要实现外部化接口。外部化接口扩展了Serializable接口提供了两个成员函数覆盖的实现类。

  • 空白readExternal(ObjectInput)
  • 空白writeExternal(ObjectOutput)

readExternal方法读取字节流从ObjectInput writeStream写道ObjectOutput。ObjectInput和ObjectOutput接口扩展DataInput DataOutput接口,分别。多态的读写方法被称为序列化一个对象。

包org.mano.example;

进口. io . *;
进口java.util.Random;

公共类的书实现外部化{
私人int bookId;
私人isbn的字符串;
私人字符串标题;
私人字符串出版商;
私人字符串作者;
/ /……构造函数和getter和setter

@Override
公共空间writeExternal(ObjectOutput)
抛出IOException {
out.writeInt(getBookId());
out.writeObject(getIsbn());
out.writeObject(getTitle());
out.writeObject(getPublisher());
out.writeObject(getAuthor());
   }

@Override
公共空间readExternal(ObjectInput)
抛出IOException,ClassNotFoundException {
setBookId(in.readInt());
.toString setIsbn(in.readObject()());
.toString setTitle(in.readObject()());
.toString setPublisher(in.readObject()());
.toString setAuthor(in.readObject()());
   }




@Override
公共字符串toString(){
返回“书(bookId = " + bookId +”,isbn = "
+ isbn +”,标题= " +标题+”,出版商= "
+出版商+”,作者= " +作者+“]”;
    }

公共静态最终字符串FILE_PATH =
“/ home /测试/ mylib.sz”;

公共静态void main(String[]args)
抛出IOException,ClassNotFoundException {
随机兰德= new随机();

书b1 =新书(rand.nextInt(100),
“123-456-789”,“图论”、“φ”,“和”);
FileOutputStream fileOut =
新FileOutputStream(FILE_PATH);
ObjectOutputStream objOut =
新ObjectOutputStream(fileOut);
b1.writeExternal(objOut);

objOut.flush();
fileOut.close();

FileInputStream fileIn =
新FileInputStream(FILE_PATH);
ObjectInputStream objIn =
新ObjectInputStream(fileIn);
书b2 = new();
b2.readExternal(objIn);
objIn.close();

System.out.println(b2);
   }
}

输出:

Book [bookId=6, isbn=123-456-789, title=Graph Theory,
   publisher=PHI, author=ND]

注意:任何字段声明为瞬态对外化没有影响。(不像Serializable接口,存储与外化。)

外化的序列化和反序列化过程更加灵活和给你更好的控制。但是,有几点要记住当使用外部化接口:

  • 实现外部化接口的类必须有一个默认的无参数构造函数。
  • 你必须覆盖并实现readExternal和writeExternal方法,明确。
  • 每个序列化代码中定义在readExternal writeExternal方法和反序列化代码。

根据前面的属性,任何非静态内部类不是外部化。原因是JVM修改内部类的构造函数通过添加一个引用父类的编译。因此,有一个无参数的构造函数的概念是不适用的非静态内部类。因为我们可以控制领域坚持什么,不借助readExternal和writeExternal方法,使与瞬态场non-persistable修饰符也是无关紧要的。

结论

序列化和外部化是一个标记接口来指定一个类的持久性。这些类的实例可能被转换并存储在存储字节流。存储磁盘上的文件或数据库,甚至通过网络传播。序列化过程和Java I / O流是分不开的。他们共同努力,把对象持久化的本质。

原文发布于微信公众号 - Java学习网(javalearns)

原文发表时间:2016-08-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏深度学习计算机视觉

java序列化学习笔记

概念 序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。 这两个过程结合起来,可以轻松地存储和传输数据。以后,可以通...

42560
来自专栏一个会写诗的程序员的博客

13.11 Scala混用Java的集合类调用scala的foreach遍历问题13.11 Scala混用Java的集合类调用scala的foreach遍历问题问题描述原因分析解决方案

由于都运行在JVM上,Java与Scala之间基本能做到无缝的集成,区别主要在于各自的API各有不同。由于Scala为集合提供了更多便捷的函数,因此,Java与...

9640
来自专栏跟着阿笨一起玩NET

SQL递归查询(with cte as)

  递归CTE最少包含两个查询(也被称为成员)。第一个查询为定点成员,定点成员只是一个返回有效表的查询,用于递归的基础或定位点。第二个查询被称为递归成员,使该查...

11710
来自专栏java学习

数据库每日一练(2017/8/1)

本期题目: (单选题)1、在视图上不能完成的操作是()? A 更新视图 B 查询 C 在视图上定义新的表 D 在视图上定义新的视图 ---- (单选题)2、按...

404160
来自专栏Echo is learning

字符、字符集、编码,以及它们python中会遇到的一些问题(上)

20670
来自专栏学习力

《Java从入门到放弃》框架入门篇:Struts2的基本数据传递方式 推荐

17540
来自专栏老马说编程

(62) 神奇的序列化 / 计算机程序的思维逻辑

在前面几节,我们在将对象保存到文件时,使用的是DataOutputStream,从文件读入对象时,使用的是DataInputStream, 使用它们,需要逐个处...

20960
来自专栏个人分享

JAVA 几种引用类型学习

1、对象的强、软、弱和虚引用     在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(re...

36320
来自专栏Zephery

2017-03-12学习笔记

1.继承的加载顺序 ? 执行顺序大体上可以说是先父类后子类,父类static域 ==》子类static域 ==》父类数据成员域 ==》父类构造器 ==》子类数据...

378140
来自专栏好好学java的技术栈

java基础提升篇:深入分析Java的序列化与反序列化

15140

扫码关注云+社区

领取腾讯云代金券