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

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

首发地址:https://mp.weixin.qq.com/s?__biz=Mzg4MzcwMTk0Mw==&mid=2247484226&idx=1&sn=3f31526d257537fb9e3db84cc9bb0695&chksm=cf42229af835ab8c7dbb1b4f980e65bee4f49623eb5fdbf01b6e91782e3dcf0bfb5bfdc4d604&token=216307076&lang=zh_CN#rd

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

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

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

以jackson为例:

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,当我们的反序列化字符串为:
{"id":null,"name":null,"address":""}

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

https://mmbiz.qpic.cn/mmbiz_png/K5AWcTRtu4Whs4g7BeelajfH0buUGxWhhq7GTf71YHla0s1MswFRt6LIeXExEHfS2TM0j1xm2zV3icc6KVcMqPw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1image.png

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

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


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();
        }
    }
}

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

https://mmbiz.qpic.cn/mmbiz_png/K5AWcTRtu4Whs4g7BeelajfH0buUGxWhaibWbszmDtbdB3giaibwWWodcOZ3fXMzicnZugAxHcketa4kqA98RLHgicg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1image.png

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

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

 resultType="Blog">

SELECT * FROM BLOG WHERE state = ‘ACTIVE’

AND title like #{title}


AND author_name like #{author.name}


**坑:API接口或RPC接口返回默认值,造成业务逻辑bug
**
拿部门同事的计费系统的一个扣费异常为例,扣费时需要通过 RPC 请求计费系统得到一个费率值,预期该接口的返回值中会包含一个浮点型的费率字段。
当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。
如果RPC返回该费率值时可能由于bug或其他业务原因没有设置,拿到默认值0.0就进行计算,不会进行扣费,这种扣费为0的异常情况无法被感知(当然你可以把这种业务情况发报警),但是如果不用原生类型或不设置包装类型默认值,以null参与计算直接抛出异常报警,是不是更容易发现。

小结

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

相关文章
|
19天前
|
JSON 前端开发 JavaScript
Java属性为什么不能是is开头的boolean
在Java实体类中,阿里规约要求boolean属性不应以is开头。文章通过实际案例分析了isUpdate字段在JSON序列化过程中变为update的问题,并提供了自定义get方法或使用@JSONField注解两种解决方案,建议遵循规约避免此类问题。
Java属性为什么不能是is开头的boolean
|
29天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
45 8
|
29天前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
64 1
|
1月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
64 17
|
1月前
|
存储 Java 开发者
Java 中 Set 类型的使用方法
【10月更文挑战第30天】Java中的`Set`类型提供了丰富的操作方法来处理不重复的元素集合,开发者可以根据具体的需求选择合适的`Set`实现类,并灵活运用各种方法来实现对集合的操作和处理。
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
121 4
|
1月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
68 2
|
1月前
|
Java Android开发
Eclipse 创建 Java 类
Eclipse 创建 Java 类
26 0
|
9天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
38 6