1. Java 序列化机制
1.1 定义
Java 序列化是将对象的状态转换为字节序列的过程,以便可以将对象持久化到文件、通过网络传输或存储到缓存中。反序列化则是将字节序列恢复为对象的过程。
1.2 实现 Serializable
接口
-
Serializable
接口:是一个标记接口,没有方法。它用于指示 Java 序列化机制该类的对象可以被序列化。 -
示例 :
javaimport java.io.Serializable; public class MyObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; // Getters and Setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "MyObject{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
1.3 序列化与反序列化操作
-
序列化 :使用
ObjectOutputStream
将对象写入流。javaimport java.io.FileOutputStream; import java.io.ObjectOutputStream; public class SerializationExample { public static void main(String[] args) { MyObject myObject = new MyObject(); myObject.setName("Kimi"); myObject.setAge(25); try (FileOutputStream fileOutputStream = new FileOutputStream("myObject.ser"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { objectOutputStream.writeObject(myObject); System.out.println("Object serialized successfully."); } catch (Exception e) { e.printStackTrace(); } } }
-
反序列化 :使用
ObjectInputStream
从流中读取对象。javaimport java.io.FileInputStream; import java.io.ObjectInputStream; public class DeserializationExample { public static void main(String[] args) { try (FileInputStream fileInputStream = new FileInputStream("myObject.ser"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { MyObject myObject = (MyObject) objectInputStream.readObject(); System.out.println("Deserialized Object: " + myObject.toString()); } catch (Exception e) { e.printStackTrace(); } } }
1.4 serialVersionUID
的作用
-
唯一标识符 :
serialVersionUID
是一个静态常量字段,用于标识类的版本。它在序列化和反序列化过程中用于验证类的版本是否一致。 -
自动生成 :如果不显式定义
serialVersionUID
,Java 会根据类的结构自动生成一个值。但建议显式定义一个固定的值,以避免因类结构变化导致的版本不一致问题。 -
示例 :
javaprivate static final long serialVersionUID = 1L;
1.5 类结构变化与 serialVersionUID
-
字段添加/删除:
-
如果类的结构变化不大(如添加或删除一些非关键字段),可以保持
serialVersionUID
不变。反序列化时,新字段的值会是其默认值。 -
示例:添加字段
address
,但保持serialVersionUID
不变。javaprivate String address;
-
-
字段类型改变:
-
如果字段类型发生改变,需要更新
serialVersionUID
的值。否则,反序列化时会抛出InvalidClassException
。 -
示例:将字段
age
从int
改为String
,并更新serialVersionUID
。javaprivate static final long serialVersionUID = 2L; private String age;
-
-
字段数量大幅变化:
- 如果字段数量发生大幅变化(如删除多个字段),建议更新
serialVersionUID
的值,以避免反序列化时出现不一致的问题。
- 如果字段数量发生大幅变化(如删除多个字段),建议更新
1.6 注意事项
- 安全性:反序列化时可能会引入安全风险,建议仅从可信源加载序列化数据。
- 性能:Java 的默认序列化机制相对低效,如果需要高性能,可以考虑使用更高效的序列化框架(如 Kryo、Protocol Buffers 等)。
2. Jackson 序列化机制
2.1 定义
Jackson 是一个流行的 JSON 处理库,用于将 Java 对象序列化为 JSON 格式的字符串,以及将 JSON 格式的字符串反序列化为 Java 对象。它广泛应用于 RESTful API 的开发中。
2.2 使用 Jackson 进行序列化和反序列化
-
序列化:将 Java 对象转换为 JSON 格式的字符串。
javaimport com.fasterxml.jackson.databind.ObjectMapper; public class JacksonSerializationExample { public static void main(String[] args) { MyObject myObject = new MyObject(); myObject.setName("Kimi"); myObject.setAge(25); try { ObjectMapper objectMapper = new ObjectMapper(); String jsonString = objectMapper.writeValueAsString(myObject); System.out.println("Serialized JSON: " + jsonString); } catch (Exception e) { e.printStackTrace(); } } }
-
反序列化:将 JSON 格式的字符串转换为 Java 对象。
javaimport com.fasterxml.jackson.databind.ObjectMapper; public class JacksonDeserializationExample { public static void main(String[] args) { String jsonString = "{\"name\":\"Kimi\",\"age\":25}"; try { ObjectMapper objectMapper = new ObjectMapper(); MyObject myObject = objectMapper.readValue(jsonString, MyObject.class); System.out.println("Deserialized Object: " + myObject.toString()); } catch (Exception e) { e.printStackTrace(); } } }
2.3 Jackson 的特点
- 基于 JSON:Jackson 使用 JSON 格式进行序列化和反序列化,适合 Web 应用和 RESTful API。
- 注解支持 :通过注解(如
@JsonProperty
、@JsonIgnore
等)可以控制序列化和反序列化的行为。 - 性能优化:Jackson 提供了高效的序列化和反序列化性能,适合处理大量数据。
2.4 示例类
java
import com.fasterxml.jackson.annotation.JsonProperty;
public class MyObject {
@JsonProperty("name")
private String name;
@JsonProperty("age")
private int age;
// Getters and Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MyObject{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3. Jackson 与 Java 序列化机制的不同
3.1 序列化格式
- Java 序列化:将对象序列化为二进制格式,适合持久化和网络传输。
- Jackson 序列化:将对象序列化为 JSON 格式的字符串,适合 Web 应用和 RESTful API。
3.2 是否需要实现接口
- Java 序列化 :需要实现
java.io.Serializable
接口。 - Jackson 序列化:不需要实现任何接口,但可以通过注解来控制序列化和反序列化的行为。
3.3 性能
- Java 序列化:相对低效,尤其是处理大量数据时。
- Jackson 序列化:性能优化较好,适合处理大量数据。
3.4 安全性
- Java 序列化:存在反序列化漏洞的风险,建议仅从可信源加载序列化数据。
- Jackson 序列化:相对安全,但仍需注意 JSON 数据的来源。
3.5 使用场景
- Java 序列化:适合需要将对象持久化为二进制文件或通过网络传输二进制数据的场景。
- Jackson 序列化:适合需要将对象转换为 JSON 格式(如 RESTful API 的响应)或从 JSON 格式还原为对象的场景。
4. 总结
-
Java 序列化:
- 需要实现
Serializable
接口。 - 使用二进制格式,适合持久化和网络传输。
- 需要显式定义
serialVersionUID
,并在类结构发生变化时手动更新。 - 相对低效,存在安全风险。
- 需要实现
-
Jackson 序列化:
- 不需要实现任何接口