0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

简述那些JDK中坑你没商量的方法

电子工程师 来源:博客园 作者:Yrion 2021-06-12 17:36 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

前言

JDK 作为我们每天必备的调用类库,里面大量提供了基础类供我们使用。可以说离开 JDK ,我们的 Java代码寸步难行。

JDK 带给我们的便利可谓是不胜枚举,但同时这些方法在使用起来也存在一些坑,如果不注意就很容易掉入到陷阱里面,导致程序抛出错误。

JDK 中的很多方法都不会做非 null 判断,可能设计 JDK 的作者默认开发者已经处理好 null 值了。不过这个设计可能会造成很严重的后果,实在是暗藏杀机。比如,今天早上我们查了一笔订单没有退款,查了一早上最终才发现是同事写的代码的 BigDecimal 的 subtract 方法的值没有做非 null 判断处理导致程序抛出了空指针异常。

看似简单的异常却直接无法让很多订单退款,实在是小问题造成大事故。而要修补退款这个问题,要耗费很多时间去修补,出错的成本太高。

本文就来看看 JDK 中那些坑你没商量的方法,这些方法很常见,相信你一定遇到过。

1 String.valueOf() 方法的陷阱

案发现场

某个鸟语花香的早上,我们在开心的敲着代码,突然客户群有人投诉反映:我们发给用户的短信有部分是尊敬的 “null” 你好,等等。

开发第一时间看了代码,觉得没有问题啊。为什么短信内容会出现用户名为 null 呢?不是经过了非空判断的吗?String.valueOf() 是 String 提供的一个类型转换的方法,我们来看一下(代码经过了简化):

// 调用用户服务根据用户id获取用户信息Map《String, Object》 userInfo = userService.getUserInfoById(userId);Object userNameObject = userInfo.get(“name”);String userName = String.valueOf(userNameObject);// 判空if(userName!=null && userName.length()》0) { String message = getMessage(userName); smsService.send(message);}

这段代码是简化过的,主要作用就是通过用户服务根据 id 获取用户信息发送短信。

后来经过定位发现了问题所在:首先,用户的名字里有特殊的 emoji 符号。数据库写入的时候有部分写入失败。因为当时的数据库字符格式并无法兼容 emoji,而获取的时候因为这个问题值为 null 了。

接下来是重点:

public static String valueOf(Object obj) { return (obj == null) ? “null” : obj.toString();}

这里是重点,也是最大的坑。

注意:这里返回了一个 “null” 的字符串,而不是 null。这两个是有很大区别的。当进行非空判断的时候,返回的是 true。也就是这个 “null” 字符串是符合判空条件的!

正确的姿势是在 String.valueOf 方法前必须判空:

if (userNameObject != null) { String userName = userNameObject.toString();}

2 Integer.parseInt() 方法很矫情

事故现场

业务场景为拉取订单,打出订单列表记录。财务人员需要拉出对账,结果总是发现很奇怪的一个现象:每次拉取少很多数据。还好财务发现了,要不然和第三方财务对账就会亏很多钱。

最终发现,是订单的一个字段值转 Integer 出错了。那个订单下的字段值是 120.0,通过 Integer.parseInt() 直接报错了。恰好开发人员认为这段开发肯定没问题,因此就没有 catch 异常。最后找了很久才发现,因为涉及到第三方,还让别人查了半天……

知道真相的我们都有点汗颜:这么丁点的错误排查了很久,实在是不应该啊。

Integer.parseInt() 方法用于将字符串转化为 Integer 类型的方法。此方法的适用性就显得比较窄,因为是 String 类型的参数没有任何限定,当在传入一些比如 50.0、20L、30d、40f 这类数据的情况下会抛出异常。

我们来看一个例子:

String input = “50.0”;int out = Integer.parseInt(input);

会抛出异常 NumberFormatException:

740e2dd4-c7a3-11eb-9e57-12bb97331649.png

事实上对于这样的数据,比如小数、float、double、long 类型数据都可以自动转换,而不是给我们抛出烦人的报错信息。如果预先知道是整数或者小数,可以用 BigDecimal 转换。

注意:此方法不适用于 double、float、Long 类型的数据,比如10d、20L。

String input = “50.0”;int out2 = new BigDecimal(input).intValue();System.out.println(out2);

