Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java避坑指南:POJO类属性建议一律为包装类型,而且不要设置任何属性默认值

Java避坑指南:POJO类属性建议一律为包装类型,而且不要设置任何属性默认值

作者头像
崔认知
发布于 2023-06-20 03:08:06
发布于 2023-06-20 03:08:06
71600
代码可运行
举报
文章被收录于专栏:nobodynobody
运行总次数:0
代码可运行

【建议】POJO类属性建议一律为包装类型,而且不要设置任何属性默认值


如果在开发中,我们对POJO类属性设置了默认值(包装类型显示设置默认值,基本类型编译期推导默认值),难免会遇到一些"坑"。

坑:反序列化可能导致默认值可以被null覆盖


以jackson为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.renzhikeji.demo;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.builder.ToStringBuilder;


/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args) throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();
        String json = "{\"id\":null,\"name\":null,\"address\":\"\"}";
        POJO value = mapper.readValue(json, POJO.class);
        System.out.println(value);
    }

    public static class POJO {
        private Long id = 0L;
        private String name;
        private String address = "";

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        @Override
        public String toString() {
            return new ToStringBuilder(this)
                    .append("id", id)
                    .append("name", name)
                    .append("address", address)
                    .toString();
        }
    }
}

POJO定义中,id默认值为0,当我们的反序列化字符串为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{"id":null,"name":null,"address":""}

json字符串中id设置为null的时候:结果反序列化后,id的默认值就没有了。

坑:POJO与DTO之间转换,一个为原生类型,一个是对应的包装类型,使用类似BeanUtils.copyProperties的工具复制对象可能会抛出异常


以org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object)为例(spring-beans-5.3.21):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.renzhikeji.demo;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.beans.BeanUtils;


/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class Demo {
    public static void main(String[] args) throws JsonProcessingException {
        POJO pojo = new POJO();
        pojo.setId(null);

        DTO dto = new DTO();
        BeanUtils.copyProperties(pojo, dto);
        System.out.println(dto);
    }

    public static class POJO {
        private Long id = 0L;
        private String name;
        private String address = "";

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        @Override
        public String toString() {
            return new ToStringBuilder(this).append("id", id).append("name", name).append("address", address).toString();
        }
    }


    public static class DTO {
        private long id = 0L;
        private String name;
        private String address = "";

        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        @Override
        public String toString() {
            return new ToStringBuilder(this).append("id", id).append("name", name).append("address", address).toString();
        }
    }
}

主要区别是:同一个属性名,但是一个是原生类型,一个是对应的包装类型,原生类型的本来意愿是不传值,就有个默认值,可惜,事与愿违:

坑:MyBatis的动态sql中,可能遇到


MyBatis动态sql中,如果遇到如下所示类似的动态sql,title或author没有显示设置,POJO中都设置了默认值,动态sql就失去了意义,业务逻辑就错了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state =ACTIVE<if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

坑:API接口或RPC接口返回默认值,造成业务逻辑bug


拿部门同事的计费系统的一个扣费异常为例,扣费时需要通过 RPC 请求计费系统得到一个费率值,预期该接口的返回值中会包含一个浮点型的费率字段。

当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。

如果RPC返回该费率值时可能由于bug或其他业务原因没有设置,拿到默认值0.0就进行计算,不会进行扣费,这种扣费为0的异常情况无法被感知(当然你可以把这种业务情况发报警),但是如果不用原生类型或不设置包装类型默认值,以null参与计算直接抛出异常报警,是不是更容易发现。

小结


POJO类属性建议一律为包装类型,而且不要设置任何属性默认值,以上的坑只是冰山一角。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
为什么阿里巴巴开发手册中强制要求 POJO 类使用包装类型?NPE问题防范
背景:写这个的原因,也是我这两天凑巧看到的,虽然我一直有 alibaba Java 开发手册,也看过不少次,但是一直没有注意过这个问题😂 属于那种看过,但又没完全看过👰 一起来看看吧冲咯🏂 hxdm,我写不出小故事😭,但是可不可以看在我写了不少,还算实用的份上,给个赞👍啊。 在这里请xdm 喝 🥤啦 一、前言🚕 今天在写一个AdvertVO类时,我当时用 id 是直接给了个 long,没有使用用包装类型,然后 idea 里面的 Alibaba Java Coding Guidelines 插件就
宁在春
2022/10/31
6190
为什么阿里巴巴开发手册中强制要求 POJO 类使用包装类型?NPE问题防范
面试官:兄弟,说说基本类型和包装类型的区别吧
POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有属性字段以及 setter 和 getter 方法,示例如下。
Remember_Ray
2020/01/21
3.6K1
面试官:兄弟,说说基本类型和包装类型的区别吧
六年前,我从苏州回到洛阳,抱着一幅“海归”的心态,投了不少简历,也“约谈”了不少面试官,但仅有两三个令我感到满意。其中有一位叫老马,至今还活在我的手机通讯录里。他当时扔了一个面试题把我砸懵了:说说基本类型和包装类型的区别吧。
沉默王二
2019/09/29
5800
面试官:兄弟,说说基本类型和包装类型的区别吧
JAVA开发规范常用的技巧总结(共12个)
例如:UserService,但是以下情景例外:DO / BO / PO / DTO / VO。
牛油果
2019/07/09
6000
JAVA开发规范常用的技巧总结(共12个)
使用CGlib实现Bean拷贝(BeanCopier)「建议收藏」
在做业务的时候,我们有时为了隔离变化,会将DAO查询出来的Entity,和对外提供的DTO隔离开来。大概90%的时候,它们的结构都是类似的,但是我们很不喜欢写很多冗长的b.setF1(a.getF1())这样的代码,于是我们需要BeanCopier来帮助我们。BeanCopier其实已经有很多开源版本,例如DozerMapper、Apache BeanUtils、Spring、Jodd BeanUtils甚至是Cglib都提供了这样的功能。下面介绍Cglib的BeanCopier的使用。
全栈程序员站长
2022/10/04
1.6K0
你真的理解Java中的包装类吗?还有不少人踩坑了
   基础数据类型不是在计算机中运算更快吗?为啥还要有包装类?    因为想要对基本类型数据进行更多的操作,最方便的方式就是将其封装成对象。为啥呢?因为在对象描述中就可以定义更多的属性和行为对该基本数据类型进行操作。基本数据类型的包装类是为了解决基本数据类型有些操作不方便带来的问题。
