前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java序列化对字段名的影响

Java序列化对字段名的影响

作者头像
二十三年蝉
发布2021-03-02 15:15:11
1K0
发布2021-03-02 15:15:11
举报
文章被收录于专栏:闻道于事闻道于事

前段时间遇到一个问题,序列化之后原本类中的属性名发生了变化,原本isDel序列化之后得到的是del,为此查了一下相关资料,发现和序列化机制有关

在阿里巴巴Java开发手册中关于这一点,有过一个『强制性』规定:

为此我们要看一下POJO中布尔类型变量不同的命名

代码语言:javascript
复制
class Model1  {
    private Boolean isSuccess;
    public void setSuccess(Boolean success) {
        isSuccess = success;
    }
    public Boolean getSuccess() {
        return isSuccess;
    }
 }

class Model2 {
    private Boolean success;
    public Boolean getSuccess() {
        return success;
    }
    public void setSuccess(Boolean success) {
        this.success = success;
    }
}

class Model3 {
    private boolean isSuccess;
    public boolean isSuccess() {
        return isSuccess;
    }
    public void setSuccess(boolean success) {
        isSuccess = success;
    }
}

class Model4 {
    private boolean success;
    public boolean isSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
        this.success = success;
    }
}

以上代码的setter/getter是使用Intellij IDEA自动生成的,仔细观察以上代码,你会发现以下规律:

  • 基本类型自动生成的getter和setter方法,名称都是isXXX()setXXX()形式的。
  • 包装类型自动生成的getter和setter方法,名称都是getXXX()setXXX()形式的。

我们可以发现,虽然Model3和Model4中的成员变量的名称不同,一个是success,另外一个是isSuccess,但是他们自动生成的getter和setter方法名称都是isSuccesssetSuccess

关于Java Bean中的getter/setter方法的定义其实是有明确的规定的,根据JavaBeans(TM) Specification规定,如果是普通的参数propertyName,要以以下方式定义其setter/getter:

https://download.oracle.com/otndocs/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/

代码语言:javascript
复制
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);

public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);

可以看boolean类型的变量方法是单独定义的,使用is方式

通过对照这份JavaBeans规范,我们发现,在Model4中,变量名为isSuccess,如果严格按照规范定义的话,他的getter方法应该叫isIsSuccess。但是很多IDE都会默认生成为isSuccess。

在序列化中,这样就会受到影响

代码语言:javascript
复制
@Data
class Model3 implements Serializable {

    private static final long serialVersionUID = 1836697963736227954L;

    private Integer age;
    
    private String name;
    
    private boolean isSuccess;

    private boolean isDel;

    public String getUser(){
        return "Hello JWZ";
    }
}

我们定义一个类,使用lombok,然后看序列化进行测试

代码语言:javascript
复制
     //定一个Model3类型
        Model3 model3 = new Model3();
        model3.setSuccess(true);

        //使用fastjson(1.2.46)序列化model3成字符串并输出
        System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model3));

        //使用Gson(2.8.5)序列化model3成字符串并输出
        Gson gson =new Gson();
        System.out.println("Serializable Result With Gson :" +gson.toJson(model3));

        //使用jackson(2.9.6)序列化model3成字符串并输出
        ObjectMapper om = new ObjectMapper();
        System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model3));

得到如下结果:

可以看到三种序列化的方式,

fastjson输出有值的数据,包含user,带is的字段被序列化不带is

Gson输出有值的数据,不包含user,带is的字段被序列化正常

Jackson输出所有有值和null的数据,包含user,带is的字段被序列化不带is

由此可以得出结论:

fastjson和Jackson是通过反射遍历getter方法,然后根据JavaBeans规则他会去掉is来获取属性值。

Gson是通过直接反射遍历类中所有属性。

现在我们试一下,对于同一个对象,如果用fastjson序列化,然后在使用Gson反序列化:

代码语言:javascript
复制
    public static void main(String[] args) {

        Model3 model3 = new Model3();
        model3.setSuccess(true);

        String fastJSONString=JSON.toJSONString(model3);

        System.out.println(fastJSONString);
        Gson gson =new Gson();
        System.out.println(gson.fromJson(fastJSONString,Model3.class));

    }

isSuccess竟然变为false

因为JSON框架通过扫描所有的getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,把model对象序列化城字符串后内容为{"success":true}

根据{"success":true}这个json串,Gson框架在通过解析后,通过反射寻找Model类中的success属性,但是Model类中只有isSuccess属性,所以,最终反序列化后的Model类的对象中,isSuccess则会使用默认值false。

因此,应尽量使用success式的命名来从源头避免这个问题。

延伸,布尔类型定义应使用Boolean还是boolean

布尔类型应该使用包装类型还是基本数据类型呢?

代码语言:javascript
复制
public class Test {

    public static void main(String[] args) {

        Model3 model3 = new Model3();
        System.out.println("model3 : " + model3);

    }

}

@Data
class Model3 implements Serializable {

    boolean success;

    Boolean del;
}

包装类型的默认值是null,基本类型的默认值输出了false,这在某些情况就会造成问题,建议在POJO和RPC的返回值中使用包装类型

所以在定义布尔类型变量时,应使用:

代码语言:javascript
复制
Boolean success;

参考:

《Java工程师成神之路》

《阿里巴巴Java开发手册》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-02-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 延伸,布尔类型定义应使用Boolean还是boolean
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档