java的序列化与反序列化

前言:本文主要记录了java中序列化与反序列化的一些概念,大家最好配合源码一起理解会更好~~~

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

序列化:把对象转换为字节序列的过程称为对象的序列化
反序列化: 把字节序列恢复为对象的过程称为对象的反序列化

什么时候要用到?

只要我们对内存中的对象进行持久化或网络传输, 这个时候都需要序列化和反序列化

Java中如何序列化?

Java序列化提供两种方式
1.实现Serializable接口
2.实现Exteranlizable接口
我们先说第一种

实现Serializable接口

在Java中实现了Serializable接口后, JVM会在底层帮我们实现序列化和反序列化,实际开发过程中除了自己的实体类,好像我们没有特殊去做处理,但是事实是如此吗?请看代码示例
// String类
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ...
    }
   
// ArrayList类   
public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    ...
    }

// HashMap类
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
	...
	}

等等...
大家有没有发现,这些我们常用的Java类其实已经给我们封装好了(都实现了Serializable接口),我们可以简单总结一下
  1. 一个类只有实现了 Serializable 接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现 Serializable 接口。而实际上,Serializable 的源码是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
  2. serialization 允许你将实现了 Serializable 接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。
对于实现Serializable非常简单,这里以idea开发工具为例
// 实现Serializable接口
xxx implements Serializable
但是真实开发过程中,我们会看到这段代码
private static final long serialVersionUID = 1L;
实现Serializable接口就算了, 为什么还要显示指定serialVersionUID的值?
如果不显示指定serialVersionUID, JVM在序列化时会根据属性自动生成一个serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输. 在反序列化时, JVM会再根据属性自动生成一个新版serialVersionUID, 然后将这个新版serialVersionUID与序列化时生成的旧版serialVersionUID进行比较, 如果相同则反序列化成功, 否则报错.

如果显示指定了serialVersionUID, JVM在序列化和反序列化时仍然都会生成一个serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的serialVersionUID就一致了.

在实际开发中, 不显示指定serialVersionUID的情况会导致什么问题? 如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错. 所以在实际开发中, 我们都会显示指定一个serialVersionUID, 值是多少无所谓, 只要不变就行.
在开发过程中,新建的实体类实现了Serializable,默认是不会生成serialVersionUID值的,我们可以在idea中配置一下,没有serialVersionUID值的实体类会黄色显示。我们 alt+enter 就可以一键生成

在这里插入图片描述

我们再说第二种

实现Exteranlizable接口

需要重写writeExternal和readExternal方法,它的效率比Serializable高一些,并且可以决定哪些属性需要序列化(即使是transient修饰的),但是对大量对象,或者重复对象,则效率低。上面说到了transient,别急我们详细来说一下Serializable失效的一些场景。

Java序列化有哪些失效场景?

先给结论:
1.transient关键字修饰的属性不会被序列化(看情况)
2.static关键字修饰的属性也不会被序列化
1.static属性为什么不会被序列化?
因为序列化是针对对象而言的, 而static属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.

看到这个结论, 是不是有人会问, serialVersionUID也被static修饰, 为什么serialVersionUID会被序列化? 其实serialVersionUID属性并没有被序列化, JVM在序列化对象时会自动生成一个serialVersionUID, 然后将我们显示指定的serialVersionUID属性值赋给自动生成的serialVersionUID.
2.深入分析transient关键字

先说transient底层实现原理:

java的serialization提供了一个非常棒的存储对象状态的机制,说白了serialization就是把对象的状态存储到硬盘上 去,等需要的时候就可以再把它读出来使用。有些时候像银行卡号这些字段是不希望在网络上传输的,transient的作用就是把这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化,意思是transient修饰的age字段,他的生命周期仅仅在内存中,不会被写到磁盘中。

被transient关键字修饰过得变量真的不能被序列化吗?

事实上,当实现了Externalizable接口时,哪一个属性被序列化使我们手动去指定的,即使是transient关键字修饰也不起作用

总结一下

1.实现Serializable接口时,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中

2.实现Externalizable接口时,只要手动指定了哪些属性需要被序列化,即使是transient关键字修饰也不起作用

3.static关键字修饰的属性不会被序列化(不管是实现哪一个)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值