阿里为何禁止在对象中使用基本数据类型

【MySQL】MySQL数据类型选择 与 阿里巴巴MySQL规范解读概要 (1)小数类型为 decimal,禁止使用 float 和 double (2)如果存储的字符串长度几乎相等,使用 char 定长字符串类型 (3)单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表 (4)业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引 (5)超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时, 保证被关联的字段需要有索引。 (6)页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 阅读详情

前两天,因为一个接口的参数问题,和一位前端工程师产生了一些分歧,需求很简单:

根据一个数值类型(type 取值范围1,2,3)来查询数据,如果没这个值,就是查询所有的数据;

这个需求很常见吧!但是在"没这个值"的问题上,想法不太一样:

  • 接口定义的规范是,查询所有时,那就不传这个type,我后端拿到的就是null,在MyBatis的配置里面,通过if标签,对type判空,来决定是否带type这个条件:
<select id="query" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  select 
  <include refid="Base_Column_List" />
  from order_info where 1 = 1
  <if test="type != null">
    AND TYPE = #{type,jdbcType=INTEGER}
  </if>
</select>
  • 前端工程师的意思是,没有值的话,那我就给你传默认值了,数值类型的默认值是:0,后端就需要根据type是否是0来查询所有;如果这么做,MyBatis中 type 就需要加上大于0的判断
<if test="type != null and type > 0">
    AND TYPE = #{type,jdbcType=INTEGER}
</if>

虽然按着前端的想法,也确实可以实现,但是这个思路,似乎并没有很强的说服力;因为 0 本身就是一个具体的值,并不符合 type 的取值范围,在 Controler 层的参数校验,就应该被干掉;如果某一天因为需求调整,将 0 也表示为某个具体类型之后,这里代码就需要做调整,同时查询所有和查询这个新增 0 的类型就会混淆,前端的展示也会受到影响;

0 和 null 对象在本质上还是有很大区别的;

在各执一词的背景下,我在技术交流群里面和各位大佬简单交流了一下,不想因为的我的执着影响到其他人;

大部分大佬的做法和我想的是一致的!最终也让前端按我的要求做了对应的调整;

那这个问题的根源,还是出在数值类型的默认值上;加上群里面几天讨论了几次相关的一些问题,这里就汇总说一下;

在阿里巴巴Java开发手册中有这样的一条规范,跟我们今天说的问题,也有一些关联:

  • 【强制】所有的 POJO 类属性必须使用包装数据类型。
  • 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
  • 【推荐】所有的局部变量使用基本数据类型。
最新阿里巴巴Java开发手册(黄山版),于2022年2月3日发布,有需要的朋友可以给我发(开发)两个字

下面就通过详细的示例,来说明一下为什么阿里的开发手册会有这样的约束;

1、Java 基本类型与包装类的关系

首先,我们需要了解清楚Java的基本数据类型对应的包装类,以及基本类型的默认值;

包装类在不实例化的前提下,默认值都是null

2、POJO 类、RPC方法、返回值 强制使用包装类

在POJO 类、RPC方法返回值和参数需要强制使用包装类,如果使用基本数据类型,实例化出来的对象,就会初始化默认值;如果不赋值,对于使用者来说,根本就不知道这个值是因为默认值产生的,还是创建者设置的,从而带来后续的一些列问题;

举个例子

  • 用户对象
@Data
public class User {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 0:女 1:男
     */
    private byte gender;
}

age 使用包装类,gender 性别用的基本数据类型(0表示女,1表示男)

  • 接口
/**
 * 添加用户
 *
 * @param user
 * @return
 */
@PostMapping("/add")
private User add(@RequestBody User user) {
    log.info("添加的用户:{}", user);
    return user;
}
  • 测试

分别按以下的两种方式传参

当所有的请求参数都必传的话(右侧传参示例),不管属性是包装类、还是基本数据类型都没啥问题;

如果是左边的传参方式,只有名字必传,其他都没值,性别使用的是byte基础数据类型,User 对象创建之后,就会赋上默认值:0;0 表示女,这时候就可能让一个帅小伙儿无缘无故变成了女孩子;

RPC 方法及返回值的参数强制使用包装类的原因和上面是差不多的

3、ORM 关系映射对象必须使用包装类

数据库查询的映射对象,如果使用基础数据类型,就可能出现 NPE(空指针)异常、对象构建异常、数据错误的问题;

继续看示例:

  • 映射对象

数据库表:

映射对象:

@TableName(value = "user_info")
@Data
@AllArgsConstructor
public class UserInfo implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 用户名
     */
    @TableField(value = "user_name")
    private String userName;
    /**
     * 年龄
     */
    @TableField(value = "age")
    private int age;
    /**
     * 来源
     */
    @TableField(value = "source")
    private Byte source;
}

