一、什么是序列化和反序列化?
- Java 序列化是指把 Java 对象转换为字节序列的过程;
- Java 反序列化是指把字节序列恢复为 Java 对象的过程。
二、序列化的作用?
- 对象持久化,可以把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中(节省内存);
例如:
Web 服务器中的 Session 会话对象,当有10万用户并发访问,就有可能出现10万个 Session 对象,显然这种情况内存可能是吃不消的。
于是 Web 容器就会把一些 Session 先序列化,让他们离开内存空间,序列化到硬盘中,当需要调用时,再把保存在硬盘中的对象还原到内存中。 - 在网络上传送对象的字节序列(网络通信时一般都以二进制序列的形势在网上传输,如文本、图片等)。
三、Java中的序列化与反序列化?
3.1 Java序列化API
Java中的序列化方式有三种:
- 如果类a仅仅实现了Serializable接口,则
ObjectOutputStream采用默认的序列化方式,对a对象的非transient实例变量进行序列化
ObjectInputStream采用默认的反序列化方式,对a对象的非transient实例变量进行反序列化 - 如果类a仅仅实现了Serializable接口,并且还定义了a对象的writeObject(ObjectOutputStream out) 和 readObject(ObjectInputStream in),则
ObjectOutputStream调用a对象的writeObject(ObjectOutputStream out)的方法进行序列化
ObjectInputStream调用a对象的readObject(ObjectInputStream in)的方法进行序列化 - 如果a类实现了ExternaInalizable接口,且User类必须实现readExternal(ObjectInput in)和wiriteExternal(ObjectOutput out)方法,则
ObjectOutputStream调用a对象的wiriteExternal(ObjectOutput out)的方法进行序列化
ObjectInputStream调用a对象的readExternam(ObjectInput in)的方法进行序列化
3.2 序列化步骤
--1--创建一个对象输出流,它可以包装一个奇特类型的目标输出流,如文件输出流:
objectOutputStream oos=new objectOutputStream(new FileOutStream(c:\\object.out));
--2--通过对象输出流writeObject()方法写对象:
oos.writeObject(new A("xiaoxiao","145263","female"));
3.3 反序列化步骤
--1--创建一个对象输入流,它可以包装一个其他类型输入流,如文件输入流:
objectInputStream ois=new ObjectInputStream(new FileInputStream("object.out"));
--2--通过对象输出流的readObject()方法读取对象:
A a=(A)ois.readObject();
--3--为了正确读数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致
3.4 serialVersionUID用途及显式和隐式区别?
- serialVersionUID的真实用途:
序列化和反序列化就是通过对比其 SerialversionUID 来进行的,我们修改一个实现 Serializable 接口的实体类,重新编译后,显然程序会重新会生成新值,那么一旦SerialversionUID 跟之前不匹配,反序列化就无法成功。 - serialVersionUID显式和隐式区别:
- 隐式定义:
当实体中增加属性后,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。例如,如果没有指定A类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显式指定serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。 - 显式定义:
显式定义serialVersionUID,这样就可以在序列化后,去添加一个字段,或者方法,而不会重新生成serialVersionUID,进而也就不会影响到后期的反序列化还原。
四、Hessian序列化与反序列化?
4.1 定义抽象序列化器
public abstract class AbstractSerializer {
/**
* 序列化
*
* @param o
* @return
*/
public abstract byte[] toBytes(Object o) throws Exception;
/**
* 反序列化
*
* @param bytes
* @param <T>
* @return
*/
public abstract <T> T fromBytes(byte[] bytes) throws Exception;
}
4.2 实现Hessian序列化器
public class HessianSerializer extends AbstractSerializer {
private static final Logger LOGGER = LoggerFactory.getLogger(HessianSerializer.class);
private static final com.caucho.hessian.io.SerializerFactory SERIALIZER_FACTORY = new com.caucho.hessian.io.SerializerFactory();
@Override
public byte[] toBytes(Object o) throws Exception {
Hessian2Output h2os = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
h2os = new Hessian2Output(bos);
h2os.setSerializerFactory(SERIALIZER_FACTORY);
h2os.writeObject(o);
h2os.flush();
return bos.toByteArray();
} finally {
close(h2os);
}
}
private void close(Hessian2Output h2os) {
if (h2os != null) {
try {
h2os.close();
} catch (Exception e) {
LOGGER.info("failed to close hessian output stream", e);
}
}
}
@Override
public <T> T fromBytes(byte[] bytes) throws Exception {
Hessian2Input h2is = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
h2is = new Hessian2Input(bis);
h2is.setSerializerFactory(SERIALIZER_FACTORY);
Object rv = h2is.readObject();
return (T)rv;
} finally {
close(h2is);
}
}
private void close(Hessian2Input h2is) {
if (h2is != null) {
try {
h2is.close();
} catch (Exception e) {
LOGGER.info("failed to close hessian input stream", e);
}
}
}
}
4.3 创建序列化枚举,方便扩展序列化类型
public enum SerializerType {
HESSIAN(5, new HessianSerializer());
private int type;
private AbstractSerializer instance;
SerializerType(int type, AbstractSerializer instance) {
this.type = type;
this.instance = instance;
}
public int getType() {
return type;
}
public AbstractSerializer getInstance() {
return instance;
}
public static SerializerType getByType(int type) {
for (SerializerType serializerType : values()) {
if (serializerType.type == type) {
return serializerType;
}
}
return null;
}
}
五、Hessian与Java序列化的差异?
- Java序列化:
Java序列化会把要序列化的对象类的元数据和业务数据全部序列化从字节流,而且是把整个继承关系上的东西全部序列化了。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。 - Hessian序列化:
hessian序列化,它的实现机制是着重于数据,附带简单的类型信息的方法。就像Integer a = 1,hessian会序列化成I 1这样的流,I表示int or Integer,1就是数据内容。而对于复杂对象,通过Java的反射机制,hessian把对象所有的属性当成一个Map来序列化,产生类似M className propertyName1 I 1 propertyName S stringValue这样的流,包含了基本的类型描述和数据内容。而在序列化过程中,如果一个对象之前出现过,hessian会直接插入一个R index这样的块来表示一个引用位置,从而省去再次序列化和反序列化的时间。这样做的代价就是hessian需要对不同的类型进行不同的处理(因此hessian直接偷懒不支持short),而且遇到某些特殊对象还要做特殊的处理(比如StackTraceElement)。而且同时因为并没有深入到实现内部去进行序列化,所以在某些场合会发生一定的不一致,比如通过Collections.synchronizedMap得到的map。