使用findBugs检查代码的时候发现,它会要求你将字符串拼接的操作全部换成StringBuilder,之前了解过String,StringBuilder和StringBuffer之间的区别,但是未进行深入的理解,本篇文章就来分析下它们之间的区别,顺便分析下String类的源码。

三者的关系如下图所示:

public class Test4 {

private static Object LOCK = new Object();

public static void main(String[] args) {

long start = System.nanoTime();

String s = "";
        for (int i = 0; i < 1000; i++) {
            s += i;
        }
        long end1 = System.nanoTime();

StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < 1000; i++) {
            sb.append(i);
        }
        long end2 = System.nanoTime();

StringBuffer sbf = new StringBuffer("");
        for (int i = 0; i < 1000; i++) {
            sbf.append(i);
        }
        long end3 = System.nanoTime();

System.out.println(end1 - start);
        System.out.println(end2 - end1);
        System.out.println(end3 - end2);

}

}

代码执行速度如下:

为什么三者在速度上会有差别,原因是String类是一个不可变类(final),而StringBuilder和StringBuffer是可变类。

不可变的意思是,String对象一旦创建了就是不能改变了,后续对这个String对象的任何操作都会生成新的String对象。JVM也专门对String类进行了优化,字符串常量是放在JVM的方法区中,准确来说是方法区中的常量池中。当创建String时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回引用,字符串不存在常量池中,才会实例化该字符串并且将其放到常量池中,这样保证了常量池中不会有相同的字符串

所以个人建议是,涉及到线程安全问题的时候,使用StringBuffer;如果没有线程安全问题且append之类的操作比较少的时候,直接使用String,否则使用StringBuilder。

分析下String类一些常用到的方法的源码:

可以看出类支持序列化操作,并且底层是将数据放到一个char[]数组中的。

codePointAt(int index):

输出index这个位置的ASCII码,越界则抛出异常。

(数字 < 大写字母 < 小写字母;数字越大,字母越靠后,ASCII码值越大)

CharAt(int index):

输出index这个位置的字符,越界则抛出异常。

IndexOf(String str):

输出在当前字符串中,str出现的第一个位置的下标。如果没有匹配的子字符串,则返回-1。

(ps: lastIndexOf()就是从后往前找而已,返回的也是第一个位置的下标)

trim():

将字符串两端 <=‘ ’的字符都去除掉,因为小于空格的都是一些不可显字符(控制符和一些其他字符),所以可以理解为去空格

substring(int a,int b):

截取字符串,截取的范围为:[a,b),越界会抛出异常

replace()/replaceFirst()/replaceAll():

replace是将字符串中的某个字符全部替换为新字符,然后返回新的字符串。

后面两个针对的是字符串,前者是替换第一个遇到的字符串序列,后者是将全部符合的字符串序列都替换掉。(用到了正则表达式)

split():

注意如果是转义字符的话,必须加上“\\”。方法内部是使用正则表达式进行分割的。

表示个人只会简单的正则表达式,难一点的还需要多学习学习…所以这里就不误人子弟了

join():

这个方法是JDK1.8新增的,就是在一个数组的各个元素前面统一增加一个前缀,然后将它们整体拼接成一个String。(这个感觉就是少些点StringBuilder之类的代码)。

equals():

所以它是比较两个字符串内容是否相同的方法

这里顺便说下“==”:

如果是在编译期就确定或者直接使用字符串常量的话,那么两个String对象的引用是相等的,否则就是不相等的。例如:

String s0 = “abc”

String s1 = “ab”+“c”

String s2 = “abc”       那么 s0==s1==s2

String s3 = new String(“abc”),那么s3和上面的0/1/2引用时不同的,虽然实际底层指向的都是常量池中的常量!

使用new String()创建的字符串不是常量,不能在编译期就确定,所以在堆中会有自己的地址空间。

compareTo(String anotherString):

重写了Comparable接口中的方法,用于比较两个字符串的大小,实现这个接口也意味着该类支持排序。

返回的0表示两个字符串完全一样

返回整数表示当前字符串比较大,大指的是当前的字符串ASCII码值比较大或者字符串长度比较长

hashCode():

计算字符串的hash码,代码其实蛮简单的,并且注释中已经说明计算hash码的公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],但为啥是31?

为啥是31的话可以看下参考中给出的stackoverflow的链接,这里就不搬他人的研究成果了,大致上说下原因有以下两个:

1. 31可以被JVM优化,31*i=(i<<5)-i

2. 31是一个不大不小的质数,如果这个质数太小的话,会增加hash冲突的风险(计算出来的值小),质数大的话会很容易溢出,导致信息丢失,31这个数刚刚好可以hash的够散。

再来简单分析下StringBuilder和StringBuffer的源码:

看下源码就知道,两者所提供的方法基本都是调用的都是父类AbstractStringBuilder提供的方法,两者最大的不同是StringBuffer的每一个方法几乎都有一个synchronize修饰(对象锁,因为方法不是static的)。