对于 float、long 类型的数据可以用以下方法来处理:

推荐使用 hutool 的 NumberUtil.parseInt() 方法。充分考虑到了 float、double、long、小数等类型数据可能带来的解析异常的问题。hutool 是一个国人开源的工具类库,这里强烈推荐,容错性和处理异常能力很强。

3 BigDecimal 的除法坑你没商量

众所周知,BigDecimal 是处理金额最有效的数据类型。一般进行财务报表计算的时候为了防止金额出现错误,一般情况下都会采用 BigDecimal。而 double、float 都会存在些许的误差。你开开心心地用 BigDecimal 进行了计算,而最终的结果返回却有问题。我们来看一个例子:

BigDecimal ten = new BigDecimal(10);BigDecimal two= new BigDecimal(2);BigDecimal result = ten.divide(two);System.out.println(result.toString());

常见的除法用起来没有任何丝毫的问题,妥妥的没毛病。但是一旦程序中的数据出现以下情况,如果用 BigDecimal 来接受前端的参数。而前端的参数是用户输入不确定的,一旦出现如下的数据,我们来看看结果:

BigDecimal ten = new BigDecimal(10);BigDecimal three= new BigDecimal(3);BigDecimal result = ten.divide(three);System.out.println(result.toString());

执行结果一看,居然报错了:

74596fc4-c7a3-11eb-9e57-12bb97331649.png

这就是 BigDecimal 的坑:一旦返回的结果是无限循环小数,就会抛出 ArithmeticException。因此在进行 BigDecimal 除法的时候,需要进行保留小数的处理。

正确的处理姿势:

BigDecimal ten = new BigDecimal(10);BigDecimal three= new BigDecimal(3);BigDecimal result = ten.divide(three, 2, BigDecimal.ROUND_HALF_UP);System.out.println(result.toString());

4 Collections.emptyList() 此 List 非彼 List

我们先来看一个例子:

public List《String》 getUserNameList(String userId) { List《String》 resultList = Collections.emptyList(); try { resultList = userDao.getUserName(userId); } catch (Exception ex) { logger.info(ex); } return resultList; }

这样会抛出错误。主要问题在于 Collections.emptyList() 并非我们平时看到的 List。此List 不支持 add、remove 方法,否则会抛出 operationNotSupportException。

List《String》 resultList = Collections.emptyList();resultList.add(“test”);

结果抛出异常:

74631236-c7a3-11eb-9e57-12bb97331649.png

原因是 Collections.emptyList 返回的并不是我们平时认识的那个 List,它是一个内部常量类:

public static final List EMPTY_LIST = new EmptyList《》();

这个 List 并不具有 add、remove 元素的能力。我猜想是因为 JDK 设计之初的想法是将这个 List 作为一种只读的 List ,并不提供数据的写入能力。因此它仅可作为一种空值返回,无法进行删除、添加操作。

5 List 可以一边删除一边遍历吗?

答案是肯定可以的。要不然的话 List 怎么删除数据呢?不过要注意遍历的姿势,我们再来看一个简单的例子:

public static void main(String[] args) { List《Integer》 resultList = new ArrayList《》(); resultList.add(1); resultList.add(2); resultList.add(3); for (Integer num : resultList) { if (num == 1) { resultList.remove(num); } }}

很不幸,又双叒叕报错了:

746f0c62-c7a3-11eb-9e57-12bb97331649.png

仔细翻阅源码会发现,每次 remove 之前会检查元素的条数。如果发现预期的 modCount 和当前的 modCount 不一致就会抛出这个异常。modCount 是 List 中用来记录修改次数的一个属性,当对元素进行统计的时候就会对该元素加 1。而当对 List 边遍历边删除的话,就会造成 excepted 与 modCount 不一致,从而抛出异常。

final void checkForModification() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); }}

正确的删除姿势就是使用 Iterator.remove 进行遍历删除,可以规避这个问题。

List《Integer》 list = new ArrayList《》();list.add(1);list.add(2);list.add(3);list.add(4);Iterator《Integer》 iterator = list.iterator();while (iterator.hasNext()) { Integer integer = iterator.next(); if (integer == 2) { iterator.remove(); }}

6 总结

JDK 的设计者有两个很大的特点:

