一篇与众不同的 String、StringBuilder 和 StringBuffer 详解(一)

简介: 这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C++、Visual Basic、C# 等。

碎碎念



这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C++、Visual Basic、C# 等。字符串使用 String 来表示,字符串一旦被创建出来就不会被修改,当你想修改 StringBuffer 或者是 StringBuilder,出于效率的考量,虽然 String 可以通过 + 来创建多个对象达到字符串拼接的效果,但是这种拼接的效率相比 StringBuffer 和 StringBuilder,那就是心有余而力不足了。本篇文章我们一起来深入了解一下这三个对象。



简单认识这三个对象


String

String 表示的就是 Java 中的字符串,我们日常开发用到的使用 "" 双引号包围的数都是字符串的实例。String 类其实是通过 char 数组来保存字符串的。下面是一个典型的字符串的声明

String s = "abc";

上面你创建了一个名为 abc 的字符串。

字符串是恒定的,一旦创建出来就不会被修改,怎么理解这句话?我们可以看下 String 源码的声明

微信图片_20220414201808.png

告诉我你看到了什么?String 对象是由final 修饰的,一旦使用 final 修饰的类不能被继承、方法不能被重写、属性不能被修改。而且 String 不只只有类是 final 的,它其中的方法也是由 final 修饰的,换句话说,Sring 类就是一个典型的 Immutable 类。也由于 String 􏰆的不可变性,类似字符串拼接、字符串截取等操作都会产生新的 Strign 对象。

所以请你告诉我下面

String s1 = "aaa";
String s2 = "bbb" + "ccc";
String s3 = s1 + "bbb";
String s4 = new String("aaa");

分别创建了几个对象?

微信图片_20220414201812.png

  • 首先第一个问题,s1 创建了几个对象。字符串在创建对象时,会在常量池中看有没有 aaa 这个字符串;如果没有此时还会在常量池中创建一个;如果有则不创建。我们默认是没有的情况,所以会创建一个对象。下同。
  • 那么 s2 创建了几个对象呢?是两个对象还是一个对象?我们可以使用 javap -c 看一下反汇编代码
public class com.sendmessage.api.StringDemo {
  public com.sendmessage.api.StringDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // 执行对象的初始化方法
       4: return
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // 将 String aaa 执行入栈操作
       2: astore_1                          # pop出栈引用值,将其(引用)赋值给局部变量表中的变量 s1
       3: ldc           #3                  // String bbbccc
       5: astore_2
       6: return
}

编译器做了优化 String s2 = "bbb" + "ccc" 会直接被优化为 bbbccc。也就是直接创建了一个 bbbccc 对象。

javap 是 jdk 自带的反汇编工具。它的作用就是根据 class 字节码文件,反汇编出当前类对应的 code 区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

javap -c 就是对代码进行反汇编操作。

  • 下面来看 s3,s3 创建了几个对象呢?是一个还是两个?还是有其他选项?我们使用 javap -c 来看一下

微信图片_20220414201818.png

我们可以看到,s3 执行 + 操作会创建一个 StringBuilder 对象然后执行初始化。执行 + 号相当于是执行 new StringBuilder.append() 操作。所以

String s3 = s1 + "bbb";
==
String s3 = new StringBuilder().append(s1).append("bbb").toString();
// Stringbuilder.toString() 方法也会创建一个 String 
public String toString() {
  // Create a copy, don't share the array
  return new String(value, 0, count);
}

所以 s3 执行完成后,相当于创建了 3 个对象。

  • 下面来看 s4 创建了几个对象,在创建这个对象时因为使用了 new 关键字,所以肯定会在堆中创建一个对象。然后会在常量池中看有没有 aaa 这个字符串;如果没有此时还会在常量池中创建一个;如果有则不创建。所以可能是创建一个或者两个对象,但是一定存在两个对象。

微信图片_20220414201824.png

说完了 String 对象,我们再来说一下 StringBuilder 和 StringBuffer 对象。

上面的 String 对象竟然和 StringBuilder 产生了千丝万缕的联系。不得不说 StringBuilder 是一个牛逼的对象。String 对象底层是使用了 StringBuilder 对象的 append 方法进行字符串拼接的,不由得对 StringBuilder 心生敬意。

微信图片_20220414201827.png

不由得我们想要真正认识一下这个 StringBuilder 大佬,但是在认识大佬前,还有一个大 boss 就是 StringBuffer 对象,这也是你不得不跨越的鸿沟。

微信图片_20220414201830.png

StringBuffer

StringBuffer 对象 代表一个可变的字符串序列,当一个 StringBuffer 被创建以后,通过 StringBuffer 的一系列方法可以实现字符串的拼接、截取等操作。一旦通过 StringBuffer 生成了最终想要的字符串后,就可以调用其 toString 方法来生成一个新的字符串。例如

StringBuffer b = new StringBuffer("111");
b.append("222");
System.out.println(b);

我们上面提到 + 操作符连接两个字符串,会自动执行 toString() 方法。那你猜 StringBuffer.append 方法会自动调用吗?直接看一下反汇编代码不就完了么?

微信图片_20220414201834.png

上图左边是手动调用 toString 方法的代码,右图是没有调用 toString 方法的代码,可以看到,toString() 方法不像 + 一样自动被调用。

StringBuffer 是线程安全的,我们可以通过它的源码可以看出

微信图片_20220414201837.png

StringBuffer 在字符串拼接上面直接使用 synchronized 关键字加锁,从而保证了线程安全性。

StringBuilder

最后来认识大佬了,StringBuilder 其实是和 StringBuffer 几乎一样,只不过 StringBuilder 是非线程安全的。并且,为什么 + 号操作符使用 StringBuilder 作为拼接条件而不是使用 StringBuffer 呢?我猜测原因是加锁是一个比较耗时的操作,而加锁会影响性能,所以 String 底层使用 StringBuilder 作为字符串拼接。

微信图片_20220414201841.png


相关文章
|
3月前
|
安全
String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。 StringBuffer可变并且线程安全;有一定缓冲区容量,字符串大小没超过容量,不会重新分配新的容量,适合多线程操作字符串; StringBuiler可变并且线程不安全。速度比StringBuffer更快,适合单线程操作字符串。 操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer
|
5月前
|
安全 Java
String、StringBuffer、StringBuilder的区别
这篇文章讨论了Java中String、StringBuffer和StringBuilder的区别。String是不可变的,每次操作都会产生新的对象,效率低且浪费内存。StringBuilder可以在原字符串基础上进行操作,不开辟额外内存,弥补了String的缺陷。StringBuffer和StringBuilder类似,但StringBuffer的方法是线程安全的。文章还列举了StringBuffer的常用方法,并提供了使用示例代码。最后总结了这三者的主要区别。
String、StringBuffer、StringBuilder的区别
|
4月前
|
canal 安全 索引
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
52 5
|
4月前
|
存储 安全 Java
String、StringBuffer 和 StringBuilder 的区别
【10月更文挑战第21天】String、StringBuffer 和 StringBuilder 都有各自的特点和适用场景。了解它们之间的区别,可以帮助我们在编程中更合理地选择和使用这些类,从而提高程序的性能和质量。还可以结合具体的代码示例和实际应用场景,进一步深入分析它们的性能差异和使用技巧,使对它们的理解更加全面和深入。
85 0
|
5月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
57 0
java基础(13)String类
|
3月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
84 2
|
4月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
83 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
4月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
91 2
|
5月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
4月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
41 1