那问题来了,什么是数据结构呢?我之前也想过怎么优雅的来回答这个问题,每次总感觉自己给出的答案不够准确。于是我便请教了「木马之王」—— Chigo,他的回答很精炼,说出了我想要表达的意思,「数据结构就是结构化的数据模型,方便计算机存储和构造数据,比如字符串、数组、List、Set、Map、Class 、树、图等」,以后别人面试你的时候,你可以用得上^_^。
对于 Java 而言,它不仅提供八种内置基本数据类型,还提供了很多丰富的拓展数据类型,之所以要提供这么多的数据类型,就是便于我们这些懒惰的程序员使用「如果你想成为一名优秀的工程师,还是值得花时间去研究这些数据结构类型的」。
好了,言归正传,今天就来聊一下 String、StringBuffer 和 StringBuilder 这三个字符串处理类。
这三个类,我相信大大小小的面试基本上都会问。今天咱们不聊面试,只来排雷。
就发展历史而言,StringBuilder 都晚于它的两个老大哥,它是在 JDK 5 中才出现的,网上很多文章都说它与 StringBuffer 没得什么区别只是去掉了 sychronized 关键字「线程安全」部分。
其实对于 JDK 7 及之前的版本可以这么说,但是对于 JDK 8 以后,StringBuffer 是加了 toStringCache 缓存字段的,而且这个字段是用 transient 关键字修饰的,肯定有些读者不知道这个关键字是干嘛的,如果你用过一些序列化的工具,一定对这个关键字不会陌生,被这个关键字修饰过的属性,是不会被序列化的,换句话来说,就是这个字段只会存在于内存之中。
总的来说,JDK 8 之后 StringBuffer 还是有一点儿改变的,后面我们详细来谈谈它具体的改变。对于它们而言,都继承自 AbstractStringBuilder,其实说白了,StringBuffer 和 StringBuilder 的具体操作都是由 AbstractStringBuilder 来实现的,重点看一下它内部是如何扩容的。
对于 String 而言,我觉得是很基础而有特别重要的类,说得难听点,你只要还想吃 Java 这口饭,这个类你必须要掌握。它可聊的话题就太多了,比如它的不可变性?JDK 6 为什么不推荐使用它的 intern() 方法?它到底是值类型还是引用类型?JDK 8 之后,常量池有那些变化?各个版本的 JDK 在编译期间对它进行那些优化?JDK 9 以后存储数据的 char 数组发生了那些改变? 云云。。。
案例 1
我之前在网上看到一个案例,特别具有代表性,在这里给各位读者分享一下。
线上服务器负载过高而导致系统报警。
一般来说,负载过高,多半是某个程序在不停的消耗 CPU 而引起的。引起该问题的代码如下,见下图。
如果你根据堆栈信息进行分析,就会发现 CPU 在不停的执行拷贝动作,是什么原因导致的呢?
如果你熟悉 StringBuffer 的话,那么很快可以定位到,是 buf.toString() 导致的。有的读者可能会问你怎么知道呢?看一下它的源码不就知道了嘛。。。
注意到没,System.arraycopy() 这个函数就是问题症结,这是一个内存拷贝函数,直接操作系统内存。
问题找到了,那么也就可以抓药了,对于这类的问题,说白了还是开发者自己重复制造轮子所致,其实这也是我一直在团队强调的,有好的轮子就不要自己去制造,编码规范一定要统一起来。
案例 2
可能有读者朋友知道,我最近建了一个技术交流群,群里主要讨论技术,也会不时的有老司机开车「记得系好安全带」。这不,一个同学发现关于 IDEA 的调试问题,目前这个问题,我们还没有找到具体的症结,感兴趣的朋友一起研究一下。
开发环境:JDK 1.8
工具:Eclipse MyEclipse IDEA
具体描述:在如下的代码中,跟下图一样打上断点,注意一定要跟进 StringBuffer 源码里面,在 Eclipse 和 MyEclipse 能按照预期结果返回,但是 IDEA 中却不能正常返回。
在红线处,打上断点。
点击红圈,跟进到 StringBuffer 源码「如果你细心的话,一定注意到 append 方法跟 1.7 有所不同」,出现如下图所示。
你会发现 toStringCache 的值未被置 null,因此,导致还是去返回缓存里面的值,最终结果为「111」,而非「111222」,见下图。
但是,在 Eclipse 中,toStringCache 的值被置 null 了,见下图。
这个问题,我觉得还是挺有意思的,我初步怀疑可能是 IDEA 的问题,但是如果 IDEA 不设置断点,返回结果就跟预期的一样「111222」。
如果你找到了原因,欢迎留言告诉我~~~
今天的分享就到这里了,写一篇文章挺费时间的,希望各位读者转发一下,忆蓉君在此谢谢各位了。
参考