大多不会做非 null 判断;

出现错误直接 throw new Exception,容错性很差。

在实际开发中,面对 JDK 一定要谨慎使用。JDK 提供了便利的同时,也有一些我们使用上的盲区。应该养成多看源码,多注意错误性处理,防止在小问题上栽大跟头。

回到最开始说的那个 subtract 方法的问题,因为这个问题等需要我处理完之后用户才能收到退款,这直接造成了用户体验直线下降,而部分用户还直接打电话投诉。同事一个小小的不谨慎和马虎就给公司造成了很多负面影响,技术问题虽然不大但是带来的业务影响范围很严重。

所以我们必须防微杜渐,小小的问题都得细细的打磨,才能避免很多问题的产生。

7 持续更新

7.1 BigDecimal 在比较的时候最好使用 compareTo 方法,不要使用 equals 方法

如下案例。虽然 BigDecimal 重写了 equals 方法,但是使用会存在问题:

public static void main(String[] args) { System.out.println(new BigDecimal(“1”).equals(new BigDecimal(“1.0”)));}

D:jdkbinjava.exe 。。.false

1 和 1.0 在比较的时候返回了 false。这是因为在 equals 的源码中进行了数据的 scale(也就是精度)的比较。如果不一致就会返回 false。如果使用 compareTo 方法就不存在这个问题。

7.2 MySQL 减法计算,如果有 null 值结果就为 null

select 5-null 结果会返回 null。所以在进行 MySQL 计算的时候,对于有可能出现 null 值的列一定要进行 ifnull(field, 0) 的转换,将 null 值转化为 0。否则就会出现一些意想不到的数据错误和空指针问题。

7.3 String 的 split 方法在进行 || 分割的时候需要进行转义,否则结果会有问题

String str = “77||88”;final String[] split1 = str.split(“||”);final String[] split2 = str.split(“\|\|”);

System.out.print(“错误的分割方式:”);for (String s : split1) { System.out.print(s + “ ”);}System.out.print(“

”);

System.out.print(“正确的分割方式:”);for (String s : split2) { System.out.print(s + “ ”);}

错误的分割方式:7 7 | | 8 8 |正确的分割方式:77 88

转自:Yrion,

链接:cnblogs.com/wyq178/p/13520745.html

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    20

    文章

    2997

    浏览量

    115684
  • 代码
    +关注

    关注

    30

    文章

    4941

    浏览量

    73151
  • JDK
    JDK
    +关注

    关注

    0

    文章

    83

    浏览量

    17098

