Java对象与字符串相互转化的方式
今天在用AI辅助编码时,遇到一个AI写的小缺陷,但是这个小缺陷也可能会有小伙伴遇到,那便是将Bean通过toString()进行了转化,存到了数据库,在后续要再使用时,转不出来系统报错了。
这里我们就来看看这种场景下这个问题应该怎么解决。
要把对象保存到数据库并能还原,常用且安全/可维护的做法有两种:
- Java 原生序列化(
Serializable
) — 存为 BLOB 或 Base64 字符串(可直接还原,但有安全与兼容性风险)。 - JSON 序列化(推荐) — 使用
Jackson
/Gson
把对象转成 JSON 字符串存VARCHAR
/TEXT
,恢复时从 JSON 反序列化回对象(更便于阅读、兼容、跨语言)。
下面给出完整示例代码(包含 JDBC 存取示例),并说明为什么不要使用 toString()
。
例子说明
-
演示一个简单的
Person
类。 -
演示三部分代码:
- 坏做法:
toString()
(展示为何不可逆)。 - 原生序列化 -> Base64 存字符串 -> 反序列化恢复。
- JSON(使用 Jackson)序列化/反序列化(推荐)。
- 坏做法:
-
给出 JDBC 插入/查询示例(示范性,不含连接池、事务管理等生产细节)。
1) 示例类(Person)
import java.io.Serializable;public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;// 默认构造器(JSON/反序列化需要)public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}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; }// 注意:toString() 仅用于调试,不用于序列化@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}
2) 为什么不要用 toString()
?
toString()
的输出格式并非标准化,通常为人阅读设计,不保证字段完整性或未来兼容性。除非你自己定义了严格、可逆的 toString()
/fromString()
,否则无法可靠恢复原对象。
如果你确实用 toString()
存储,只有在你完全控制格式并提供严格解析逻辑时 才能还原;但这通常比直接使用标准序列化/JSON 更脆弱且费力。
3) 原生 Java 序列化(存 Base64 字符串)——示例代码
import java.io.*;
import java.util.Base64;public class JavaSerializationUtil {public static String serializeToBase64(Object obj) throws IOException {ByteArrayOutputStream bos = new ByteArrayOutputStream();try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(obj);}return Base64.getEncoder().encodeToString(bos.toByteArray());}public static Object deserializeFromBase64(String base64) throws IOException, ClassNotFoundException {byte[] data = Base64.getDecoder().decode(base64);try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {return ois.readObject();}}
}
JDBC 示例(将 Base64 字符串存为 VARCHAR 或 TEXT):
import java.sql.*;public class JdbcExample {// 假设已经有 JDBC URL、用户名密码public static void savePersonAsBase64(Connection conn, int id, String base64) throws SQLException {String sql = "INSERT INTO persons_blob (id, data_base64) VALUES (?, ?)";try (PreparedStatement ps = conn.prepareStatement(sql)) {ps.setInt(1, id);ps.setString(2, base64);ps.executeUpdate();}}public static String loadPersonBase64(Connection conn, int id) throws SQLException {String sql = "SELECT data_base64 FROM persons_blob WHERE id = ?";try (PreparedStatement ps = conn.prepareStatement(sql)) {ps.setInt(1, id);try (ResultSet rs = ps.executeQuery()) {if (rs.next()) return rs.getString("data_base64");return null;}}}
}
安全注意:Java 原生反序列化可能存在反序列化漏洞(如果你从不可信来源反序列化)。只在受信环境或采取白名单/安全措施时使用。
4) 推荐:使用 JSON(以 Jackson 为例)
Maven 依赖
<!-- Jackson -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version> <!-- 示例,请以实际需要调整 -->
</dependency>
JSON 序列化/反序列化工具类
import com.fasterxml.jackson.databind.ObjectMapper;public class JsonUtil {private static final ObjectMapper mapper = new ObjectMapper();public static String toJson(Object obj) throws Exception {return mapper.writeValueAsString(obj);}public static <T> T fromJson(String json, Class<T> clazz) throws Exception {return mapper.readValue(json, clazz);}
}
JDBC 示例(把 JSON 存 VARCHAR/TEXT)
import java.sql.*;public class JdbcJsonExample {public static void savePersonAsJson(Connection conn, int id, String json) throws SQLException {String sql = "INSERT INTO persons_json (id, data_json) VALUES (?, ?)";try (PreparedStatement ps = conn.prepareStatement(sql)) {ps.setInt(1, id);ps.setString(2, json);ps.executeUpdate();}}public static String loadPersonJson(Connection conn, int id) throws SQLException {String sql = "SELECT data_json FROM persons_json WHERE id = ?";try (PreparedStatement ps = conn.prepareStatement(sql)) {ps.setInt(1, id);try (ResultSet rs = ps.executeQuery()) {if (rs.next()) return rs.getString("data_json");return null;}}}
}
使用示例(完整流程)
// 假设 conn 是已打开的 JDBC Connection
Person p = new Person("Alice", 30);// JSON 存取(推荐)
String json = JsonUtil.toJson(p);
JdbcJsonExample.savePersonAsJson(conn, 1, json);// 读取并还原
String loadedJson = JdbcJsonExample.loadPersonJson(conn, 1);
Person p2 = JsonUtil.fromJson(loadedJson, Person.class);// Java 原生序列化存取(可行,但需谨慎)
String base64 = JavaSerializationUtil.serializeToBase64(p);
JdbcExample.savePersonAsBase64(conn, 2, base64);String loadedBase64 = JdbcExample.loadPersonBase64(conn, 2);
Person p3 = (Person) JavaSerializationUtil.deserializeFromBase64(loadedBase64);
5) 小结与建议(要点)
- 不要用
toString()
存数据库 —— 不可逆、格式不稳定、不能确保完整字段数据。 - 推荐使用 JSON(Jackson/Gson):跨语言、人可读、易调试、向前兼容性好。存
VARCHAR/TEXT
。 - 如果对象包含复杂类型或必须保持二进制完整性(比如图像、缓存对象),可以用 Java 序列化或 protobuf、Avro 等二进制序列化方案;注意 Java 反序列化的安全问题。
- 在生产环境:考虑字段版本管理、兼容策略、字段升级、以及对敏感数据的加密/脱敏。
- 若你必须从
toString()
恢复,只有在toString()
明确按可逆格式定义并提供配套解析器时才可行 —— 否则放弃该方案。
终章
这里也有小伙伴喜欢使用fastjson或hutool的JSONUtil来做一些转换处理,这些都是可以的。