Java String,StringBuffer与StringBuilder

1 不可变的String

在 Java中,字符串属于对象,Java提供了 String类 来创建和操作字符串
通过查看 String源码,可以发现

  • String类是 final类,并且它的成员方法都默认为final方法
  • String类是通过 char数组来保存字符串的
  • String类的 sub,concat,replace等操作,都不是在原有字符串上进行的,而是重新生成了一个新的字符串,即进行这些操作后,最原始的字符串并没有被改变
char[] helloArray = {'h','e','l','l','0'};
String helloString = new String(helloString);

String对象是不可变的
String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串的内容。

String str1 = "java";
String str2 = "java";
System.out.println(str1 == str2); // true

在代码中,可以创建同一个String对象的多个别名,而它们所指向的对象是相同的,一直呆在一个单一的物理位置上,从未动过。
在这里插入图片描述

内存分析
String str = "hello"; str = str+"world";;
初始String值为“hello”,然后在这个字符串后面加上新的字符串“world”,这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“hello world”字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费。

2 StringBuffer和StringBuilder

当对字符串进行修改的时候,需要使用 StringBuffer与 StringBuilder类。
和 String类不同的是,StringBuffer和StringBuilder类的对象能够被多次修改,并且不产生新的未使用的对象
通过查看源码发现,StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相同,区别是 StringBuffer类的成员方法前面多了一个关键字:synchronized。即 StringBuffer是线程安全的,但是 StringBuilder的访问速度更快,因此,一般用 StringBuilder.

注意:
String 可以赋空值,StringBuilder,StringBuffer不行,它们是对象,必须先 new,获得具体对象再使用
StringBuffer s = null; //结果警告:Null pointer access: The variable result can only be null at this location
StringBuffer s = new StringBuffer();//StringBuffer对象是一个空的对象

StringBuffer s = new StringBuffer(“abc”);//创建带有内容的StringBuffer对象,对象的内容就是字符串”

3 String,StringBuffer,StringBuilder三者的执行效率

一般情况下,StringBuilder > StringBuffer > String

不是所有情况下都这样
比如:String str = “hello”+“world”; 的效率,就比 StringBuilder sBuilder = new StringBuilder().append(“hello”).append(“world”); 要高
因此,这三个类各有利弊,应当根据不同的情况来进行选择使用

  • 当字符串相加操作或者改动较少的情况下,建议使用 String str = “hello”;这种形式
  • 当字符串相加操作较多的情况下,建议使用 StringBuilder,如果采用了多线程,则使用 StringBuffer
4 String的重载 “+”

在 Java中,唯一被重载的运算符就是用于 String的 "+" 与"+="
除此之外,Java不允许程序员重载其他的运算符

public class StringTest {
    String a = "abc";
    String b = "mongo";
    String info = a + b + 47;
}

String对象是不可变的,所以在上述的代码过程中可能会是这样工作的:

(1)"abc" + "mongo"创建新的String对象abcmongo;

(2)"abcmongo" + "47"创建新的String对象abcmongo47;

(3)引用info 指向最终生成的String。

但是这种方式会生成一大堆需要垃圾回收的中间对象,性能相当糟糕。

4.1 编译器的优化处理

通过反编译代码会发现,编译器自动引入了StringBuilder类
编译器创建了一个StringBuilder对象,并调用StringBuilder.append()方法,最后调用toString()生成结果,从而避免中间对象的性能损耗。


编译器优化String对象的连接,而下面这种情况会直接连接作为常量。

public class StringTest {
    String info = "Andy" + "24" + "Developer";
}
4.2 编译器的优化是有限度的

性能较低的代码

public void  implicitUseStringBuilder(String[] values) {
      String result = "";
      for (int i = 0 ; i < values.length; i ++) {
          result += values[i];
      }
      System.out.println(result);
 }

通过反编译发现,StringBuilder对象创建发生在循环之间,也就是意味着有多少次循环就会创建多少个StringBuilder对象,这样明显性能较低。


性能较高的代码

 public void explicitUseStringBuider(String[] values) {
      StringBuilder result = new StringBuilder();
      for (int i = 0; i < values.length; i ++) {
          result.append(values[i]);
      }
  }

StringBuilder的创建,位于循环之外,所以不会多次创建 StringBuilder


综上,循环体中需要尽量避免隐式或者显式创建StringBuilder。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值