当前位置: 首页 > news >正文

String、StringBuilder 和 StringBuffer 详解

碎碎念

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

简单认识这三个对象

String

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

String s = "abc";

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

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

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

所以请你告诉我下面

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

分别创建了几个对象?
在这里插入图片描述

 首先第一个问题,s1 创建了几个对象。字符串在创建对象时,会在常量池中看有没有 aaa 这个字符串;如果没有此时还会在常量池中创建一个;如果有则不创建。我们默认是没有的情况,所以会创建一个对象。下同。
 那么 s2 创建了几个对象呢?是两个对象还是一个对象?我们可以使用 javap -c 看一下反汇编代码

public class com.sendmessage.api.StringDemo {public com.sendmessage.api.StringDemo();Code:0: aload_01: invokespecial #1                  // 执行对象的初始化方法4: returnpublic static void main(java.lang.String[]);Code:0: ldc           #2                  // 将 String aaa 执行入栈操作2: astore_1													# pop出栈引用值,将其(引用)赋值给局部变量表中的变量 s13: ldc           #3                  // String bbbccc5: astore_26: return
}

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

javap 是 jdk 自带的反汇编工具。它的作用就是根据 class 字节码文件,反汇编出当前类对应的 code 区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
javap -c 就是对代码进行反汇编操作。
  • 下面来看 s3,s3 创建了几个对象呢?是一个还是两个?还是有其他选项?我们使用 javap -c 来看一下

我们可以看到,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 arrayreturn new String(value, 0, count);
}

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

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

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

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

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

StringBuffer

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

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

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

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

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

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

StringBuilder

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

深入理解 String、StringBuilder、StringBuffer

 我们上面说到,使用 + 连接符时,JVM 会隐式创建 StringBuilder 对象,这种方式在大部分情况下并不会造成效率的损失,不过在进行大量循环拼接字符串时则需要注意。如下这段代码

String s = "aaaa";
for (int i = 0; i < 100000; i++) {s += "bbb";
}

这是一段很普通的代码,只不过对字符串 s 进行了 + 操作,我们通过反编译代码来看一下。

// 经过反编译后
String s = "aaa";
for(int i = 0; i < 10000; i++) {s = (new StringBuilder()).append(s).append("bbb").toString();    
}

 你能看出来需要注意的地方了吗?在每次进行循环时,都会创建一个 StringBuilder 对象,每次都会把一个新的字符串元素 bbb 拼接到 aaa 的后面,所以,执行几次后的结果如下

 每次都会创建一个 StringBuilder ,并把引用赋给 StringBuilder 对象,因此每个 StringBuilder 对象都是强引用, 这样在创建完毕后,内存中就会多了很多 StringBuilder 的无用对象。

这样由于大量 StringBuilder 创建在堆内存中,肯定会造成效率的损失,所以在这种情况下建议在循环体外创建一个 StringBuilder 对象调用 append()方法手动拼接。

例如:

StringBuilder builder = new StringBuilder("aaa");
for (int i = 0; i < 10000; i++) {builder.append("bbb");
}
builder.toString();

这段代码中,只会创建一个 builder 对象,每次循环都会使用这个 builder 对象进行拼接,因此提高了拼接效率。

String

String 还提供了一些其他方法

  • charAt :返回指定位置上字符的值
  • getChars: 复制 String 中的字符到指定的数组
  • equals: 用于判断 String 对象的值是否相等
  • indexOf : 用于检索字符串
  • substring: 对字符串进行截取
  • concat: 用于字符串拼接,效率高于 +
  • replace:用于字符串替换
  • match:正则表达式的字符串匹配
  • contains: 是否包含指定字符序列
  • split: 字符串分割
  • join: 字符串拼接
  • trim: 去掉多余空格
  • toCharArray: 把 String 对象转换为字符数组
  • valueOf: 把对象转换为字符串

StringBuilder

 StringBuilder 类表示一个可变的字符序列,我们知道,StringBuilder 是非线程安全的容器,一般适用于单线程场景中的字符串拼接操作,下面我们就来从源码角度看一下 StringBuilder。

首先我们来看一下 StringBuilder 的定义

public final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequence {...}

StringBuilder 被 final 修饰,表示 StringBuilder 是不可被继承的,StringBuilder 类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder 类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt() 方法等。

StringBuilder 实现了 2 个接口

  • Serializable 序列化接口,表示对象可以被序列化。
  • CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,例如 length()、charAt()、subSequence()、toString() 方法等。

