场景
Java开发手册中关于POJO的布尔类型的变量名的要求是:
【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
说明:在本文 MySQL 规约中的建表约定第一条,表达是与否的变量采用 is_xxx 的命名方式,所以,需要
在<resultMap>设置从 is_xxx 到 xxx 的映射关系。
反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析的时候,
“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
下面来验证下上面的结论。
在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个 RPC 接口的时候,
我们一般会定义一个字段表示本次请求是否成功的。
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
我们先新建以下四个POJO
Model1 :
public class Model1 {
private Boolean isSuccess;
public Boolean getSuccess() {
return isSuccess;
}
public void setSuccess(Boolean success) {
isSuccess = success;
}
}
Model2 :
public class Model2 {
private Boolean success;
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
}
Model3:
public class Model3 {
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean success) {
isSuccess = success;
}
}
Model4 :
public class Model4 {
private boolean success;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
以上四个POJO都是通过IDEA自动生成的get和set方法,可以看到:
基本类型自动生成的 getter 和 setter 方法,名称都是isXXX()和setXXX()形式的。
包装类型自动生成的getter和setter方法,名称都是getXXX()和setXXX()形式的
JavaBean 中关于 setter/getter 的规范
关于 Java Bean 中的 getter/setter 方法的定义其实是有明确的规定的,根据JavaBeans(TM) Specification规定:
JavaBeans(TM) Specification 1.01 Final Release
如果是普通的参数 propertyName,要以以下方式定义其 setter/getter:
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);
但是,布尔类型的变量 propertyName 则是单独定义的:
public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);
通过对照这份 JavaBeans 规范,我们发现,在 Model4 中,变量名为 isSuccess,如果严格按照规范定义的话,
其getter 方法应该叫 isIsSuccess。但是很多IDE 都会默认生成为 isSuccess。
Java中fastJson、jackson、Gson对于POJO的布尔变量isSuccess序列化的影响
拿常用的序列化进行举例,如果需要引入依赖,根据自己项目情况自行引入
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.0</version>
</dependency>
引入依赖之后,示例代码如下:
Model3 model3 = new Model3();
model3.setSuccess(true);
System.out.println("Serialiazable Result with fashjson:"+com.alibaba.fastjson.JSON.toJSONString(model3));
com.google.gson.Gson gson = new Gson();
System.out.println("Serialiazable Result with Gson:"+gson.toJson(model3));
com.fasterxml.jackson.databind.ObjectMapper om = new ObjectMapper();
System.out.println("Serialiazable Result with jackson:"+om.writeValueAsString(model3));
以上代码的输出结果为
//Serialiazable Result with fashjson:{"success":true}
//Serialiazable Result with Gson:{"isSuccess":true}
//Serialiazable Result with jackson:{"success":true}
可以看到
在 fastjson 和 jackson 的结果中,原来类中的 isSuccess 字段被序列化成success。
而 Gson 中是 isSuccess 字段。
fastjson 和 jackson 在把对象序列化成 json 字符串的时候,是通过反射遍历出该类中的所有 getter 方法,
得到isSuccess,然后根据 JavaBeans 规则,他会认为这是属性success的值。直接序列化成 json:{"success":true}
但是 Gson 并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成 json:{“isSuccess”:true}
可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,
所以,对于同一个类的同一个对象的序列化结果可能是不同的。
如果对于同一个对象,使用fastjson进行序列化,再使用Gson反序列化会发生什么?
测试代码如下:
System.out.println(gson.fromJson(com.alibaba.fastjson.JSON.toJSONString(model3),Model3.class).isSuccess());
以上输出结果为false
原因是因为 JSON 框架通过扫描所有的 getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,
把 model 对象序列化城字符串后内容为{"success":true}。根据{"success":true}这个 json 串,
Gson 框架在通过解析后,通过反射寻找 Model 类中的 success 属性,
但是 Model 类中只有 isSuccess 属性,所以,最终反序列化后的 Model 类的对象中,isSuccess 则会使用默认值 false。
所以,在定义 POJO 中的布尔类型的变量时,不要使用 isSuccess 这种形式,而要直接使用 success !
Java的POJO中布尔变量使用基本数据类型boolean还是包装数据类型Boolean
新建一个POJO
import java.util.StringJoiner;
public class Model {
private Boolean success;
private boolean failure;
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public boolean isFailure() {
return failure;
}
public void setFailure(boolean failure) {
this.failure = failure;
}
@Override
public String toString() {
return new StringJoiner(",",Model.class.getSimpleName()+"[","]")
.add("success = "+success)
.add("failure= "+failure)
.toString();
}
}
并使用Java 8的StringJoiner重写toString方法。
然后输出该对象的toString
Model model = new Model();
System.out.println("default model: "+model);
以上代码输出结果
default model: Model[success = null,failure= false]
可以看到,当我们没有设置 Model 对象的字段的值的时候,Boolean 类型的变量会设置默认值为null,
而 boolean 类型的变量会设置默认值为false。即对象的默认值是null,boolean 基本数据类型的默认值是false。
在 Java 开发手册中,对于 POJO 中如何选择变量的类型也有着一些规定
关于基本数据类型与包装数据类型的使用标准如下:
1) 【强制】所有的 POJO 类属性必须使用包装数据类型。
2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
3) 【推荐】所有的局部变量使用基本数据类型。
说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。
正例:
数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:
某业务的交易报表上显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调
用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线-。所以包装数据类型
的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。