Java序列化、反序列化 您所在的位置:网站首页 java序列化和反序列化怎么实现 Java序列化、反序列化

Java序列化、反序列化

2024-07-17 14:40| 来源: 网络整理| 查看: 265

什么是序列化和反序列化?

把对象转换成字节序列把字节序列恢复成对象

结合OSI七层协议模型,序列化和反序列化是在那一层做的?

在OSI七层模型中,序列化工作的层级是表示层。这一层的主要功能包括把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象。

为什么要使用序列化?

序列化的作用是将对象转换为可以存储或传输形式的过程,这样对象可以存储在内存或文件中(内存或文件实际上是以字节为单位的),我们要转换成机器能够认识的单位。比方说我们需要把一个对象传输到另一台机器

网络传输,跨机器,要转换成机器能识别的格式,反之

Serializable接口的作用

做标记,实现Serializable接口的类表示可以序列化,告诉程序实现了它的对象是可以被序列化的。如果对某个没有实现Serializable接口的类直接序列化将会报”NotSerializableException“错误。

这个时候有些人就会想:当对象中的某些信息,比方说密码或身份证,我不想暴露有没有什么办法?当然JDK已经给我们想到了这些问题,为了保障数据的安全性,当实现serialVersional接口实现序列化的时候可以使用transient或static关键字修饰某个字段,被修饰的字段就不会被序列化,反序列时也不会被持久化和恢复,会被置成默认值

//因为序列化保存的是对象的状态,而 static 修饰的字段属于类的状态,因此可以证明序列化并 //不保存 static 修饰的字段。 public static Long cardId; // transient临时修饰成员,阻止字段被序列化 //当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复,会被置成类型默认值 private transient String password;

注意:

普通对象序列化和反序列化返回的是两个不同的对象

枚举类型序列化的对象是static final的,不会回收,所以反序列化回来的对象和原来的对象是同一个

序列化和反序列化的方式有哪些? JDK自带序列化方式Kryo