StringBuilder 使用 AbstractStringBuilder 类中的两个变量作为元素

char[] value; // 存储字符数组int count; // 字符串使用的计数

StringBuffer

 StringBuffer 也是继承于 AbstractStringBuilder ,使用 value 和 count 分别表示存储的字符数组和字符串使用的计数,StringBuffer 与 StringBuilder 最大的区别就是 StringBuffer 可以在多线程场景下使用,StringBuffer 内部有大部分方法都加了 synchronized 锁。在单线程场景下效率比较低,因为有锁的开销。

StringBuilder 和 StringBuffer 的扩容问题

 我相信这个问题很多同学都没有注意到吧,其实 StringBuilder 和 StringBuffer 存在扩容问题,先从 StringBuilder 开始看起

首先先注意一下 StringBuilder 的初始容量

public StringBuilder() {super(16);
}

StringBuilder 的初始容量是 16,当然也可以指定 StringBuilder 的初始容量。

在调用 append 拼接字符串,会调用 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;
}

上面代码中有一个 ensureCapacityInternal 方法,这个就是扩容方法,我们跟进去看一下

private void ensureCapacityInternal(int minimumCapacity) {// overflow-conscious codeif (minimumCapacity - value.length > 0) {value = Arrays.copyOf(value,newCapacity(minimumCapacity));}
}

这个方法会进行判断,minimumCapacity 就是字符长度 + 要拼接的字符串长度,如果拼接后的字符串要比当前字符长度大的话,会进行数据的复制,真正扩容的方法是在 newCapacity 中

private int newCapacity(int minCapacity) {// overflow-conscious codeint newCapacity = (value.length << 1) + 2;if (newCapacity - minCapacity < 0) {newCapacity = minCapacity;}return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)? hugeCapacity(minCapacity): newCapacity;
}

扩容后的字符串长度会是原字符串长度增加一倍 + 2,如果扩容后的长度还比拼接后的字符串长度小的话,那就直接扩容到它需要的长度 newCapacity = minCapacity,然后再进行数组的拷贝。

总结

 本篇文章主要描述了 String 、StringBuilder 和 StringBuffer 的主要特性,String、StringBuilder 和 StringBuffer 的底层构造是怎样的,以及 String 常量池的优化、StringBuilder 和 StringBuffer 的扩容特性等。

我是好文章的搬运工

本文转载自:
作者:程序员cxuan
链接:https://juejin.cn/post/6844904181455831048

相关文章:

String、StringBuilder 和 StringBuffer 详解

碎碎念 这是一道老生常谈的问题了&#xff0c;字符串是不仅是 Java 中非常重要的一个对象&#xff0c;它在其他语言中也存在。比如 C、Visual Basic、C# 等。字符串使用 String 来表示&#xff0c;字符串一旦被创建出来就不会被修改&#xff0c;当你想修改StringBuffer 或者是 …...

windows服务器上传文件解决方案

1.说明 1.如果上传到linux系统&#xff0c;通常使用ftp相关技术&#xff0c;配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式&#xff08;常规是开启windows的ftp服务&#xff0…...

Android Studio翻译插件推介(Translation)

前言 Android Studio翻译插件适合英语水平不太好的程序员&#xff08;比如&#xff1a;我&#xff09;&#xff0c;最常用的翻译插件Translation和AndroidLocalize&#xff0c;本文主要讲解Translation&#xff0c;亲测可用。 先看看效果&#xff1a;这里是Android的API,任意选…...

DNS,DNS污染劫持,DNS加密

1. DNS&#xff08;Domain Name System&#xff09;DNS&#xff08;Domain Name System&#xff09;&#xff0c; 也叫网域名称系统&#xff0c;是互联网的一项服务。它实质上是一个 域名 和 IP 相互映射的分布式数据库.DNS&#xff08;Domain Name Server&#xff0c;域名服务…...

【Python】如何度量优秀代码——静态分析工具

静态分析工具背景有哪些静态分析工具呢度量Python代码的静态属性度量Python的生态系统代码的坏味道在类层面上在方法层面上结语背景 静态代码分析工具能够提炼出丰富的代码静态属性信息&#xff0c;这使得程序员可以对代码的复杂性、可修改性和可读性有进一步的了解。 有哪些…...

