序列化
问题
- 什么是序列化
- 什么是反序列化
- 序列化的方法
- 反序列化的方法
- transient关键字的含义
- Serializable 接口
- static变量能否被序列化
- 序列化ID
- 单例类的序列化
- Externalizable
- 序列化与对象克隆
解答
- 什么是序列化
序列化是将内存中的对象字节化到文件或者或者网络流中,用户持久化对象或者网络传输对象。由于是序列化为字节,所以与InputStream和OutputStream有关 - 什么是反序列化
反序列化是将文件或者数据库或者网络流中的字节解析成为Java对象。 - 序列化方法
使用ObjectOutputStream继承了OutputStream,并实现了DataOutput接口。 - 反序列化方法
使用ObjectInputStream,继承了InputStream,并实现了DataInput接口 - transient关键字
如果那个字段不需要序列化,那么使用transient后不会将该字段的值序列化,反序列化后是null - Serializable接口
该接口是一个标志接口,任何需要序列化的类必须要实现该接口表示序列化 - static变量能否被序列化
不能被序列化。static属于类成员,序列化针对的是对象 - 序列化ID
序列化ID代表这次序列化的对象的版本号,反序列化后会检查该id是否一致,如果不一致,那么序列化不一样。如果没有显示定义,那么序列化的时候默认取根据该类的.class文件计算一个值。反序列化的时候,如果对象修改了(任何修改,比如多一个空格),那么根据该类编译的class文件计算的序列化id也会不一样,导致反序列化失败。
如下:
Exception in thread "main" java.io.InvalidClassException: main.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 4102796403741744869
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at main.Hello.main(Hello.java:58)
最好自己定义序列化的ID
- 单例的序列化与反序列化
单例模式下,某个类被反序列化时,通过反射会新建一个对象,导致单例模式被破坏,所以,为了防止这种情况。需要在被序列化中加个readResolve
方法。为什么加这个方法?
因为在ObjectInputStream的readObject方法进行反序列化的时候,会检查该类是否有readResolve方法,如果有,那么会反序列后的对象就是该方法return的Object。
class Apple implements Serializable{
private static Apple instance = new Apple();
private Apple(){
}
public static Apple getInstance(){
return instance;
}
public void eat(){
System.out.println("eat apple");
}
private Object readResolve(){
return instance;
}
}
ObjectInputStream readObject调用链
readObject--->readObject0--->readOrdinaryObject--->checkResolve
*Externalizable
该接口继承了Serializeble接口
public interface Externalizable extends java.io.Serializable {
/**
* The object implements the writeExternal method to save its contents
* by calling the methods of DataOutput for its primitive values or
* calling the writeObject method of ObjectOutput for objects, strings,
* and arrays.
*
* @serialData Overriding methods should use this tag to describe
* the data layout of this Externalizable object.
* List the sequence of element types and, if possible,
* relate the element to a public/protected field and/or
* method of this Externalizable class.
*
* @param out the stream to write the object to
* @exception IOException Includes any I/O exceptions that may occur
*/
void writeExternal(ObjectOutput out) throws IOException;
/**
* The object implements the readExternal method to restore its
* contents by calling the methods of DataInput for primitive
* types and readObject for objects, strings and arrays. The
* readExternal method must read the values in the same sequence
* and with the same types as were written by writeExternal.
*
* @param in the stream to read data from in order to restore the object
* @exception IOException if I/O errors occur
* @exception ClassNotFoundException If the class for an object being
* restored cannot be found.
*/
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
如果需要定制序列化和反序列化的逻辑,那么可以实现该接口
package main;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.jar.JarInputStream;
class Person implements Externalizable {
public transient String name;
public String age;
public String country = "china";
public static String sex = "nan";
public String email;
public String address;
public Person() {
}
public Person(String name, String age, String country) {
this.name = name;
this.age = age;
this.country = country;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(1000);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = (String) in.readObject();
this.age = String.valueOf(in.readInt());
}
}
public class Hello {
public static void main(String[] args) throws Exception {
xuliehua();
fanxuliehua();
}
public static void xuliehua() throws IOException, ClassNotFoundException {
FileOutputStream fos = new FileOutputStream("person.txt");
ObjectOutputStream out = new ObjectOutputStream(fos);
Apple apple = Apple.getInstance();
out.writeObject(new Person("tom", "0", "japan"));
out.close();
}
public static void fanxuliehua() throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.txt"));
Person str = (Person) in.readObject();
System.out.println(str.name);
System.out.println(str.age);
}
}
- 序列化与对象克隆
见另一篇博文对象克隆