zoukankan      html  css  js  c++  java
  • Java序列化对字段名的影响

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

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

     

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

    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/

    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。

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

    @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,然后看序列化进行测试

         //定一个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反序列化:

        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

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

    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的返回值中使用包装类型

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

    Boolean success;

    参考:

    《Java工程师成神之路》

    《阿里巴巴Java开发手册》

  • 相关阅读:
    高精度加法
    高精度计算(一)
    算法总结
    崛起之路
    2015浙江高考满分作文汇总(9篇)
    努力
    NOIP2015总结
    P3197 [HNOI2008]越狱[组合数学]
    【原创】SPFA判负环
    P1351 联合权值[鬼畜解法]
  • 原文地址:https://www.cnblogs.com/jiangwz/p/14434957.html
Copyright © 2011-2022 走看看