为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

public java.lang.String addMethod();

Code:

0: ldc #16 // String

2: astore_1

3: iconst_0

4: istore_2

5: iload_2

6: ldc #17 // int 100000

8: if_icmpge 41

11: new #7 // class java/lang/StringBuilder

14: dup

15: invokespecial #8 // Method java/lang/StringBuilder.""😦)V

18: aload_1

19: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

22: iload_2

23: invokevirtual #18 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

26: ldc #19 // String wupx

28: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

31: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

34: astore_1

35: iinc 2, 1

38: goto 5

41: aload_1

42: areturn

可以看出,第 8 行到第 38 行构成了一个循环体:在第 8 行的时候做条件判断,如果不满足循环条件,则跳转到 41 行。编译器做了一定程度的优化,在 11 行 new 了一个 StringBuilder 对象,然后再 19 行、23 行、28 行进行了三次 append() 方法的调用,不过每次循环都会重新 new 一个 StringBuilder 对象。

再来看 stringBuilderMethod() 方法的字节码:

public java.lang.String stringBuilderMethod();

Code:

0: new #7 // class java/lang/StringBuilder

3: dup

4: invokespecial #8 // Method java/lang/StringBuilder.""😦)V

7: astore_1

8: iconst_0

9: istore_2

10: iload_2

11: ldc #17 // int 100000

13: if_icmpge 33

16: aload_1

17: iload_2

18: invokevirtual #18 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

21: ldc #19 // String wupx

23: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

26: pop

27: iinc 2, 1

30: goto 10

33: aload_1

34: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

37: areturn

13 行到 30 行构成了循环体,可以看出,在第4行(循环体外)就构建好了 StringBuilder 对象,然后再循环体内只进行 append() 方法的调用。

由此可以看出,在 for 循环中,使用 + 进行字符串拼接,每次都是 new 了一个 StringBuilder,然后再把 String 转成 StringBuilder,再进行 append,而频繁的新建对象不仅要耗费很多时间,还会造成内存资源的浪费。这就从字节码层面解释了为什么不建议在循环体内使用 + 去进行字符串的拼接。

接下来再来让我们看下使用 + 或者 StringBuilder 拼接字符串的原理吧。

2、使用 + 拼接字符串

================

在 Java 开发中,最简单常用的字符串拼接方法就是直接使用 + 来完成:

String boy = “wupx”;

String girl = “huxy”;

String love = boy + girl;

反编译后的内容如下:(使用的反编译工具为 jad)

String boy = “wupx”;

String girl = “huxy”;

String love = (new StringBu

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

ilder()).append(boy).append(girl).toString();

通过查看反编译以后的代码,可以发现,在字符串常量在拼接过程中,是将 String 转成了 StringBuilder 后,使用其 append() 方法进行处理的。

那么也就是说,Java中的 + 对字符串的拼接,其实现原理是使用 StringBuilder 的 append() 来实现的,使用 + 拼接字符串,其实只是 Java 提供的一个语法糖。

**3、**使用 StringBuilder 拼接字符串

==================================

StringBuilder 的 append 方法就是第二个常用的字符串拼接姿势了。

和 String 类类似,StringBuilder 类也封装了一个字符数组,定义如下:

char[] value;

与 String 不同的是,它并不是 final 的,所以是可以修改的。另外,与 String 不同,字符数组中不一定所有位置都已经被使用,它有一个实例变量,表示数组中已经使用的字符个数,定义如下:

int count;

其 append() 方法源码如下:

public StringBuilder append(String str) {

super.append(str);

return this;

}

该类继承了 AbstractStringBuilder 类,看下其 append() 方法:

public AbstractStringBuilder append(String str) {

if (str == null)

return appendNull();

int len = str.length();

ensureCapacityInternal(count + len);

str.getChars(0, len, value, count);

count += len;

return this;

}

首先判断拼接的字符串 str 是不是 null,如果是,调用 appendNull() 方法进行处理,appendNull() 方法的源码如下:

private AbstractStringBuilder appendNull() {

int c = count;

ensureCapacityInternal(c + 4);

final char[] value = this.value;

value[c++] = ‘n’;

value[c++] = ‘u’;

value[c++] = ‘l’;

value[c++] = ‘l’;

count = c;

return this;

}

如果字符串 str 不为 null,则判断拼接后的字符数组长度是否超过当前数组长度,如果超过,则调用 Arrays.copyOf() 方法进行扩容并复制,ensureCapacityInternal() 方法的源码如下:

private void ensureCapacityInternal(int minimumCapacity) {

if (minimumCapacity - value.length > 0) {

value = Arrays.copyOf(value,

newCapacity(minimumCapacity));

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值