深入了解Java对象序列化

时间:2022-04-29
本文章向大家介绍深入了解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流是分不开的。他们共同努力,把对象持久化的本质。