Open3D 点云高程归一化(基于2维地面点,Python版本)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前的博客中Open3D 点云高程归一化(基于地面点,Python版本)是基于三维空间进行最近地面点的查询操作,这里对其进行修改一下,将点云投影到水平面,基于二维空间进行最近地面点的查询,这种方式对一些较为陡峭的…...

动态系统的建模与分析

前言 CS小菜鸡控制理论入门 视频学习笔记 视频传送门&#xff1a;动态系统的建模与分析】9_一阶系统的频率响应_低通滤波器_Matlab/Simulink分析 拉普拉斯变换 F(s)L{f(t)}∫0∞f(t)e−stdtF(s)\mathcal{L}\{f(t)\}\int_0^\infty f(t)e^{-st}\mathrm{d}tF(s)L{f(t)}∫0∞​f(t)…...

QCC51XX---HCI log

高通在新的S3/S5以及往后新的平台上面,引入了一个新的调试功能。就是标题说的HCI log,他类似air trace那样用来分析蓝牙协议的,这样我们就可以很详细地找到通信协议之间哪个部分出了问题。以前我们都是通过抓包器抓air trace分析的,抓包器一个要几十万,学会这个功能就相当…...

Redis四 原理篇

《Redis四 原理篇》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《Redis四 原理篇》《Redis四 原理篇》1、原理篇-Redis数据结构1.1 Redis数据结构-动态字符串1.2 Redis数据结构-intset1.3 Redis数据结构-Dict1.4 Redis数据结构-ZipList1.4.1 Redis数据…...

从0开始写Vue项目-Vue实现数据渲染和数据的增删改查

从0开始写Vue项目-环境和项目搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue2集成Element-ui和后台主体框架搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue页面主体布局和登录、注册页面_慕言要努力的博客-CSDN博客从0开始写Vue项目-SpringBoot整合Mybatis-plus实现…...

AI技术的发展,人工智能对我们的生活有那些影响?

人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。 人工智能是计算机科学的一个分支&#xff0c;它企图了解智能的实质&#xff0c;并生产出一…...

Unity中的Mathf数学运算讲解(值得收藏)

Unity中的Mathf数学运算有哪些&#xff1f; Mathf.Abs(f)绝对值 计算并返回指定参数 f 绝对值 例如&#xff1a; // 输出 10 Debug.log(Mathf.Abs(-10)) Debug.log(Mathf.Abs(10))Mathf.Sin正弦 static function Sin (f : float) : float 计算并返回以弧度为单位指定的角 f 的…...

ABBYY FineReader16最新PDF图片文字识别软件

ABBYY FineReader16是非常好的一款 OCR 识别软件&#xff08;可以识别不可编辑的PDF和图片文件&#xff09;&#xff0c;操作非常简单。ABBYY FineReader 16是一款知名的OCR文字识别软件&#xff08;图片文字识别&#xff09;。ABBYY 16采用了ABBYY最新推出的基于AI的OCR技术&a…...

Leetcode14. 最长公共前缀

一、题目描述&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 示例 1&#xff1a; 输入&#xff1a;strs [“flower”,“flow”,“flight”] 输出&#xff1a;“fl” 示例 2&#xff1a; 输入&#xff1a;…...

JTT808jt1078

前言 交通部与2016年10月份推出了JT/T 1078-2016标准&#xff0c;全称是<道路运输车辆卫星定位系统视频通信协议> JTT808 808消息头内容如下表所示&#xff1a; 起始字节字段数据类型描述及要求0消息IDWORD2消息体属性WORD消息体属性格式结构图见图24终端手机号BCD[6…...

数字孪生加持,水利水电工程或将实现全生命周期管理

水利水电工程在数字孪生技术的加持&#xff0c;使得建设和运营更加高效和智能化&#xff0c;将工程中各种元素、过程和系统数字化&#xff0c;并建立数字孪生模型&#xff0c;以实现工程建设和运营的智能化管理。数字孪生对水利水电实现对工程建设的全生命周期管理&#xff0c;…...

RA4M2开发(3)----读取ISL29035数据,并在OLED上显示,串口打印

概述 HS3003是一种数字式温湿度传感器&#xff0c;可以测量环境中的温度和湿度。读取HS3003的数据需要连接传感器到一个数据采集系统&#xff0c;一般是微处理器或者单片机。以下是一个简单的读取HS3003数据的概述&#xff1a; 连接电路&#xff1a;将HS3003传感器连接到微处…...

密码复杂度