底层和String一样,也是将内容保存在数组当中,但是这个数组没有final修饰,即数组中的内容是可变的。它的方法应该就是修改数组中的内容,随便找一个方法来分析下。

append():

底层是调用System.arraycopy()完成数组拷贝了,其他的方法也差不多都是调用的arraycopy()方法,这里就不再一一分析了。

参考:

https://www.cnblogs.com/Wilange/p/7570633.html(三者的类结构图)

http://www.cnblogs.com/chenssy(字符串常量在JVM中的分布)

https://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier(计算hashCode为什么选31)

个人对String,StringBuffer,StringBuilder的一些理解相关推荐

  1. `java`学习笔记(十二)`Java`--`String``StringBuffer``StringBuilder`

    Java–String&&StringBuffer&&StringBuilder 文章目录 `Java`--`String`&&`StringBuffe ...

  2. 116day(String,StringBuffer,StringBuilder,模拟器和虚拟机的区别,复合命题的种类)

    <2018年2月4日>[连续116天] 标题:String,StringBuffer,StringBuilder,模拟器和虚拟机的区别,复合命题的种类: 内容: A.详见http://bl ...

  3. String StringBuffer StringBuilder的区别

    简单的事情做到极致也是一种能力. 首先,说一下目前我还没进行整理的情况下,我的理解: String 不可修改 修改后相当于又新创建创建一个字符串 比如: String a = "abc&qu ...

  4. String,StringBuffer,StringBuilder三者的异同

    String,StringBuffer,StringBuilder三者的异同:? String:不可变的字符序列;底层使用char[]存储(用final修饰) StringBuffer:可变的字符序列 ...

  5. 重温java中的String,StringBuffer,StringBuilder类

    不论什么一个系统在开发的过程中, 相信都不会缺少对字符串的处理. 在 java 语言中, 用来处理字符串的的类经常使用的有 3 个: String.StringBuffer.StringBuilder ...

  6. 浅谈 Java 字符串(String, StringBuffer, StringBuilder)

    我们先要记住三者的特征: String 字符串常量 StringBuffer 字符串变量(线程安全) StringBuilder 字符串变量(非线程安全) 一.定义 查看 API 会发现,String ...

  7. String, StringBuffer, StringBuilder之间的区别

    String与StringBuffer/StringBuilder之间的主要区别 1.String对象不可变, 如果修改会重新创建一个对象, 然后把值保存进去. StringBuffer/String ...

  8. java中 String StringBuffer StringBuilder的区别

    * String类是不可变类,只要对String进行修改,都会导致新的对象生成. * StringBuffer和StringBuilder都是可变类,任何对字符串的改变都不会产生新的对象. 在实际使用 ...

  9. String, StringBuffer,StringBuilder

    String, StringBuffer,StringBuilder的区别 java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,都实现了CharSe ...

最新文章

  1. java安装傻瓜_安装Java傻瓜式教程
  2. Android APK应用安装原理(2)-查找APK并自动安装
  3. 2016-2017-1 《信息安全系统设计基础》 学生博客及Git@OSC 链接
  4. Linux 里有/lib和/usr/lib,这两个目录下的库文件有什么区别吗?如果没区别为什么又要分开放呢...
  5. java 怎么判断1_1.0_1.00是同一个数_php如何优雅的判断 0.0/0.00/0.000 是否为空? 测试过用empty函数不行...
  6. reac——父组件向子组件传递值,子组件何时能同步获得父组件改变后的值
  7. [蓝桥杯2018初赛]全球变暖-dfs,bfs,连通块
  8. python类定义的讲解_python面向对象、自定义类等使用实例讲解
  9. JAVA获得天气json数据的方法,获取从天气预报接口返回回来的json数据
  10. mongodb数据库扩展名_MongoDB如何存储数据
  11. 【MySQL】MySQL审计操作记录
  12. js页面跳转 和 js打开新窗口 方法 【转】
  13. 同一账号,后一用户登录,前一个用户则被踢掉
  14. ps投影怎么做之教程:人像投影和物体长投影制作
  15. pytorch之transforms
  16. 第十二课 Java基础篇——面向对象进阶(三)
  17. “百度杯”CTF比赛 十一月场Look
  18. 美国习惯用语flip out的中英文翻译解释和例子
  19. 高等数学学习笔记——第四十九讲——一阶常微分方程的求解
  20. 华为员工必选题:做奋斗者,还是劳动者?

热门文章

  1. Python 音频处理以及可视化 Amplitude,MFCC,Mel Spectrogram, librosa 库
  2. ppt测试什么软件,PDF转PPT软件哪个好测试
  3. redis 统计数量_使用redis实现在线人数统计
  4. Axure交互-选中状态实现登录和注册Tab页切换
  5. ChromeFK插件推荐系列三:购物助手插件推荐
  6. signature=988b478f2af5e3a20272ed9c171b1a97,Problems of Conquering Everest
  7. 使用RetroPie构建复古游戏机
  8. Bzoj4899--记忆的轮廓
  9. 地球坐标,火星坐标,百度坐标转换C++
  10. Spring Aop基础使用