原文标题:那些JDK中坑你没商量的方法

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    那些年我用OpenCV+Qt趟过哪些?写给视觉应用开发者的避指南

    从一个写脚本的算法爱好者,到能独立设计交付跨平台视觉检测系统,中间踩了无数的,也总结出一套还算行之有效的方法。今天,际赛威刘老师不谈高深理论,就想把这套从基础搭建、核心架构到项目实战的完整
    发表于 12-02 09:43

    STM32 5 个容易踩的外设使用技巧

    STM32是嵌入式开发领域的热门MCU,功能丰富到几乎可以完成所有常见控制任务:GPIO、ADC、UART、定时器、DMA……应有尽有。但是,正因为功能强大,开发的机会也随之增多。很多初学者
    的头像 发表于 11-24 19:04 514次阅读
    STM32 5 个容易踩<b class='flag-5'>坑</b>的外设使用技巧

    聊聊怎么把 SAW 滤波器量“准”:VNA、S 参数和群时延那些

    是否较低。 PCB 上输入/输出走线短而直,下方有整块地平面,附近多打 GND via。 如果有 FCom 提供的推荐版图,对照一下自己板子的走线/焊盘,能提前避掉不少。 3. 看 S 参数
    发表于 11-19 17:38

    为什么360°镜头容易“踩”?

    视场角来更直观地描述其视野范围。如何选择与避?明确的FOV需求:全视野(360°x180°):镜头正上方的半球形全景。这是最常见的360°镜头,能一次性覆盖机器
    的头像 发表于 11-18 11:29 178次阅读
    为什么360°镜头容易“踩<b class='flag-5'>坑</b>”?

    选择身份核验终端硬件设备,需要避开那些

    是天波小编为准备的一份避指南。办公楼使用天波人脸识别身份核验终端闸机首先,我们在选择身份核验终端时,要先看它的处理器性能,和是否支持国产操作系统。一般来说,足够的
    的头像 发表于 09-02 10:23 485次阅读
    选择身份核验终端硬件设备,<b class='flag-5'>你</b>需要避开<b class='flag-5'>那些</b><b class='flag-5'>坑</b>?

    舵机原理简述!

    舵机原理简述 舵机是一种高精度的位置伺服执行机构,广泛应用于机器人关节、无人机舵面控制、航模操控等场景,其核心功能是通过接收控制信号,精确驱动输出轴旋转到指定角度并保持稳定。以下从结构组成、控制
    的头像 发表于 08-22 10:57 1105次阅读

    UWB自动跟随技术原理、算法融合优化和踩实录

    UWB为什么是最靠谱的自动跟随技术?原理是什么?需要做什么算法融合、优化?我们在开发过程踩过的
    的头像 发表于 08-14 17:45 1129次阅读
    UWB自动跟随技术原理、算法融合优化和踩<b class='flag-5'>坑</b>实录

    PLC工业智能网关:功能解析、场景落地与选型避攻略

    如何避免选型踩?本文从技术原理、核心价值、典型场景、避指南四大维度,结合真实案例与行业趋势,为您彻底拆解PLC工业智能网关的“真面目”。
    的头像 发表于 07-16 13:21 463次阅读
    PLC工业智能网关:功能解析、场景落地与选型避<b class='flag-5'>坑</b>攻略

    关于SFP连接器必须了解的那些知识

    关于SFP连接器必须了解的那些知识 一、SFP光笼子的作用及材料组成 1.光笼子的概念与作用 ① 光笼子是什么? SFP Cage(Small Form-factor Pluggable Cage
    的头像 发表于 06-17 09:42 776次阅读
    关于SFP连接器<b class='flag-5'>你</b>必须了解的<b class='flag-5'>那些</b>知识

    JDK从8升级到21的问题集

    " •sun.misc.BASE64Encoder等内部API废弃 → 引发编译错误 •JAXB/JAX-WS从JDK核心剥离 → XML处理链断裂 •Lombok与新版
    的头像 发表于 06-06 16:49 592次阅读

    Modbus TCP通信的那些!遇到过几个?

    凌晨三点,产线骤停!触摸屏疯狂报通讯超时,老板夺命连环Call轰炸,而你盯着PLC的Error灯狂闪、仪表数据集体躺平——Modbus TCP的,谁踩谁懂! 别慌!这篇Modbus TCP常见问题
    的头像 发表于 04-16 17:17 1129次阅读
    Modbus TCP通信的<b class='flag-5'>那些</b><b class='flag-5'>坑</b>!<b class='flag-5'>你</b>遇到过几个?

    公路路面病害(如裂缝、槽等)检测

    公路路面病害(如裂缝、槽等)检测
    的头像 发表于 04-10 11:43 690次阅读
    公路路面病害(如裂缝、<b class='flag-5'>坑</b>槽等)检测

    SMT贴片加工那些关键要素,了解吗?

    一站式PCBA智造厂家今天为大家讲讲SMT贴片加工流程的关键要素有哪些?SMT贴片加工流程的关键要素。随着电子产品日益小型化、轻量化的发展趋势,SMT贴片加工作为电子制造的关键技术,已经成为
    的头像 发表于 04-01 09:46 659次阅读

    被问爆的避方法!——天线设计

    天线设计,也是4G模组应用中最容易踩的地方。今天主要分享讨论Air700ECQ/EAQ/EMQ系列模组,天线管脚到4G天线之间的电路设计和走线规则。   Air700ECQ/EAQ/EMQ模组属于
    的头像 发表于 12-26 16:45 1375次阅读
    被问爆的避<b class='flag-5'>坑</b><b class='flag-5'>方法</b>!——天线设计

    电源设计避(下)

    。在上一期《电源设计避(上)》,我们讨论了电源设计的电源功率、稳定性、纹波控制以及尖峰和浪涌的问题,并结合实际案例提出了相应的应对措施。接下来,我们将继续探索
    的头像 发表于 12-16 11:37 1068次阅读
    电源设计避<b class='flag-5'>坑</b>(下)