共计 1789 个字符,预计需要花费 5 分钟才能阅读完成。
前言
只有光头才能变强。
文本已收录至我的 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<Student> 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<String> 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 多篇原创 技术文章、海量视频资源、精美脑图,关注即可获取!
觉得我的文章写得不错,点 赞!