基本内容
不可变的String
String中的+操作符
StringBuilder和StringBuffer
String在内存中的位置
1 不可变的String
String的对象是不可变的,如下:
在上述代码中,实际上分别创建了abc和def两个字符串,str只是由指向abc的引用变为指向def的引用。
我们来看下Java中String类的源代码
我们可以看到,其实String类在Java内部是以final修饰的字符数组形式存储的。这也就表明了,String只会初始化一次,并且不可被继承。
我们通过源代码再来看下对String进行分割,合并等操作后String是否会发生变化。
可以看出,其实在这些操作后都生成了新的字符串。
2 String中的+操作符
Java中是不允许程序员对操作符重载的,但是Java自身对+操作符进行了重载。
在String的操作中,+操作符表示字符串连接。
我们反编译上述代码
我只截取了反编译后的部分内容。
可以看出在Java的内部实现中,+操作是以StringBuilder的形式实现的。
注意:
ldc,将int, float或String型常量值从常量池中推送至栈顶,在这里是查找java字符串推送至栈顶。
astore_1,将栈顶引用型数值存入第二个本地变量,在这里就是将java存储到本地变量。
new,创建一个对象,并将其引用值压入栈顶。
dup,复制栈顶数值并将复制值压入栈顶。
invokespecial,调用方法,注意观察注释中调用的方法名。
aload_1,将第二个引用类型本地变量推送至栈顶
其实这个过程就是:
在常量池中查找java字符串,并推送至栈顶。
将java存储到第二个本地变量中。
创建StringBuilder类型对象,并推送至栈顶。
在常量池中查找hello字符串,并推送至栈顶。
调用方法初始化StringBuilder
调用StringBuilder的append()方法,合并字符串。
调用StringBuilder的toString()方法,将StringBuilder转换为String。
在String使用+连接字符串的时候,创建了很多String对象,这无疑会影响到效率。
3 StringBuilder和StringBuffer
使用StringBuilder/StringBuffer的意义无非就是为了提高操作字符串的效率。
我们先看下源码中关于这两个类的声明。
都是继承自抽象类AbstractStringBuilder,都熟悉点了Serializable和CharSequence接口。
其实不仅仅是类的声明,StringBuilder/StringBuffer其实是在功能上是完全相同的,只是StringBuffer中的方法大多被synchronized修饰,因此是线程安全的,而其实不仅仅是类的声明,StringBuilder不是线程安全的。
我们使用《Thinking in Java》中的例子来探究下StringBuiler是否真的能提升效率。
我们先提供两个方法
我们反编译后上述代码
我们只保留两个方法中的循环部分。
注意:
goto,无条件跳转到指定位置。
if_icmplt,比较栈顶两int型数值大小,当结果小于0时跳转到指定位置。
我们明白了这两项以后就可以看出,String使用+连接字符串的时候,每次都会创建StringBuilder对象,而这肯定是会影响执行效率的。
最后,我想说一些关于StringBuilder和StringBuffer的实现原理的内容。
我们来看它们的父类AbstractStringBuilder的源代码(只截取一部分)。
我们可以看到,AbstractStringBuilder内部也是以字符数组实现的。注意count是指实际长度,而capacity指的是容量,注意它们的区别。
以上是扩容的两个方法,原理很简单,大家可以自己思考下。还有个trimToSize()方法,是缩减容量的。
至于StringBuilder和StringBuffer所实现的方法,在这里不多说,大家可以去看API进行学习。
4 String在内存中的位置
之前我在【CoreJava】equals()和==的比较中提到过String的存放位置,但只是稍微讲了下关于两种创建方式存储位置的不同。
我们使用两种种方式创建字符串
我们反编译上面的代码
可以看出两种方式都是首先从字符创常量池中寻找abc,但是它们的区别在哪呢?
两种创建方式都是首先在字符串常量池中寻找abc,找不到则创建abc。
str1是存储在栈中的地址引用,指向字符串常量池中的abc。
创建str2的时候,则首先在堆中创建abc,指向字符串常量池中的abc,再在栈中创建str2,指向堆中的abc。
这就是我们为什么推荐使用
字符串的内容也基本结束了,《Thinking in Java》中还补充了正则表达式和格式化输出的一些内容。正则表达式可以作为一整篇文章来讲,而且我自己目前也不太熟练,所以就不在这多说了。格式化输出呢,我用的比较少,平时用的也不多,所以也没有怎么研究过。毕竟脑子有限,还是要有一些侧重的。
领取专属 10元无门槛券
私享最新 技术干货