Java 序列化
Serializable 接口、对象序列化与反序列化、serialVersionUID 与 transient。
Serializable 接口
Serializable is a marker interface with no methods: implementing it tells the JVM the class may be serialised. Subclasses inherit that automatically.
import java.io.Serializable;
class Employee implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
transient String password;
Employee(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
}
public class MarkerDemo {
public static void main(String[] args) {
Employee e = new Employee("Tom", 20, "secret");
System.out.println(e instanceof Serializable);
System.out.println(e.name + " " + e.password);
}
}true
Tom secret序列化对象
ObjectOutputStream.writeObject writes an object to a byte stream — a file or a socket. Only the non-transient fields of a serialisable object are written.
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Point implements Serializable {
private static final long serialVersionUID = 1L;
int x, y;
transient int cached;
Point(int x, int y) {
this.x = x;
this.y = y;
this.cached = 999;
}
}
public class SerializeDemo {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
out.writeObject(new Point(1, 2));
}
System.out.println(bos.size() > 0);
}
}true反序列化对象
ObjectInputStream.readObject restores it. Deserialisation skips constructors and restores fields straight from the stream, so transient fields come back as defaults (0 here).
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Point implements Serializable {
private static final long serialVersionUID = 1L;
int x, y;
transient int cached;
Point(int x, int y) {
this.x = x;
this.y = y;
this.cached = 999;
}
}
public class DeserializeDemo {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
out.writeObject(new Point(1, 2));
}
try (ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()))) {
Point p = (Point) in.readObject();
System.out.println(p.x + "," + p.y);
System.out.println(p.cached);
}
}
}1,2
0serialVersionUID the JVM derives one from the class shape; adding a field changes it and old data fails with InvalidClassException. Pin it explicitly.Serializable 接口
Serializable 是标记接口,没有任何方法 —— 实现它就等于告诉 JVM「这个类可以被序列化」。它的子类也会自动可序列化。
import java.io.Serializable;
class Employee implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
transient String password;
Employee(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
}
public class MarkerDemo {
public static void main(String[] args) {
Employee e = new Employee("Tom", 20, "secret");
System.out.println(e instanceof Serializable);
System.out.println(e.name + " " + e.password);
}
}true
Tom secret序列化对象
ObjectOutputStream.writeObject 把对象写成字节流,可以写到文件或网络。只有可序列化对象的非 transient 字段会被写入。
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Point implements Serializable {
private static final long serialVersionUID = 1L;
int x, y;
transient int cached;
Point(int x, int y) {
this.x = x;
this.y = y;
this.cached = 999;
}
}
public class SerializeDemo {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
out.writeObject(new Point(1, 2));
}
System.out.println(bos.size() > 0);
}
}true反序列化对象
ObjectInputStream.readObject 还原对象。反序列化不调用构造方法,字段直接从字节流恢复,所以 transient 字段会是默认值(这里是 0)。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Point implements Serializable {
private static final long serialVersionUID = 1L;
int x, y;
transient int cached;
Point(int x, int y) {
this.x = x;
this.y = y;
this.cached = 999;
}
}
public class DeserializeDemo {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
out.writeObject(new Point(1, 2));
}
try (ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()))) {
Point p = (Point) in.readObject();
System.out.println(p.x + "," + p.y);
System.out.println(p.cached);
}
}
}1,2
0serialVersionUID 不写时 JVM 按类结构自动生成,改一个字段就会变,导致旧数据反序列化抛 InvalidClassException。显式声明一个固定值最稳。