查询方法

@Test
void getById() {
    UserInfo userInfo = userInfoService.getById(1);
    log.info("根据ID查询用户信息:{}", userInfo);
}

问题一:对象构建异常

当映射对象包含 @AllArgsConstructor(Lombok的注解) 时,会自动生成带所有属性的构造方法:

public UserInfo(final Integer id, final String userName, final int age, final Byte source) {
    this.id = id;
    this.userName = userName;
    this.age = age;
    this.source = source;
}

查询 id 为 1 的数据时,由于 age 为 null,当通过以上构造方法实例化对象时,将一个 null 对象赋给一个基础数据类型,就会出现IllegalArgumentException异常:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.ehang.mysql.mybatis.plus.generator.user.demain.UserInfo with invalid types (Integer,String,int,Byte) or values (1,一行Java 1,null,1). Cause: java.lang.IllegalArgumentException

问题二:数据错误

当对象中,没有 @AllArgsConstructor 注解,只带有 @Data 注解时,会生成所有属性的 Getter、Setter 方法;查询的结果会通过各个属性 Setter 方法基础赋值;

==>  Preparing: SELECT id,user_name,age,source FROM user_info WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, user_name, age, source
<==        Row: 1, 一行Java 1, null, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1152bcd]
2022-10-30 12:57:09.308  INFO 504100 --- [           main] com.ehang.mysql.mybatis.plus.GetTest     : 根据ID查询用户信息:UserInfo [Hash = 1795612732, id=1, userName=一行Java 1, age=0, source=1, serialVersionUID=1]

以上日志可以看出,id 为 1 的 age 数据库中查出来的是 null,不会调用对象的 Setter 方法赋值,可由于对象中的 age 是 int 基础数据类型,在对象创建之后,就赋予了初始值 0,最终造成使用者拿到的 UserInfo 对象和数据库中的结果不一致的问题,这是绝对不允许的。

  • 解决办法

把基本数据类型换成包装类就好了;

4、局部变量推荐使用基础数据类型

【推荐】所有的局部变量使用基本数据类型。

那既然基本数据类型总是有问题,我们全部用包装类不就好了;但这里为什么局部变量又推荐使用基本数据类型呢?因为一旦涉及到运算,基础数据类型可以省去拆箱、装箱的动作,提高运行效率;

  • 什么是装箱和拆箱?
    • 装箱

就是自动将基本数据类型转换为包装器类型;原理是调用包装类的valueOf方法,如:Integer.valueOf(1)、Boolean.valueOf(true)...

    • 拆箱

就是自动将包装器类型转换为基本数据类型;原理是是调用包装类的xxxValue方法(xxx表示类型),如:

public boolean booleanValue() {
    return value;
}

还是以上周,一位小伙伴儿在群里面问的关于拆、装箱的效率问题为例:

由于没有他的源码和需求,这里我写了一个新的测试用例,来论证这个问题;

示例代码

public class Main {
    public static void main(String[] args) {
        int[] nums = new int[]{1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010};
        long start = System.currentTimeMillis();
        for (int n = 0; n < 10000000; n++) {
            for (int i = 0; i < nums.length; i++) {
                nums[i] = nums[i] + 1;
                nums[i] = nums[i] + 2;
                nums[i] = nums[i] + 3;
            }
        }
        System.out.printf("int 遍历10000000的耗时:%sms\n",System.currentTimeMillis()-start);

        start = System.currentTimeMillis();
        Integer[] nums2 = new Integer[]{1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010};
        for (int n = 0; n < 10000000; n++) {
            for (int i = 0; i < nums2.length; i++) {
                nums2[i] = nums2[i] + 1;
                nums2[i] = nums2[i] + 2;
                nums2[i] = nums2[i] + 3;
            }
        }
        System.out.printf("Integer 遍历10000000的耗时:%sms\n",System.currentTimeMillis()-start);
    }
}

代码逻辑很简单,分别有一个 int 和 Integer 数组,里面的初始值一样,遍历10000000,每次将数组中的每个值都分别+1、+2、+3再放回到数组中去;

测试结果

int 遍历10000000的耗时:194ms
Integer 遍历10000000的耗时:1965ms

根据耗时,会发现,int 数组的效率差不多是 Integer 数组的10倍

  • 原因分析

明明初始值一样,计算出来的结果也一样,为什么效率会差了10倍之多?

主要的原因是出在以下的三行代码中:

nums[i] = nums[i] + 1;
nums[i] = nums[i] + 2;
nums[i] = nums[i] + 3;

如果是 int 数组,所有的值都是保存在栈中;不会有任何拆、装箱的动作;取值、计算、再赋值的过程也就是一气呵成,非常丝滑;