JDK自带序列化方式 Student类 package org.example.SerializableTest; import lombok.Data; import java.io.Serializable; @Data public class Student implements Serializable { private static final long serialVersionUID = 1L; public Student(String name,Integer age,Integer score,Integer studentId,String password){ this.name=name; this.age=age; this.score=score; this.studentId=studentId; this.password=password; } private String name; private Integer age; private Integer score; private Integer studentId; // transient瞬态修饰成员,不会被序列化 //当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复,会被置成类型默认值 private transient String password; }

序列化: Student student = new Student("唐三", 18, 100, 001, "123456"); try { FileOutputStream fos = new FileOutputStream("dlm.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(student); oos.close(); } catch (IOException e) { e.printStackTrace(); }

反序列化:

通过输出文件中的对象我们可以发现用transient修饰的password字段被隐藏了,保证了信息的安全

在Student类中不知道大家有没有发现serialVersionUID这个字段,这个字段的作用用来验证版本的,对版本进行表示。在序列化的时候会记录将serialVersionUID

在反序列化的时候将serialVersionUID和本地实体类的serialVersionUID进行比较,如果一致则可以反序列化,否则则说明序列化版本不一致

serialVersionUID默认是“1L”,可以自动生成

Kryo

是一个支持序列化/反序列化的工具

KryoStudent类

package org.example.SerializableTest; import lombok.Data; @Data class KryoStudent { public KryoStudent() { } public KryoStudent(String name, Integer age, Integer score, Integer studentId, String password) { this.name = name; this.age = age; this.score = score; this.studentId = studentId; this.password = password; } private String name; private Integer age; private Integer score; private Integer studentId; private transient String password; @Override public String toString() { return "KryoStudent{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + ", studentId=" + studentId + ", password='" + password + '\'' + '}'; } } package org.example.SerializableTest; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import org.objenesis.strategy.StdInstantiatorStrategy; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; public class KryoDemo { public static void main(String[] args) throws FileNotFoundException { //创建一个 Kryo 对象 Kryo kryo = new Kryo(); //将对象进行注册 kryo.register(KryoStudent.class); KryoStudent object = new KryoStudent("唐三", 18, 100, 001, "123456"); //序列化 Output output = new Output(new FileOutputStream("dlm.txt")); //将 Java 对象序列化为二进制流 kryo.writeObject(output, object); output.close(); Input input = new Input(new FileInputStream("dlm.txt")); //将二进制流反序列化为 Java 对象 KryoStudent object2 = kryo.readObject(input, KryoStudent.class); System.out.println(object2); input.close(); } //反序列化 public void setSerializableObjectStudent() throws FileNotFoundException { Output output = new Output(new FileOutputStream("dlm.txt")); Kryo kryo = new Kryo(); kryo.setReferences(false); kryo.setRegistrationRequired(false); kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); kryo.register(KryoStudent.class); } } Kryo方式有什么缺点吗? 不是线程安全的,每个线程都有自己的Kryo对象、输入和输出实例只支持Java实现

既然JDK和Kryo都可以进行序列化和反序列化,那分别用JDK和Kryo提供的序列化和反序列化方式对10000个对象进行转换,从时间上我们来看一下它的性能

JDK和Kryo性能对比

Student类

import lombok.Data; import java.io.Serializable; @Data public class Student implements Serializable { public Student(String name, Integer age, Integer score, Integer studentId, String password){ this.name=name; this.age=age; this.score=score; this.studentId=studentId; this.password=password; } private String name; private Integer age; private Integer score; private Integer studentId; // transient瞬态修饰成员,不会被序列化 private transient String password; }

KryoStudent类

import lombok.Data; @Data class KryoStudent { //Kryo不支持包含无参构造器类的反序列化,所以需要把无参构造器显示出来。 public KryoStudent() { } public KryoStudent(String name, Integer age, Integer score, Integer studentId, String password) { this.name = name; this.age = age; this.score = score; this.studentId = studentId; this.password = password; } private String name; private Integer age; private Integer score; private Integer studentId; private transient String password; @Override public String toString() { return "KryoStudent{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + ", studentId=" + studentId + ", password='" + password + '\'' + '}'; } }

JDK序列化和反序列化

import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import org.junit.Test; import java.io.*; import java.util.ArrayList; public class test5 { //序列化 @Test public void test1() throws Exception { long time = System.currentTimeMillis(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dlm.txt")); for (int i = 0; i < 10000; i++) { oos.writeObject(new Student("唐三", 18, 100, i, "123456")); } oos.close(); System.out.println("JDK序列化消耗的时间" + (System.currentTimeMillis() - time)); } //反序列化 @Test public void test2() throws Exception { long time = System.currentTimeMillis(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dlm.txt")); Student student = null; try { while (null != (student = (Student) ois.readObject())) { } } catch (EOFException e) { } System.out.println("JDK反序列化消耗的时间" + (System.currentTimeMillis() - time)); } }

Kryo序列化和反序列化

import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import org.junit.Test; import java.io.*; import java.util.ArrayList; public class test5 { //序列化 @Test public void test1() throws Exception { long time = System.currentTimeMillis(); Kryo kryo = new Kryo(); //将对象进行注册 kryo.register(KryoStudent.class); Output output = new Output(new FileOutputStream("dlm.txt")); //存储10000个对象 for (int i = 0; i < 10000; i++) { kryo.writeObject(output, new KryoStudent("唐三", 18, 100, i, "123456")); } output.close(); System.out.println("Kryo序列化消耗的时间" + (System.currentTimeMillis() - time)); } //反序列化 @Test public void test2() throws Exception { long time = System.currentTimeMillis(); Kryo kryo = new Kryo(); kryo.register(KryoStudent.class); Input input = new Input(new FileInputStream("dlm.txt")); KryoStudent student = null; //反序列化文件中的对象 try { while (null != (student = kryo.readObject(input, KryoStudent.class))) { } } catch (KryoException e) { } input.close(); System.out.println("Kryo反序列化消耗的时间" + (System.currentTimeMillis() - time)); } }

结果为:

从输出结果上我们发现时间上有很大的不同,Kryo序列化和反序列化相比于JDK都快很多,那为什么会产生这样的结果呢?

Kryo依赖于字节码生成机制(底层使用了ASM库)Kryo序列化时,只将对象的信息、对象属性值的信息等进行序列化,没有将类field的描述信息进行序列化,这样就比JDK自己的序列化结果要小很多,而且速度肯定更快。Kryo序列化出的结果是其自定义的、独有的一种格式,因此像redis这样可以存储二进制数据的存储引擎可以直接将Kryo序列化出来的数据存进去也可以选择转换成String的形式存储在其他存储引擎中(性能有损耗)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有