java中的序列化和反序列化
介绍
序列化就是将对象转换成字节序列,反序列化就是将字节序列转换成对象。
使用
默认序列化和反序列化
public class Client {
public static void main(String[] args) {
User user = new User();
user.setUserName("lisi");
System.out.println(user);
byte[] data = serialize(user);
User newUser = deserialize(data, User.class);
System.out.println(newUser);
System.out.println(user == newUser);
}
// 将一个对象序列化成字节序列
private static byte[] serialize(Object obj) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(obj);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// 将字节序列反序列化成指定类型的对象
private static <T> T deserialize(byte[] data, Class<T> clazz) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream bis = new ObjectInputStream(bais)) {
Object obj = bis.readObject();
return clazz.cast(obj);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Setter
@Getter
@ToString
public static class User implements Serializable {
private String userName;
}
}
一个类要序列化必须实现Serializable接口,序列化字段为非static非transient修饰,
从结果可以看到反序列化出的对象是一个新的对象。
自定义序列化和反序列化
public class Client {
public static void main(String[] args) {
User user = new User();
user.setUserName("lisi");
System.out.println(user);
byte[] data = serialize(user);
User newUser = deserialize(data, User.class);
System.out.println(newUser);
System.out.println(user == newUser);
}
private static byte[] serialize(Object obj) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(obj);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static <T> T deserialize(byte[] data, Class<T> clazz) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream bis = new ObjectInputStream(bais)) {
Object obj = bis.readObject();
return clazz.cast(obj);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Setter
@Getter
@ToString
public static class User implements Serializable {
private String userName;
// 自定义序列化过程
private void writeObject(ObjectOutputStream oos) throws IOException {
// 默认序列化逻辑
oos.defaultWriteObject();
// 将用户名长度序列化
oos.writeInt(userName.length());
}
// 自定义反序列化过程
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// 默认反序列化逻辑
ois.defaultReadObject();
// 读取用户名长度
System.out.println(ois.readInt());
}
}
}
自定义序列化必须添加writeObject方法,必须为private非static,参数类型为ObjectOutputStream,返回类型为void。
自定义反序列化必须添加readObject方法,必须为private非static,参数类型为ObjectInputStream,返回类型为void。
HashMap就是通过自定义来实现序列化和反序列化的。
/**
* Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
* serialize it).
*
* @serialData The <i>capacity</i> of the HashMap (the length of the
* bucket array) is emitted (int), followed by the
* <i>size</i> (an int, the number of key-value
* mappings), followed by the key (Object) and value (Object)
* for each key-value mapping. The key-value mappings are
* emitted in no particular order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
int buckets = capacity();
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
s.writeInt(buckets);
s.writeInt(size);
internalWriteEntries(s);
}
/**
* Reconstitute the {@code HashMap} instance from a stream (i.e.,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
reinitialize();
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
else if (mappings > 0) { // (if zero, use defaults)
// Size the table using given load factor only if within
// range of 0.25...4.0
float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
float fc = (float)mappings / lf + 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
更彻底的自定义
public class Client {
public static void main(String[] args) {
User user = new User();
user.setUserName("lisi");
System.out.println(user);
byte[] data = serialize(user);
User newUser = deserialize(data, User.class);
System.out.println(newUser);
System.out.println(user == newUser);
}
private static byte[] serialize(Object obj) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(obj);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static <T> T deserialize(byte[] data, Class<T> clazz) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream bis = new ObjectInputStream(bais)) {
Object obj = bis.readObject();
return clazz.cast(obj);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Setter
@Getter
@ToString
public static class User implements Serializable {
private String userName;
// 替换序列化对象
private Object writeReplace() {
User user = new User();
user.setUserName(userName);
return user;
}
// 处理反序列出的对象
private Object readResolve() {
userName = "lisi1";
return this;
}
}
}
writeReplace方法会在真正的序列化执行之前执行,会替换我们的待序列化对象,readResolve会在反序列化执行之后执行,就可以对结果做一些后置处理。通过readResolve我们可以进行单例的保护性恢复,直接返回单例实例。
java11中的不可变集合序列化和反序列化就是通过这两个方法实现的。
序列化Id
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
必须为static且final的字段。
如果我们没有声明这个字段,序列化时会根据class,method信息综合计算出一个。