但是如果是 Integer 数组,nums[i] = nums[i] + 1;这行代码,计算过程如下:

1. 在 nums[i] 中取出 Integer;
2. 将取出的值拆箱;`intValue`方法;
3. 拆箱后的 int 与 1 做相加运算,得到计算结果;
4. 将计算结果的 int 装箱生成 Integer 对象(主要耗时的地方);
5. 将结果放到 nums[i] 中。


由于做了3次运算,意味着每次循环都会将上面步骤重复3次;并经历了3次拆箱、3次装箱动作;那这个过程必定会带来性能上的消耗;

因此在局部变量中,合理的使用基本类型,可以有效的提高效率;

好了,看到这里,阿里的这条约束应该就能彻底理解了

基于Android Studio的校园智能点餐系统开发实战(附完整源码与部署指南) 本文详细介绍了基于Android Studio开发校园智能点餐系统的完整流程,包括需求分析、环境搭建、数据库设计、客户端与后台功能实现。系统采用SSM框架和MySQL数据库,解决了校园食堂高峰期排队问题,提供高效的点餐体验。附完整源码与部署指南,助力开发者快速构建类似应用。 阅读详情

相关推荐

TC3xx芯片SPI多从机通信避坑指南:AUTOSAR框架下的DMA配置与片选切换

本文针对英飞凌TC3xx芯片在AUTOSAR框架下实现SPI多从机通信的实战难题,提供了详尽的避坑指南。重点解析了如何利用硬件支持的16路片选(SLSO)灵活切换,在AUTOSAR中正确配置不同时序的从设备,并深入探讨了异步模式下DMA的启用条件与关键配置细节,帮助开发者规避通信失败风险,提升系统可靠性。

nn3456的博客 154

阿里建议所有 POJO 类属性使用包装类,但这些坑你有注意到吗?

包装类在Java 5中和泛型一起引入,引入包装类的原因有两点:解决无法创建基本类型泛型集合的问题加入对基本类型为null这个语义的支持并提供boxing和unboxing的语法糖,让编译器支持基本类型和包装类的自动转化,减少开发者的工作量。但是经常有同学因为误用包装类导致惨烈的线上问题,在使用包装类的时候务必需要注意以下四点:与基础类截然不同的==和equals语义糟糕的...

Java知音 816

基于STM32与ESP8266的MQTT协议实现阿里云物联网平台远程LED控制

本文详细介绍了如何利用STM32单片机与ESP8266 Wi-Fi模块,通过MQTT协议连接阿里云物联网平台,实现远程控制LED灯的完整物联网项目。内容涵盖硬件连接、阿里云平台配置、MQTT协议与AT指令解析、STM32代码实战以及数据解析与调试技巧,为嵌入式开发者提供了一个清晰、实用的物联网入门实践指南。

svm4gardener的博客 794

POJO类属性必须使用包装数据类型

POJO类属性必须使用包装数据类型

zhangyupeng的博客 342

Unity 第一人称视角控制移动、旋转

第一人称视角操作此功能主要是借鉴YouTube,写的蛮好我就搬运过来让大家也试试。

qq_36515472的博客 4202

【NCBI】SRA toolkit安装及使用-Windows&Linux版本

由于市面上的文章介绍SRA toolkit基本上都是基于Linux,而在windows下运行SRA toolkit基本上可以达到相同的效果且更为方便,故本文将分别阐明在Windows和Linux环境下SRA toolkit的安装及使用。

weixin_48076899的博客 2万+

关于基本数据类型与包装数据类型的使用标准

1)所有POJO类属性必须使用包装数据类型; 2)RPC方法的返回值和参数必须用包装数据类型; 3)所有局部变量推荐使用基本数据类型。 说明:POJO类属性没有初值是为了提醒用户在使用时必须自己显示地赋值,任何NPE问题,和入库检查,都由使用者来确保。 举例说明: 1.所有sql使用的默认值都是null,如果POJO中的属性为基本类型,当查不到记录时,把null赋值给基本类型是会出错的。 2.包装类型本身可以转换为基本类型,如果设置基本类型比如是int的话,一开始的值就是0,但是0本身就代表着一

罗格的博客 984

机器学习ROC曲线中的阈值thresholds

在ROC曲线上的不同点对应于不同的阈值。根据任务的具体要求,可以选择合适的阈值,以达到平衡召回率和误报率,或者根据具体应用场景调整模型的工作点。,表示样本属于正例的概率。这个概率值可以被截断为二元分类的决策,通过选择一个阈值来决定样本是被分类为正例还是负例。通过改变阈值,可以得到不同的真正例率(TPR)和假正例率(FPR),从而绘制出ROC曲线。总体来说,ROC曲线提供了模型在不同阈值下的性能综合情况,帮助分析模型的分类能力。通常来说,随着阈值的增加,TPR会减小,而FPR会增加。ROC曲线的绘制涉及。

