由一篇吐槽对String空字符串判断的文章所引发的碎碎念
Posted cc11001100
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由一篇吐槽对String空字符串判断的文章所引发的碎碎念相关的知识,希望对你有一定的参考价值。
一、起因
最近有篇关于String空字符串判断的文章火了,老是看到这篇文章,既然如此我也只好认真看了下:程序员晒出一段代码引来无数网友狂喷!网友:你就活该当码农!
我也觉得这段代码写的不怎么的,首先程序的正确性应该没有问题,我只是觉得如果写出的代码跟道上认可的规范有违的话可能会恶心到后面接手的人而不自知,我现在莫名打喷嚏的时候就在想肯定是在之前公司写的代码又被接手的人吐槽了… :)
看上去就是个判断字符串是否为空字符串的方法,首先这类方法应该提取到StringUtil类似的工具类中,而这个private的权限看上去应该是在调用它的类中找了个宽敞的地就给写上了,这种开发习惯可能会导致项目中存在一些冗余代码。如果对于基础代码的复用没有把握好的话,当项目功能庞大到一定程度的时候用IDEA黑色主题就能看到满满的shi黄色以及后人从头开始阅读项目代码时会不断有这种感觉:啊,这个代码片段我好像刚刚在哪里看到过…个人猜测可能是项目刚刚开发的时候工期比较紧,程序员心想先能跑起来再说反正以后我会重构的,当然大家都懂的,当一个程序员说以后xxx的时候八成回头就忘…然后就这鸟样了。
扯一个话外题,如果细心的话会注意到刚才我说的是StringUtil,关于Util类的命名也有不少的争论,有人认为应该是FooUtils,而有的人认为应该是FooUtil不带s,我个人比较认可的方法是工具类所在的包名带s为com.foo.utils,而其下所放的工具类则不带s为FooUtil,当然只是个人看法,关于这个可以做个试验,在一个依赖开源项目比较多的项目中使用StringUtil为前缀一搜索就能看到不同开源项目也是用哪种方式命名的都有,这个问题还真不太好下定论,反正一个项目中统一用一个风格即可。
然后就是我非常想吐槽的方法名这是导致我写这篇博客的原因,validateString是个什么鬼,如果我前面的猜测成立,这个方法是写在调用它的类中的话还好,因为方法名应该反映的是做了什么而不是怎么做,这个方法只是为了校验一个东西是否合法具体怎么做不关心,但是是校验的是什么东西呢?String是啥?这名字更合适的是将validateString的String改为具体指代的概念,比如validateEmail/validateUserName,而如果是在StringUtil类中的话,validateString就太模糊,因为当针对字符串的操作种类比较多的时候,对其命名就应该粒度更细一些,这里我认为在StringUtil中这样的操作一个更合适的方法签名是public boolean isNotEmpty(String s),后面会针对字符串的几种状态及相关方法进行详述。
接下来就是实现了,我也闹不明白为啥搞那么复杂,直接return应该也可以:
private boolean validateString(String str) { return str != null && !"".equals(str); }
上面用到了取反来对表达式结果否定,取反一般脑子会需要急拐弯一下,人脑运算速度稍高不是老司机的话急拐弯容易翻车,所以编程中应该尽量少用取反,为了更好理解可以再稍稍改一下:
private boolean validateString(String str) { return str != null && str.length() != 0; }
如果是抽取到StringUtil中的话就是:
public static boolean isNotEmpty(String s) { return s != null && s.length() != 0; }
另外还有一个小细节,小括号和花括号之间没有空格,可能是代码没有格式化过,应该格式化一下统一风格的。
这里还有楼主认为维护别人的代码会让自己不舒服,因为IT行业的特殊性,大部分情况下项目的生命周期都要比开发人员在这个公司的工龄要长,一段代码经过几个人的手是很正常的。这里又想扯一个话外题,就是一个之前看过的例子,具体记不清了大概就是说有个人开发了一套系统,但是留有很大的隐患估计自己也不知道,后来过了很久了问题终于爆发了,关键是这个时候他都升了好几级了暂称他为P3,他直接下属是P2,P2的某个倒霉的直接下属是P1(此处级别绝无映射只是为了方便叙述),P1找到了问题的根源发现是当初开发系统的人的考虑不周导致很难搞然后汇报给了直属领导P2,P2要维护直属领导P3的面子不敢让其知道这是当年P3留下的隐患(表示不理解,这点担当都没有还当啥领导啊…),就想了个孬点让P1在工作时间之外把这个问题解决了不计入正常工作,这样也不必向上汇报了,然后工作时间照常给他排其它任务,倒霉的P1觉得自己很冤就在论坛发帖子把这事捅了出来让大家评理…反正我是被唬得一愣一愣的,感觉IT圈有点乱,遇到这种问题还真够喝一壶的。
最后还有一个想法要说,就是看到那篇文章的评论中某高级工程师的回复,很容易让人认为这个行业已经变得是劣币驱逐良币,希望只是刚吃完饭血糖升高判断力降低才导致发言失误。还有就是当名字后面还挂着公司名字时候,说的话是要为公司负责的,你这一时爽了但不小心玩脱了以后让别人怎么看你东家,你看我虽然级别低没title但是写这些没水平的垃圾文章也还是从来不让别人知道我东家是谁免得给东家丢人… :(
二、我对String状态的几种理解
String状态指的是一个String类型的变量的值可能会有的几种状态:
null 指针为null "" 空字符串(empty) " " 空白字符串(blank) " aabb " 字符串 "aaccbb" 只含字母 "12345" 只含数字
发现好多人都分不清空字符串(empty)和空白字符串(blank)的区别,空字符串就是长度为0的字符串,空白字符串就是只包含空白字符的字符串,在trim之后长度为0。
下面是几个字符串相关的很常用的方法,也是比较通用的概念(都是在字符串指针不为null的前提下):
isEmpty(): 是否空字符串,即字符串的长度为0,比如""
isNotEmpty():对isEmpty取反
isBlank():是否空白字符串,即字符串中只有空白字符,比如" "
isNotBlank():对isBlank取反
trim():去除字符串两边的空白字符,比如" foo "执行trim之后是"foo",具体请见:Java笔记之java.lang.String#trim
trimToEmpty():返回值要么是trim()的结果,要么是空字符串,一定不为null,比如null—> "", " " –> ""
trimToNull():某些情况下不允许为空字符串,如果为空的话就认为是null,可以使用此方法直接将一个空白字符串变为null,比如" " –> null
对于字符串的处理应该尽量使用Apache commons-lang3或者google guava库。
为什么要去使用这种开源库而不是自己造轮子?
一个是知名开源库会有社区专门去维护,使用群体比较大,我们知道当数据量够大的时候规律就比较明显,同理使用的人比较多了之后的一个好处是有BUG会被发现的比较快,可能一个新版本的BUG第二天就被发现并贴出官方声明了,而自己写的实现如果没有人帮review的话可能错误永远都不会被发现了。另外一个原因就是知名开源库因为使用的人比较多,大家有各种好的想法都会提出来去讨论应用到这上面,运用集体智慧不断的去完善、优化它,不管是API的设计还是实现上都会比自己一个人想出来要好一些。
当然硬要抬杠的话总能找到反例,之前碰到过个很奇葩的API实现,就是Apache commons-io的org.apache.commons.io.FileUtils#byteCountToDisplaySize(java.math.BigInteger),代码贴出来感受一下:
/** * Returns a human-readable version of the file size, where the input represents a specific number of bytes. * <p> * If the size is over 1GB, the size is returned as the number of whole GB, i.e. the size is rounded down to the * nearest GB boundary. * </p> * <p> * Similarly for the 1MB and 1KB boundaries. * </p> * * @param size the number of bytes * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes) * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a> * @since 2.4 */ // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed? public static String byteCountToDisplaySize(final BigInteger size) { String displaySize; if (size.divide(ONE_EB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_EB_BI)) + " EB"; } else if (size.divide(ONE_PB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_PB_BI)) + " PB"; } else if (size.divide(ONE_TB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_TB_BI)) + " TB"; } else if (size.divide(ONE_GB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_GB_BI)) + " GB"; } else if (size.divide(ONE_MB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_MB_BI)) + " MB"; } else if (size.divide(ONE_KB_BI).compareTo(BigInteger.ZERO) > 0) { displaySize = String.valueOf(size.divide(ONE_KB_BI)) + " KB"; } else { displaySize = String.valueOf(size) + " bytes"; } return displaySize; }
BigInteger的整除把1.9M被算成1M我忍了,1.9T算成1T这种实在没办法忍差太多了…
这个实现大家看了都说不好,很多年了作者也没改,对于这种情况也没什么好办法…
.
以上是关于由一篇吐槽对String空字符串判断的文章所引发的碎碎念的主要内容,如果未能解决你的问题,请参考以下文章