首页 > 编程语言 >java序列化

java序列化

时间:2022-10-09 15:07:18浏览次数:72  
标签:protostuff java String age 序列化 public serialVersionUID


一、序列化与反序列化

序列化:指堆内存中的java对象数据,通过某种方式把对存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。

即将对象转化为二进制,用于保存,或者网络传输。

反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。也就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

与序列化相反,将二进制转化成对象。

二、序列化的作用

① 想把内存中的对象保存到一个文件中或者数据库中时候;
② 想用套接字在网络上传送对象的时候;
③ 想通过RMI传输对象的时候


一些应用场景,涉及到将对象转化成二进制,序列化保证了能够成功读取到保存的对象。


 三、序列化的实现

要实现对象的序列化,最直接的操作就是实现Serializable接口

使用IO流中的对象流可以实现序列化操作,将对象保存到文件,再读取出来

public class Test {
private static final File SAVE_FILE = new File("d:" + File.separator + "a.txt") ; //操作文件
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setUserName("测试");
person.setAddress("昌平");
person.setAge(12);
//saveObject(person);
System.out.println(loadObject());
}
public static void saveObject(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SAVE_FILE)) ; //序列化构造方法
oos.writeObject(obj) ; //序列化实现方法
oos.close() ;
}
public static Object loadObject() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SAVE_FILE)) ;
Object obj = ois.readObject() ; //反序列化
ois.close() ;
return obj ;
}
}
public class Person implements Serializable {

private static final long serialVersionUID = 1L;
private String userName;
private Integer age;
private Integer sex;
private String address;
private String email;

public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Integer getSex() {
return sex;
}

public void setSex(Integer sex) {
this.sex = sex;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
return "Person{" +
"userName='" + userName + '\'' +
", age=" + age +
", sex=" + sex +
", address='" + address + '\'' +
", email='" + email + '\'' +
'}';
}
}

四、序列化ID的作用

可以看到,我们在进行序列化时,加了一个serialVersionUID字段,这便是序列化ID

 private static final long serialVersionUID = 1L;

这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。

即序列化ID是为了保证成功进行反序列化

五、默认的序列化ID

当我们一个实体类中没有显式的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。

如果没有显示指定serialVersionUID,会自动生成一个。

只有同一次编译生成的class才会生成相同的serialVersionUID

但是如果出现需求变动,Bean类发生改变,则会导致反序列化失败。为了不出现这类的问题,所以我们最好还是显式的指定一个serialVersionUID。

六、序列化其他问题

  1. 静态变量不会被序列化( static,transient)
  2. 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
  3. 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化。

子类序列化时:
 如果父类没有实现Serializable接口,没有提供默认构造函数,那么子类的序列化会出错;
 如果父类没有实现Serializable接口,提供了默认的构造函数,那么子类可以序列化,父类的成员变量不会被序列化。如果父类实现了Serializable接口,则父类和子类都可以序列化。
 

七、使用效率更高的序列化框架—Protostuff

其实java的原生序列化方式(通过实现Serialiable接口),效率并不是最高的。
github上有一个分析序列化效率的项目:https://github.com/eishay/jvm-serializers/wiki

其中看的出来性能最优的为google开发的colfer ,但是由于colfer的使用难度太大,而更多的都是使用protostuff序列化框架。适用改框架要引入两个库(core与runtime)。

①github地址:https://github.com/protostuff/protostuff

③如果用Maven,则添加依赖:
 

<dependency> <groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.7.2</version>
</dependency>

<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.7.2</version>
</dependency>

测试

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;

public class Main {
public static void main(String[] args) {
User user = new User();
user.setName("旭旭宝宝");
user.setAge(33);

Schema<User> schema = RuntimeSchema.getSchema(User.class);
// 保存对象,序列化,转化二进制数据
LinkedBuffer buffer = LinkedBuffer.allocate(512);
final byte[] protostuff;
try {
protostuff = ProtobufIOUtil.toByteArray(user, schema, buffer);
} finally {
buffer.clear();
}
// 读取对象,反序列化
User userObject = schema.newMessage();
ProtostuffIOUtil.mergeFrom(protostuff, userObject, schema);
System.out.println(userObject);
}
}

 User类,并未实现Serializable接口

public class User {

private String name;

private int age;

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 "User [name=" + name + ", age=" + age + "]";
}

}

标签:protostuff,java,String,age,序列化,public,serialVersionUID
From: https://blog.51cto.com/u_11334685/5740460

相关文章

  • 003Java的诞生
    003Java的诞生1、计算机语言发展史(1)第一代语言机器语言我们都知道计算机的基本计算方式都是基于二进制的方式。二进制:010111001010110010110100这种代码是直接输......
  • Java虚拟机详解(五)------JVM参数
    JVM参数有很多,其实我们直接使用默认的JVM参数,不去修改都可以满足大多数情况。但是如果你想在有限的硬件资源下,部署的系统达到最大的运行效率,那么进行相关的JVM参数设置是必......
  • 【Java复健指南01】简介与数组
    写在最前学习Java已经是很久之前的事情了,因为技术栈的转变,很久没有使用Java正经地开发过项目。对于该语言的理解也是停留在表面,因此萌生了重新学习的念头。一方面是为刷......
  • Java实现多线程
    Java实现多线程的方式有4种分别是继承Thread类,实现Runnable,Callable接口和通过线程池提交线程任务。其中实现Callable接口的方式可以获取返回值。1.继承Thread类通过继......
  • 浏览器中javascript简易实现json数据保存到客户端
    思路很简单,就是利用Blob、URL.createObjectURL()方法和<a>便签的HTML5新属性download来模拟远端文件下载保存。下面直接上代码savePath:function(){varme......
  • JavaScript异步概念及与c#异步的区别
    JS的异步操作函数往往是通过回调函数来实现异步任务的结果处理,在ES6之前如setTimeout函数和异步AJAX编程;在ES6规范后Promise类对象使得书写异步任务更加容易,返回Promise......
  • java---了解以下运算符
    了解即可1&2用于条件判断,&条件1和2都执行1&&2,条件1判断错误的情况下,条件2不执行&当运算符的化,例如4&7,两者上下对比都是1则为1,反之为0,结果就是二进制100也就是......
  • JAVA中计算两个日期时间的差值竟然也有这么多门道
    JAVA中计算两个日期时间的差值竟然也有这么多门道上半年春招的时候,作为面试官,对于面试表现的不错的同学会要求其写一小段代码看看。题目很简单:给定一个日期,然后计算下......
  • Java获取当前系统事件System.currentTimeMillis()方法 ,获取当前时间戳10位 166529114
    Java获取当前系统事件System.currentTimeMillis()方法,获取当前时间戳10位1665291145转为时间字符串yyy-MM-ddSystem.currentTimeMillis()产生一个当前的毫秒,这个毫秒......
  • java获取当前时间戳的三种方法比较效率(*)
    java获取当前时间戳的三种方法比较效率(*)获取当前时间戳//方法一System.currentTimeMillis();//方法二Calendar.getInstance().getTimeInMillis();//方法三ne......