这篇文章也是Java中关于字符串知识的补充,主要介绍关于字符串拼接的知识。本文基于jdk1.8.0_181。1、字符串拼接字符串拼接是我们在Java代码中经常做的事情,即将多个字符串拼接在一起。我们都知道String在Java中是一个不可变类,一旦实例化就不能修改。不可变类的实例一旦创建,其成员变量的值就无法修改。这种设计有很多优点,比如hashcode可以缓存,使用更方便,更安全。但是由于字符串是不可变的,那么字符串连接呢?1.字符串不变性和字符串拼接其实所谓的字符串拼接就是重新生成一个新的字符串。下面一段字符串拼接代码:其实我们得到的s已经是一个新的字符串了。下图中保存的是对重新创建的String对象的引用。那么,在Java中,如何进行字符串拼接呢?连接字符串的方法有很多种,这里介绍几种比较常用的。2、使用+拼接字符串在Java中,最简单的字符串拼接方式就是直接使用符号+进行拼接。例如:这里有一个特殊的点,有人将Java中使用+拼接字符串的作用理解为运算符重载。其实不是,Java不支持运算符重载。这其实只是Java提供的一个语法糖。稍后我会详细介绍。运算符重载:在计算机编程中,运算符重载是一种多态性。运算符重载就是重新定义一个已有的运算符,赋予它另一种功能,以适应不同的数据类型。句法糖:句法糖,又译为糖衣语法,是英国计算机科学家彼得·兰丁发明的一个术语,是指在计算机语言中加入的某种语法,对语言的功能没有任何影响。但是程序员使用起来更方便。语法糖使程序更简洁,更易读。3、concat除了可以使用+来拼接字符串,还可以使用String类中的concat方法来拼接字符串。如:4.StringBuffer关于字符串,Java中除了定义了一个String类可以用来定义字符串常量之外,还提供了一个StringBuffer类可以用来定义字符串变量。它的对象可以扩展和修改。.使用StringBuffer方便地连接字符串。例如:5、StringBuilder除了StringBuffer之外,还有一个类StringBuilder也可以使用,其用法与StringBuffer类似。如:6、StringUtils.join除了JDK内置的字符串拼接方法外,还可以使用一些开源库中提供的字符串拼接方法名,比如apache.commons中提供的StringUtils类,其中join方法可以拼接字符串。这里简单说一下,StringUtils中提供的join方法的主要作用是:将数组或集合用一个拼接字符拼接在一起,形成一个新的字符串,如:而Java8中的String类也提供了一个静态的join方法,用法类似于StringUtils.join。以上就是Java中常用的五种字符串拼接方法,那么用哪一种比较好呢?阿里巴巴Java开发手册为什么不建议在循环体中使用+进行字符串拼接?(阿里巴巴Java开发手册关于字符串拼接)下面我们来分析一下以上五种方法的底层原理,再分析到底哪种更好。2、使用+拼接字符串的实现原理前面说过,使用+拼接字符串其实只是Java提供的一个语法糖。那么,我们来解决一下这个语法糖,看看它的内部原理是什么。如何实现。还是这么一段代码。我们反编译他生成的字节码看看结果。反编译后的内容如下,反编译工具为jad。通过查看反编译后的代码可以发现,原来的字符串常量是通过将String转成StringBuilder并在拼接过程中使用其append方法进行处理的。那么也就是说,Java中拼接字符串的原理就是使用StringBuilder.append。3.concat是如何实现的我们来看看concat方法的源码,看看这个方法是如何实现的。这段代码首先创建一个字符数组,其长度为现有字符串和要拼接的字符串的长度之和,然后将两个字符串的值复制到一个新的字符数组中,并使用这个字符数组创建一个新的字符串对象并返回。通过源码我们也可以看到,在concat方法之后,其实是new了一个String,这也呼应了我们前面说的字符串的不变性。四、StringBuffer和StringBuilder接下来,我们看一下StringBuffer和StringBuilder的实现原理。与String类类似,StringBuilder类也封装了一个字符数组,定义如下:与String不同的是,它不是final的,所以可以修改。此外,与String不同的是,并非字符数组中的所有位置都已被使用。它有一个实例变量,指示数组中使用的字符数。定义如下:append源码如下:该类继承AbstractStringBuilder类,见下,append方法:append会直接将字符复制到内部字符数组中,如果字符数组的长度不是够了,会加长。StringBuffer类似于StringBuilder。最大的区别是StringBuffer是线程安全的。看一下StringBuffer的append方法。这个方法是用synchronized声明的,说明它是一个线程安全的方法。并且StringBuilder不是线程安全的。5.StringUtils.join是如何实现的通过查看StringUtils.join的源码可以发现,它其实是通过StringBuilder来实现的。6.效率比较既然字符串拼接的方法有那么多种,那么哪种方法效率最高呢?让我们做一个简单的比较。我们使用上面表格中的代码来测试接下来的五个字符串连接代码的运行时间。结果如下:从结果可以看出,从短到长的对比是:StringBuffer在StringBuilder的基础上做了同步处理,所以会消耗更多的时间,这个很好理解。StringUtils.join也是用了StringBuilder,里面还有很多其他的操作,所以耗时比较长,这个也很好理解。其实StringUtils.join更擅长处理字符串数组或者列表的拼接。那么问题来了,我们之前分析过,其实使用+拼接字符串的实现原理也是使用StringBuilder,那么为什么结果相差那么大,1000多次呢?我们再反编译一下下面的代码:反编译后的代码如下:我们可以看到反编译后的代码,在for循环中,每次都是new一个StringBuilder,然后把String转成StringBuilder,然后进行append。当然,频繁的新对象会花费很多时间。不仅会耗费时间,而且频繁创建对象还会造成内存资源的浪费。因此阿里巴巴Java开发手册推荐:在循环体中,使用StringBuilder的append方法扩展字符串的连接方法。而不是使用+。7.小结本文介绍什么是字符串拼接。虽然字符串是不可变的,但是仍然可以通过创建新的字符串来拼接字符串。常用的字符串拼接方法有五种,分别是using+、usingconcat、usingStringBuilder、usingStringBuffer和usingStringUtils.join。由于在字符串拼接的过程中会创建新的对象,如果要在循环体中进行字符串拼接,则必须考虑内存和效率问题。因此,经过对比,我们发现直接使用StringBuilder的方式是最高效的。因为StringBuilder的目的是定义可变字符串和字符串更改操作。不过需要强调的是:1、如果在循环体中不进行字符串拼接,直接使用+即可。2.如果在并发场景下进行字符串拼接,请使用StringBuffer,而不是StringBuilder。【本文为专栏作家霍利斯原创文章,作者微信公众号Hollis(ID:hollishuang)】点此阅读更多本作者好文