程序员云帆哥
2022/05/12
5200
Java - Jackson JSON Java Parser API
Jackson JSON Java Parser非常流行,并且也用于Spring框架。
小小工匠
2021/08/17
9360
28个Java开发常用规范技巧总结
例如:UserService,但是以下情景例外:DO / BO / PO / DTO / VO。
用户1516716
2019/07/23
5840
Java基本类型和包装类型
POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有属性字段以及 setter 和 getter 方法,示例如下。
崔笑颜
2020/06/08
8700
Java中的基本数据类型和包装类型的这些知识,你都知道吗?
Java 中的基本数据按类型可以分为四大类:布尔型、整数型、浮点型、字符型; 这四大类包含 8 种基本数据类型。
code随笔
2020/07/06
5350
Java中的基本数据类型和包装类型的这些知识,你都知道吗?
开发注意事项
4.要从全局去思考,比如这次对于成功率的定义,就是成功的/总量,拆的太散显然是不合理的。稍微有变动就会导致成功率不准确。
用户9704972
2022/04/27
9040
MapStruct从入门到精通:Java对象映射的终极指南
在Java开发中,对象之间的转换(如DTO转Entity、VO转BO)是常见但繁琐的任务。传统的getter/setter方式不仅繁琐且易错,而反射工具BeanUtils.copyProperties()则存在性能问题。MapStruct作为基于注解的代码生成器,提供了编译时类型安全的优雅解决方案。本文将深入解析MapStruct的核心功能与最佳实践。
Java微观世界
2025/04/22
1700
MapStruct从入门到精通:Java对象映射的终极指南
巨坑系列:Java Bean 转 Map 的那些坑
将 Java Bean 转 Map 最常见的手段就是使用 JSON 框架,如 fastjson 、 gson、jackson 等。 但使用 JSON 将 Java Bean 转 Map 会导致部分数据类型丢失。 如使用 fastjson ,当属性为 Long 类型但数字小于 Integer 最大值时,反序列成 Map 之后,将变为 Integer 类型。
明明如月学长
2022/07/12
1.3K0
巨坑系列:Java Bean 转 Map 的那些坑
为什么阿里代码规约要求避免使用 Apache BeanUtils 进行属性复制
有一次开发过程中,刚好看到小伙伴在调用 set 方法,将数据库中查询出来的 Po 对象的属性拷贝到 Vo 对象中,类似这样:
JAVA葵花宝典
2019/06/13
4.5K0
什么是POJO类?
简单的Java对象(Plain Old Java Objects)实际就是普通JavaBeans,使用POJO名称是为了避免和EJB混淆起来, 而且简称比较直接. 其中有一些属性及其getter setter方法的类,有时可以作为value object或dto(Data Transform Object)来使用.当然,如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection之类的方法。 POJO是Plain Old Java Objects的缩写不错,但是它通指没有使用Entity Beans的普通java对象,并且作者在这里提到的pojo是用来实现业务逻辑(business logic )的。
全栈程序员站长
2022/09/02
1.2K0
Java避坑记:比较的数字是不同的包装类型,千万注意使用equals方法导致的问题
Java中,比较两个不同类型的包装类型时候,即使数值相等,使用equals方法也会返回false
崔认知
2023/06/19
4590
Java避坑记:比较的数字是不同的包装类型,千万注意使用equals方法导致的问题
Java 比较器 和 包装类
Java Comparable接口强行对实现它的每个类的对象进行整体排序 这种排序被称为:自然排序
Java_慈祥
2024/08/06
1890
Java 比较器 和 包装类
Java序列化对字段名的影响
前段时间遇到一个问题,序列化之后原本类中的属性名发生了变化,原本isDel序列化之后得到的是del,为此查了一下相关资料,发现和序列化机制有关
二十三年蝉
2021/03/02
1.1K0
Java序列化对字段名的影响
MapStruct 拷贝类属性
看了上面的方法,你可能觉得不是很简单么。但如果属性非常多,比如有20多个。用上面的方法就会不美观,满屏的getter/setter方法,看着都眼花。
小石头
2022/11/10
1.1K0
MapStruct 拷贝类属性
深入浅出 Java 中的包装类
前阵子,我们分享了《Java中的基本数据类型转换》这篇文章,对许多粉丝还是有带来帮助的,今天讲一下 Java 包装类的的由来,及自动装箱、拆箱的概念和原理。
Java技术栈
2019/07/12
7720
深入浅出 Java 中的包装类
推荐阅读
相关推荐
为什么阿里巴巴开发手册中强制要求 POJO 类使用包装类型?NPE问题防范
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验