行子的博客 3657

关于POJO类使用包装类型的思考

关于基本数据类型与包装数据类型的使用标准如下: 1) 所有的POJO类属性必须使用包装数据类型。 2) RPC方法的返回值和参数必须使用包装数据类型。 3) 所有的局部变量推荐使用基本数据类型。 说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。 【正例】:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。( NPE 下文有解释) 【反例】:某业务的交易报表上显示成交总额涨跌情况,即正负 x.

weixin_42552851的博客 739

AIGC实战指南:从提示词到工作流,掌握AI内容生成核心技能

人工智能生成内容(AIGC)作为当前AI技术的重要应用领域,其核心原理基于大语言模型(LLM)对海量数据的学习与模式识别。这项技术的价值在于将复杂的创作过程自动化,显著提升内容生产效率与创意实现的多样性。在实际应用中,AIGC已广泛渗透至文本创作、图像设计、音频合成及视频生成等多个场景,成为数字内容生产的重要工具。要高效运用AIGC,关键在于掌握提示词工程与模型微调等核心技能。例如,通过结构化提示词设计,可以精确引导模型生成符合预期的专业周报;而选择合适的微调模型,则能大幅提升图像生成的风格控制力与输出质量

weixin_26808439的博客 377

hadoop 切片机制详解

hadoop切片机制详解

congge 1万+

Java对象禁止使用基本类型详解

Java对象禁止使用基本类型详解

靖节先生的博客 903

从 Material Requirements Planning 看 SAP 物料计划的底层控制逻辑

文章摘要:SAP中的MRP(物料需求计划)不仅仅是运行一个程序(如MD01N),其关键在于主数据和组织层级的正确配置。MRP的核心是解决"何时需要多少物料"的问题,涉及采购、生产、库存等多种供应来源。物料主数据的MRP视图必须在组织层级(如工厂、存储位置)下维护,不同层级的计划逻辑可能完全不同。S/4HANA更强调MRP Area作为细分计划单位,取代传统存储位置MRP。实施时需注意MRP Profile的合理使用,避免批量错误,并特别关注物料生命周期管理(如停用物料和替代件),这些因素共同决定了MRP能否

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1417

我司为什么禁止使用阿里巴巴Java规范?

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!每天14:00更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 Elast...

weixin_44421461的博客 179

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

为什么阿里巴巴禁止开发人员 boolean 类型变量使用 isXXX 来命名?

>>号外:关注“Java精选”公众号,菜单栏->聚合->干货分享,回复关键词领取视频资料、开源项目。背景平时工作中大家经常使用到boolean以及Boolean类...

Java精选 767

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

阿里规约禁止开发人员使用isSuccess作为变量名

阿里规约禁止开发人员使用isSuccess作为变量名 参考网址: https://mp.weixin.qq.com/s?__biz=MzAwMjk5Mjk3Mw==&mid=2247485276&idx=1&sn=7a36cf27893b1f6e1969185ba09c3cac&chksm=9ac0b97eadb730682a47f5df1d0c25b3b8497670f957d889a70913c095422b7eefd592f71ac7&scene=21#wech

java技术博客 556

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

我司为什么禁止使用阿里巴巴 Java 规范?

点击下方“IT牧场”,选择“设为星标”来源:www.jianshu.com/p/97778b21bd00前言阿里巴巴出了一本Java规范,在国内Java开发眼里被赋予了神圣的殿堂,但是我司禁止使用阿里巴巴的开发手册。目前Google Guava在实际应用中非常广泛,本篇博客将以博主对Guava使用的认识以及在项目中的经验来给大家分享!学习使用Google Guava可以让你快乐编程,写出优雅的JA...

ITMuch的专栏 738

很多公司为什么禁止使用阿里巴巴Java规范?

前言阿里巴巴出了一本Java规范,在国内Java开发眼里被赋予了神圣的殿堂,但是我司禁止使用阿里巴巴的开发手册。目前Google Guava在实际应用中非常广泛,本篇博客将以博主对Guava使用的认识以及在项目中的经验来给大家分享!学习使用Google Guava可以让你快乐编程,写出优雅的JAVA代码!面向对象思想处理字符串Joiner/Splitter/CharMatcherJDK提供的Str...

公众号-老炮说Java 269
上一篇: 深入浅出 Spring 框架,原来以前的都白学了
下一篇: 惊艳!Linux 中迷人的 Shell 脚本工具
Java佳佳
博客等级 码龄4年 216粉丝 286原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值