资讯专栏INFORMATION COLUMN

记一次愚蠢的经历--String不可变性

woshicixide / 1633人阅读

摘要:文本已收录至我的仓库,欢迎记录一次在写代码时愚蠢的操作,本文涉及到的知识点不可变性一交代背景我这边有一个系统,提供一个接口去发送短信。

前言
只有光头才能变强。

文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y

记录一次在写代码时愚蠢的操作,本文涉及到的知识点:String不可变性

一、交代背景

我这边有一个系统,提供一个RPC接口去发送短信。外部调用我的接口需要传入手机号等等参数,我这边负责解析这些参数、做一些业务的处理,然后调用短信渠道商的接口发送短信。

每当调用完短信渠道商的接口时,我会对这次发送的记录入库(存入MySQL中),同样地短信渠道商会返回发送或失败的回执给我,我也会入库(存入MySQL中)。

那天,有人来找到我,说某个手机号收不到短信,用户并没有屏蔽短信(欠费、关机)等等一些操作,就是收不到短信。

于是我就去排查啦,首先我先去DB里边找有没有对应的发送记录,发现这条记录是存在的,而且在DB上看不出来有什么异常。

所以,这就排除了这个操作在中途被拦截的情况(因为已经入库了,就肯定调用过短信运营商的接口)

后来就去捞日志,看看调用短信运营商返回的Result对象的信息是什么,然后就去问了一下短信运营商可能出现这种问题的原因是什么。那边回复的是:“如果是部分的手机号出现这种状况,是不是你们的手机号没有trim啊?

于是,我又去捞日志,发现手机号后面真的带有一个空格(扎心了,之前一直看不到)。要处理这个问题就变得异常简单了,我只要在入口里边对手机号进行trim就好了。

二、编写代码

我这边是支持同一条短信向多个手机号发送,于是手机号我这边用的是HashSet来进行接收。对手机号进行trim我写下了如下的代码:

// 说明:Task对象 有个 key属性,这个key属性的类型是HashSet

if (task.getKey() != null && task.getKey().size() > 0) {
    for (String s : task.getKey()) {
        s.trim();
    }
}

代码很简单,我做的就两步:

判断是否为null,不为null值则遍历手机号集合

对每个手机号进行trim

上面的代码有问题吗?必须有问题啊,没问题我还写啥。

下面写个小Demo,我们会发现:在代码的11行上调用trim()方法后,在12行再输出,还是会有空格的情况。

2.1 为什么会有这种错觉?

其实,我们在初学Java的时候,肯定会学到String类。在学习的时候也是明确String是不可变的,但总是有个感觉我们把String对象给改了,为什么?

我觉得第一点是这样的:我们操作的往往是可变的对象,对象的某些属性改了,我们就认为已经改了。比如下面的代码:

HashSet students = getStudent();
for (Student s1 : students) {
    s1.setName("Java3y");
}

执行完,我们就认为在HashSet里边的Student的名字全改成Java3y了,而实际上也是如此。

我觉得第二点是这样的:我们平时操作String对象,都是直接把操作后的结果传过去,这看起来就像修改原对象了一样。比如下面类似的代码:

// 去重
String phone = "   137888888888    ";
sendPhone(phone.trim());

// 转成大写后输出
System.out.println(phone.toUpperCase());

// ... 等等

2.2 怎么改

现在问题已经知道了,String对象是不可变的,对String对象进行操作,“看似”把原来的String对象改了,实际上是生成了一个新的String对象。

回到我那个问题,也很好解决,把trim好的手机号设置到HashSet就行了

// 说明:Task对象 有个 key属性,这个key属性的类型是HashSet

HashSet hs = new HashSet();
if (task.getKey() != null && task.getKey().size() > 0) {
    for (String s : task.getKey()) {
        hs.add(s.trim());
    }
}
task.setKey(hs);
最后

这个B写了一篇文章来解释自己是怎么“合理“写Bug的,真不要脸。

乐于输出干货的Java技术公众号:Java3y。公众号内有200多篇原创技术文章、海量视频资源、精美脑图,关注即可获取!

觉得我的文章写得不错,点

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75554.html

相关文章

  • 一次愚蠢操作--线程安全问题

    摘要:文本已收录至我的仓库,欢迎记一次在工作中愚蠢的操作,本文关键字线程安全我怎么天天在写啊一交代背景我这边有一个系统,提供一个接口去发送各种信息比如短信邮件微信等等渠道。小王用了一阵子也没说有什么问题,于是这个需求就交付了。 前言 只有光头才能变强。文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 记一次在工作中愚蠢...

    Forelax 评论0 收藏0
  • 一次 Booking 线上面试中遇到小问题

    从事 Android 开发工作要满 5 年了,虽然明白自己技术很一般,但是也总是期望能够有机会进入更好的平台发展。这不,因为机缘巧合有了一次 Booking 的面试邀请(是在 hackerrank 上),然后开始临时抱佛脚 (leetcode 走起),最终选择了一个周末去完成线上测试,结果我完全没预料到。本以为会被某道题的逻辑绕昏,结果哪知道被标准输入这个东西卡得死死的,现在就记录一下这次非常糟...

    X1nFLY 评论0 收藏0
  • 一次 Booking 线上面试中遇到小问题

    从事 Android 开发工作要满 5 年了,虽然明白自己技术很一般,但是也总是期望能够有机会进入更好的平台发展。这不,因为机缘巧合有了一次 Booking 的面试邀请(是在 hackerrank 上),然后开始临时抱佛脚 (leetcode 走起),最终选择了一个周末去完成线上测试,结果我完全没预料到。本以为会被某道题的逻辑绕昏,结果哪知道被标准输入这个东西卡得死死的,现在就记录一下这次非常糟...

    lykops 评论0 收藏0
  • [译] Python 学习 —— __init__() 方法 1

    摘要:第一是在对象生命周期中初始化是最重要的一步每个对象必须正确初始化后才能正常工作。第二是参数值可以有多种形式。基类对象的方法对象生命周期的基础是它的创建初始化和销毁。在某些情况下,这种默认行为是可以接受的。 注:原书作者 Steven F. Lott,原书名为 Mastering Object-oriented Python __init__()方法意义重大的原因有两个。第一是在对象生命...

    MobService 评论0 收藏0
  • 如果连铁将军都不再可靠--一次排查使用分布式轮候锁+SESSION防订单重复仍然加锁失效问题经历

    摘要:尽可能地将数据写入,例如创建设置的都会将数据立即的写入再来看看文档怎么描述的看看这可爱的默认值我们终于知道了当我们不做任何设置时,默认采用的是方式显而易见,使用方式能最大限度的减少与的交互,而在大多数场景下都是没有问题的。 0.问题背景 此次问题源于一次挺严重的生产事故:客户的订单被重复生成了,而出问题的代码其实很简单: // .... redisLockUtil.lock(membe...

    econi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<