前言
创建对象时,只要我们需要,便会一直存在。而若是程序终止,那无论如何它都不会继续存在。但某些情况下,如果对象能够在程序不运行时仍能保存其信息,那将非常有用。这样,在下次运行程序时,该对象将被重建并且拥有的信息与在程序上次运行时它所拥有的信息相同。此时,对象序列化便隆重登场了...
关于对象序列化
当我们在内存中创建可复用的Java对象时,一般情况下会依赖于JVM的生命周期,即会随着JVM的停止而销毁。但Java对象序列化可实现JVM停止运行之后保存(持久化)指定的对象,并可随时读取该对象。
引用《Thinking in Java》里面的一段话:
利用它(对象序列化)可以实现轻量级持久性,“持久性”意味着一个对象的生存周期并不取决于程序是否正在执行;它可生存于程序的调用之间。
解析对象序列化
序列化分为两部分:序列化 和 反序列化 。
- 序列化:将数据分解成字节流,以便在文件或网络上传输。(即 Java Object 转成 byte[] )
- 反序列化:打开字节流并重构对象,从而取出对象。(即 byte[] 转成 Java Object )
一般用途
1. 将内存中的对象状态保存至文件或数据库
2. 网络传输
3. Java远程方法调用(RMI)
具体实例
(一)将对象状态保存至文件或数据库
- 实现 java.io.Serializable 接口
File file = new File("person.out");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file)); //创建一个写入指定OutputStream的ObjectOutputStream对象
Person person = new Person("Hello", 101, Gender.MALE);
out.writeObject(person); //将指定的对象写入
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object newPerson = in.readObject(); //从ObjectInputStream读取对象
in.close();
System.out.println(newPerson);
分析:
上述是网上常见的一个例子,非常简单。实现
Serializable 接口采用的是默认序列化机制,我们注意到以上例子使用了两个类 ObjectOutputStream 、ObjectInputStream ,该demo就是使用了 ObjectOutputStream 来持久化对象,而使用 ObjectInputStream 从文件中取出对象。
影响序列化的因素:transient关键字
(1) 当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
(2) 若是想运用其它方法来序列化这个标了transient的字段,则可以通过添加两个方法:writeObject()与readObject()。
- 实现 java.io. Externalizable 接口
JDK中提供了另一个序列化接口——
Externalizable ,** Externalizable** 继承于 Serializable ,序列化的细节需要程序员完成。使用该接口之后,之前基于 Serializable 接口的序列化机制就将失效。
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
(二) 使用 Protostuff 序列化库
在开发项目中,序列化是经常需要处理的问题。不管是网络传输上用json、xml、对象序列化,还是缓存数据中的序列化,都是相当重要的。
前提
以并发系统为例,使用 Redis 缓存存储数据时,若单单只使用JDK的序列化,效率会很低,对并发类系统造成的影响无法想象。而Google开发的一套序列化方案 Protostuff ,好处很多,独立于语言,独立于平台,重要的是效率相当高,使用 Protostuff 序列化后的字节大小是json的10分之一,xml的20分之一,是二进制序列化的10分之一。
目的
以实际的并发系统为例,使用 Protostuff 框架进行序列化处理。
说明
- 开发环境:IDEA,Tomcat
- 软件环境:SpringMVC,Spring,MyBatis
- 模块描述:若缓存中有我们需要的对象,则取出来;若是没有,则存入缓存中( Redis )
过程
- 序列化
set Object() -> 序列化 -> byte[]
- 反序列化
get -> byte[] -> 反序列化 -> Object()
编码
- Maven 引入 Protostuff
<!-- protostuff序列化依赖 -->
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.8</version>
</dependency>
- 在Dao层缓存RedisDao中引入 Protostuff
set Object() -> 序列化 -> byte[]
//LinkedBuffer:缓存器
byte[] bytes = ProtobufIOUtil.toByteArray(seckill, schema,
LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
//超时缓存
int timeout = 60 * 60; //1小时
String result = jedis.setex(key.getBytes(), timeout, bytes);
get -> byte[] -> 反序列化 -> Object()
byte[] bytes = jedis.get(key.getBytes());
//从缓存获取到
if (bytes != null) {
//空对象
Seckill seckill = schema.newMessage();
ProtobufIOUtil.mergeFrom(bytes, seckill, schema);
return seckill;
}
测试
结果表明,字节数被压缩得极小,在并发系统效率确实不错,很值得学习的一个序列化库!
总结
本博文的内容均为自己的总结,想深入学习的朋友可参考《Thinking in Java》和《Effective Java》,里面写的内容非常详细的。