首页
学习
活动
专区
工具
TVP
发布

Chapter13-字符串

基本内容

不可变的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》中还补充了正则表达式和格式化输出的一些内容。正则表达式可以作为一整篇文章来讲,而且我自己目前也不太熟练,所以就不在这多说了。格式化输出呢,我用的比较少,平时用的也不多,所以也没有怎么研究过。毕竟脑子有限,还是要有一些侧重的。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180124G0UZ7I00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券