1 场景
java对象
某些时候,需要序列化
成字符串
存储在数据库中,需要的时候,再将字符串反序列化
为java对象。
如使用shiro缓存分布式session
,需要将session对象序列化成字符串存储在redis中,来达到分布式应用共享session的功能。
为什么不序列化成json?
java对象同样可以转换为json
,需要的时候,再转换为java对象。这种情况一般只适用转换自己创建的javaBean对象
,如果类是非自己管理的javaBean,对象内部存在没有对外开放set/get方法的属性
,转换后将丢失
。
序列化自己javaBean对象的好处是,通过序列化后的json字符串,可以很清楚知道被序列化的java对象的内容
。
2 应用
2.1 过程描述
序列化过程:
(1)java对象通过对象输出流ObjectOutputStream
,写入到内存字节数组输出流ByteArrayOutputStream
中
(2)通过内存字节数组输出流ByteArrayOutputStream
获取字节数组byte[]
(3)字节数组根据自定义算法
转换成字符串(hex/base64等)
反序列化过程:
(1)被序列化后字符串
,根据字节数组转换字符串的算法
,转换为字节数组byte[]
(2)字节数组通过内存字节数组输入流ByteArrayInputStream
写入到对象输入流ObjectInputStream
(3)通过对象输入流ObjectInputStream读取java对象
2.2 字节数组、字符串转换
字节数组转码成字符串依赖如下maven包:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
2.2.1 hex转码
(1)字节数组hex转码
public static String bytesToString(byte[] bytes) {
//转换成hex
return org.apache.commons.codec.binary.Hex.encodeHexString(bytes);
}
(2)字符串hex反转码成字节数组
public static byte[] stringToByte(String str) throws DecoderException {
//转换成hex
return org.apache.commons.codec.binary.Hex.decodeHex(str);
}
2.2.2 base64转码
建议此种方式
(1) 字节数组base64转码
public static String bytesToString(byte[] bytes) {
//转换成base64
return org.apache.commons.codec.binary.Base64.encodeBase64String(bytes);
}
(2) 字符串base64反转码成字节数组
public static byte[] stringToByte(String str) throws DecoderException {
//转换成base64
return org.apache.commons.codec.binary.Base64.decodeBase64(str);
}
2.3 对象序列化字节数组
字节数组、字符串转换使用2.2
中定义的转换方法:bytesToString、stringToByte
2.3.1 使用commons-lang3包实现对象序列化
建议此种方式,代码简单
maven依赖如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
代码如下:
(1)java对象序列化成字符串
public static String serialize(Serializable obj) throws Exception{
if(obj!=null) {
byte[] bytes = SerializationUtils.serialize(obj);
return bytesToString(bytes);
}
return null;
}
(2)字符串反序列化成java对象
public static <T extends Serializable> T deserialize(String str) throws Exception{
if(StringUtils.isNotEmpty(str)){
return SerializationUtils.deserialize(stringToByte(str));
}
return null;
}
2.3.2 编码实现对象序列化
代码如下:
(1)java对象序列化成字符串
public static String serialize(Serializable obj) throws Exception{
if(obj!=null) {
ByteArrayOutputStream bos=null;
ObjectOutputStream oos=null;
try {
bos=new ByteArrayOutputStream();
oos=new ObjectOutputStream(bos);
oos.writeObject(obj);
byte[] bytes = bos.toByteArray();
return bytesToString(bytes);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("序列化对象失败:"+String.valueOf(obj));
}finally {
try {
if(bos!=null){
bos.close();
}
if(oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
(2)字符串反序列化成java对象
public static <T extends Serializable> T deserialize(String str) throws Exception{
if(StringUtils.isNotEmpty(str)){
ByteArrayInputStream bai=null;
ObjectInputStream ois=null;
try{
bai=new ByteArrayInputStream(stringToByte(str));
ois=new ObjectInputStream(bai);
return (T)ois.readObject();
}catch (Exception e){
e.printStackTrace();
throw new Exception("字符串反序列化对象失败:"+String.valueOf(str));
}finally {
try {
if(ois!=null){
ois.close();
}
if(bai!=null){
bai.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
2.4 完整工具类代码
2.4.1 maven依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
2.4.2 java代码
package com.demo.cs.template.util;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
/**
* 序列化工具类
*/
public class SerializeUtil {
/**
* 字节数组转换为字符串
*/
public static String bytesToString(byte[] bytes) {
//// 字符串、字节数组转换方式很多,可以根据自己的要求,自定义转换方式
//转换成hex
//return org.apache.commons.codec.binary.Hex.encodeHexString(bytes);
//转换成base64
return org.apache.commons.codec.binary.Base64.encodeBase64String(bytes);
}
/**
* 字符串转换为字节数组
* @param str
* @return
*/
public static byte[] stringToByte(String str) throws DecoderException {
//// 字符串、字节数组转换方式很多,可以根据自己的要求,自定义转换方式
//转换成hex
//return org.apache.commons.codec.binary.Hex.decodeHex(str);
//转换成base64
return org.apache.commons.codec.binary.Base64.decodeBase64(str);
}
/**
* 序列化对象(依赖commons-lang3包)
* @param obj 序列化对象
* @return 对象序列化之后的字符串
*/
public static String serialize(Serializable obj) throws Exception{
if(obj!=null) {
byte[] bytes = SerializationUtils.serialize(obj);
return bytesToString(bytes);
}
return null;
}
/**
* 反序列化对象(依赖commons-lang3包)
* @param str 反序列化字符串
* @return 反序列化之后的对象
*/
public static <T extends Serializable> T deserialize(String str) throws Exception{
if(StringUtils.isNotEmpty(str)){
return SerializationUtils.deserialize(stringToByte(str));
}
return null;
}
}
3 限制
3.1 序列化前置条件
java对象可序列化成字节数组的前提是:
java类必须实现序列化接口:Serializable
,此接口代表该类的实例化对象,可以进行序列化(对象内的内置属性,也需要实现此接口
)。
如不实现此接口,序列化成对象时,将抛出异常:java.io.NotSerializableException
为防止调用工具类的时候,参数未实现此接口,可在工具类指定参数
或返回值泛型
的类型:
public static String serialize(Serializable obj) throws Exception{...}
public static <T extends Serializable> T deserialize(String str) throws Exception{...}
3.2 字节数组转换字符串
对象序列化后,是以字节数组的形式存在,需要将字节数组转码成可读的字符串。上面有hex和base64转码示例。
无论以哪种方式转码和反转码,均需采用一样的算法。
3.3 序列化时忽略属性
如对象属性被关键字transient
所修饰,对象序列化时,将会忽略此属性。如下:
private transient String userName;
4 扩展
4.1 hex和base64
(1)含义
java中有可见字符
和不可见字符
的区分,不可见字符在输出字符串的时候无法看到。如将字节数组
转换成字符串
,需要先根据编码规则将字节数组
转换成字符数组
,再将字符数组
转换成字符串
,由于部分转换后的字符是不可见的
,此类字符无法转换成可见的字符串,将存在数据的丢失
。在将字节数组转换成对应的字节时,hex和base64可以将不可见字符
转换成可见字符
,实现数据的二进制安全转换
。
hex和base64都是编码方式
和加密无关,将字节数组编码为可见的字符串
。
hex的意思是16进制
,编码后大小变为2倍
,一个字符用两个可见字符来表示。
base64如字面意思,64进制
来表示数组,编码后大小变成4/3
倍,3个字符用4个可见字符来表示。
(2)编码速度
hex编码解码速度比base64快,hex速度更快
。
(3)编码后大小
base64编码后字符串大小比hex小,base64占用空间更小
。
(4)转换后格式
hex编码后内容如下:
aced000573720022636f6d2e64656d6f2e63732e74656d706c6174652e6265616e2e54656d7055736572a55d364cf96782ab0200024c00036167657400134c6a6176612f6c616e672f496e74656765723b4c0008757365724e616d657400124c6a6176612f6c616e672f537472696e673b7870737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000012740006e5bca0e4b889
base64编码后内容如下:
rO0ABXNyACJjb20uZGVtby5jcy50ZW1wbGF0ZS5iZWFuLlRlbXBVc2VypV02TPlngqsCAAJMAANhZ2V0ABNMamF2YS9sYW5nL0ludGVnZXI7TAAIdXNlck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAASdAAG5byg5LiJ
4.2 base32
还有种编码方式base32,如字面意思,32进制来转码字符数组。可酌情使用
网友评论