检查账户认证失败次数限制 修复建议&#xff1a; 配置SSH方式账户认证失败次数限制 编辑/etc/pam.d/sshd文件 在auth行下方添加&#xff1a; auth required pam_tally.so deny5 unlock_time600 no_lock_time 在account行下方添加&#xff1a; account required pam_tally.s…...

Python打印() 中的 SEP 参数

默认情况下&#xff0c;Python 中 print&#xff08;&#xff09; 函数的参数之间的分隔符是空格&#xff08;softspace 功能&#xff09;&#xff0c;可以根据我们的选择对其进行修改并设置为任何字符、整数或字符串。“sep”参数用于实现相同的目的&#xff0c;它仅在python …...

AG9300方案替代|替代AG9300设计Type-C转VGA方案|CS5260设计原理图

AG9300方案替代|替代AG9300设计Type-C转VGA方案|CS5260设计原理图 安格 AG9300是一款实现USB TYPE-C到VGA数据的单片机解决方案转换器。ALGOLTEK AG9300支持USB Type-C显示端口交替模式&#xff0c;AG9300可以将视频和音频流从USB Type-C接口传输到VGA端口。在AG9300&#xff0…...

力扣-文章浏览

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1148. 文章浏览二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.其他总结前言 一、题目&#xff1a;1148…...

Unity提取场景中的静态文本

有些单机项目开发的时候没有做本地文本配置文件&#xff0c;全部写死在场景的对象上面&#xff0c;简单记录一下怎么提取场景里面的文本并且写入到配置文件里面using System.Collections.Generic;using System.IO;using TMPro;using UnityEditor;using UnityEngine;using Unity…...

Netty常用核心类说明

MessageToByteEncoder MessageToByteEncoder是一个抽象编码器&#xff0c;子类可重写encode方法把对象编码为ByteBuf输出。 MessageToByteEncoder继承自ChannelOutboundHandlerAdapter&#xff0c;encode在出站是被调用。 public class MyMessageEncoder extends MessageToB…...

ingress服务

user.default.svc.cluster.local是集群内部service的dns地址&#xff0c;集群外部想访问集群里面的service&#xff0c;可以通过LoadBalaner和NodePort。LoadBalaner可以获得一个公网ip&#xff1b;NodePort在宿主机上开一个端口&#xff0c;访问这个端口会把报文实际的转发到集…...

java 抽象类 详解

目录 一、抽象类概述&#xff1a; 二、抽象方法 : 1.概述 : 2.应用 : 3.特点 : 三、抽象类特点 : 1.关于abstract关键字 : 2.抽象类不能被实例化&#xff0c;只能创建其子类对象 : 3.抽象类子类的两个选择 &#xff1a; 四、抽象类的成员 : 1.成员变量 : 2.成员方…...

MySQL的安装(详解)

文章目录前言一、yum方式安装1、下载并安装MySQL2、 启动MySQL数据库3、查看MySQL初始密码4、登录数据库5、修改MySQL默认密码6、授予root用户远程管理权限7、输入exit退出数据库二、rpm安装方式1、检查2、卸载mariadb3、安装4、启动5、密码总结前言 本教程为Linux下安装mysql的…...

界面控件DevExpress WinForm——轻松构建类Visual Studio UI(二)

DevExpress WinForm拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜任…...

BabylonJS之放烟花

一&#xff1a; 技术调研 1. 方案一&#xff1a;ParticleSystem 用ParticleSystem来实现每一束的烟花效果&#xff0c;如果浏览器支持WebGL2功能&#xff0c;使用GPUParticleSystem性能会有极大的提升。 优点&#xff1a; 烟花效果易实现且效果好。 缺点&#xff1a; 每一个P…...

vue3 布局样式的原理

style scoped <style scoped > 它的 CSS 只作用于当前组件中的元素&#xff0c;如果子组件只有一个根元素&#xff0c;也会被渗透 原理&#xff1a; 当我们再组建中使用scoped时&#xff0c;vue会自动为组件中所有元素生成一个随机的属性&#xff0c;形如&#xff1a;da…...

Qt程序使用路径方式和注意事项

Qt程序使用路径方式和注意事项 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;Qt开发经验 &#x1f448;文章目录Qt程序使用路径方式和注意事项[toc]前言一、Windows下Qt程序使用路径1.准备工作2.测试结果二、Linux下Qt程序使用路径1.准备工作2.测试结…...