POJO 是 Plain Old Java Object 的缩写,是一种简单的 Java 对象,通常用于表示数据。它的结构简单,不依赖于特定的框架。一个基本的 POJO 示例如下所示:
public class User {
private String name;
private int age;
// name 的 getter 方法
public String getName() {
return name;
}
// name 的 setter 方法
public void setName(String name) {
this.name = name;
}
// age 的 getter 方法
public int getAge() {
return age;
}
// age 的 setter 方法
public void setAge(int age) {
this.age = age;
}
}
现在很多项目流行使用 Lombok 函数库简化 POJO 的编写。以下是使用 Lombok 简化后的 User 类型,使用 @Data 来自动产生 getter、setter 等方法:
@Data
public class User {
private String name;
private int age;
}
在某些场景下,开发者可能会在 POJO 中添加自定义的 get 方法以提供额外的功能。例如下列代码自定义了一个 getNextSubId
方法来方便取得 下一个 subId。
@Data
static class Order {
AtomicInteger subId = new AtomicInteger(0);
//自定义特殊的 get 方法,方便取得下一个 subId
public int getNextSubId(){
return subId.incrementAndGet();
}
}
注意!这个方法并不符合 POJO 中 get 方法的惯例 (Convention),因为调用 getNextSubId
方法是有副作用 (side effect) 的,它改变了 subId
的值。
POJO 用来表示数据,所以常被做序列化处理。例如:序列化为 JSON 字符串。然而,某些 JSON 序列化函数库,在序列化成 JSON 字符串时,会默认调用 POJO 中所有 get 开头命名的方法,导致自定义的特殊的 get 方法被调用。
例如:fastjson 1.2.83 版本,在序列化时就会有这个行为,示例代码使用 JSON.toJSONString
来序列化刚才定义的 Order POJO,如下所示:
Order order = new Order();
String json1 = JSON.toJSONString(order);
System.out.println(json1);
// 输出:{"nextSubId":1,"subId":{"value":1}}
String json2 = JSON.toJSONString(order);
System.out.println(json2);
// 输出:{"nextSubId":2,"subId":{"value":2}}
上述代码中可以看到每调用一次 JSON.toJSONString
, subId
就会“被递增”,因为在序列化时 getNextSubId
被调用了。代码中序列化做了两次, subId
也从 0 递增到了 2。
这样的 bug 可能被隐藏得很深,例如:在某些条件下才会做序列化(见以下代码):
if (...) {
....
if (...) {
log.info("order={}", JSON.toJSONString(order));
}
} else {
....
}
Bug 显示的表象就会是 subId
会神奇地在某些情况下自动递增。
而 Bug 的起因仅仅是一段 log 代码。
看似简单的 POJO 也可能隐藏着潜在的问题。魔鬼往往藏在细节之中,而这些细节会在最意想不到的时候引爆问题。
要避免这类陷阱,还需要回归到软件开发的基本功:
看似老生常谈,但在软件开发的道路上, 这些看似基础的原则往往是确保系统稳定性的关键。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。