聊聊Java中的常用类String
String、StringBuffer、StringBuilder 的区别
从可变性分析
String不可变。StringBuffer、StringBuilder都继承自AbstractStringBuilder,两者的底层的数组value并没有使用private和final修饰,所以是可变的。
AbstractStringBuilder 源码如下所示
abstract class AbstractStringBuilder implements Appendable, CharSequence {char[] value;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;}}
从线程安全性考虑
String类是常量线程安全。StringBuilder线程不安全。StringBuffer线程安全。
从性能上分析
String是常量每次添加字符串都会将引用指向新的字符串。StringBuilder非线程安全所以性能上相较于StringBuffer会快10%-15%。
三者使用场景建议
- 操作少量数据,
String即可 - 单线程操作大量字符串,建议使用
StringBuilder。 - 多线程用
StringBuffer。
为什么String 是不可变的?
从源码可以看到String底层是使用字符数组存储值的,之所以不可变是因为:
value私有且final也没有对外提供字符串操作的方法。- 类设置为
final,子类也无法继承该类对其进行修改。
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];
}
字符串拼接用“+” 的底层工作机制是什么?
如下所示代码
public class StringTest {@Testpublic void addTest() {String s1 = "hello";String s2 = "world";String s3 = "guy";String s4 = s1 + s2 + s3;}
}
查看其字节码可以看到JVM为了避免大量常量创建,会将其进行优化,改用StringBuilder进行拼接后toString。
INVOKESPECIAL java/lang/StringBuilder.<init> ()VALOAD 1INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;ALOAD 2INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;ALOAD 3INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;ASTORE 4
但是在循环体内使用+=的情况下很可能造成性能灾难。
@Testpublic void addTest2() {String[] arr = {"hello", "world", "guys"};String string = "";for (int i = 0; i < arr.length; i++) {string += arr[i];}}
可以看到在循环体内会不断创建StringBuilder进行拼接。

来看看我们手动创建StringBuilder 进行拼接和+=由JVM优化后的性能差距
@Testpublic void addTest2() {String[] arr = new String[1000];for (int i = 0; i < arr.length; i++) {arr[i] = String.valueOf(i);}long start = System.currentTimeMillis();String string = "";for (int i = 0; i < arr.length; i++) {string += arr[i];}long end = System.currentTimeMillis();System.out.println("使用+=耗时:" + (end - start));start = System.currentTimeMillis();StringBuilder builder =new StringBuilder();for (int i = 0; i < arr.length; i++) {builder.append(arr[i]);}end = System.currentTimeMillis();System.out.println("使用StringBuilder耗时:" + (end - start));}
输出结果,可以看到StringBuilder比+=快了将近6倍。
使用+=耗时:6
使用StringBuilder耗时:1
String和Object的equals() 有什么区别
String对equals进行了重写,String比较的是字符串的值是否一致
public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}
而Object比较的则是两者的引用地址是否一致
public boolean equals(Object obj) {return (this == obj);}
字符串常量池是什么,它有什么用?
如下代码所示,Java会将字符串存放在方法区的字符串常量池,后续如有变量需要可以直接复用,关于字符串常量池后文会介绍。
@Testpublic void stringConst(){String s1="s";String s2="s";System.out.println(s1==s2);//true}
图解String s1 = new String(“abc”);
这段代码实际上会创建两个对象:
- 创建String对象s1指向堆区的String对象
- 在字符串常量池中创建字符串abc

intern 方法是什么?有什么用?
该方法会将字符串值存放到字符串常量池中,并返回该引用。注意如果常量池存在则直接返回引用。若不存在才会创建并返回引用。
常量intern
可以看到下面这段代码,调用intern 的字符串和常量池的对象==比较返回的是true
@Testpublic void internTest() {String s1 = "s";String s2 = s1.intern();String s3 = new String("s");String s4 = s3.intern();System.out.println(s1 == s2);//trueSystem.out.println(s3 == s4);//falseSystem.out.println(s1 == s4);//true}
常量+=intern
再补充一个神奇的现象,常量字符串进行+=时会被JVM在编译自动优化,例如String s1="a"+"b"实际上会被优化为String s1="ab",所以下面这段intern就会出现下面的结果:
原因也很简单:
对于编译期可以确定值的字符串,也就是常量字符串 ,JVM 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。 在编译过程中,Javac 编译器会进行一个叫做 常量折叠(Constant Folding) 的代码优化。
这里说到一个叫常量折叠的概念,常量折叠就是将常量表达式计算求值,并用求得的值来替换表达式,然后放到常量表中的一种机制。
@Testpublic void internTest2() {String s1 = "s"+"tring";String s2 = "string";String s3 = new String("string");String s4 = s3.intern();System.out.println(s1 == s2);//trueSystem.out.println(s3 == s4);//falseSystem.out.println(s1 == s4);//true}
这一点我们查看字节码文件就得以印证

final+=的intern
final字符串会被JVM优化为常量,所以下面这段代码也会返回true
@Testpublic void internTest3() {final String s1 = "hello";final String s2 = "world";String s3 = s1 + s2;String s4 = "helloworld";System.out.println(s3 == s4);//true}
查看字节码得以印证。

引用或者函数获取的+=
注意JVM不会对引用和方法这种动态变化的情况进行优化,所以下面这段代码就会返回false。
@Testpublic void internTest3() {final String s1 = "hello";final String s2 = getStr();String s3 = s1 + s2;String s4 = "helloworld";System.out.println(s3 == s4);//false}private String getStr() {return "world";}
参考文献
Java基础常见面试题总结(中)
相关文章:
聊聊Java中的常用类String
String、StringBuffer、StringBuilder 的区别 从可变性分析 String不可变。StringBuffer、StringBuilder都继承自AbstractStringBuilder ,两者的底层的数组value并没有使用private和final修饰,所以是可变的。 AbstractStringBuilder 源码如下所示 ab…...
R语言piecewiseSEM结构方程模型在生态环境领域实践技术
结构方程模型(Sructural Equation Modeling,SEM)可分析系统内变量间的相互关系,并通过图形化方式清晰展示系统中多变量因果关系网,具有强大的数据分析功能和广泛的适用性,是近年来生态、进化、环境、地学、…...
IDEA设置查看JDK源码
问题 我们在查看JDK源码时,可能会遇到这种情况,步入底层查看JDK源码时,出现一堆var变量,可读性非常之差,例如笔者最近想看到nio包下的SocketChannelImpl的write方法,结果看到这样一番景象: pu…...
SSM—Mybatis
目录 和其它持久化层技术对比 搭建MyBatis 开发环境 创建maven工程 创建MyBatis的核心配置文件 创建mapper接口 创建MyBatis的映射文件 通过junit测试功能 加入log4j日志功能 核心配置文件详解 MyBatis的增删改查 新增 删除 修改 查询一个实体类对象 查询list集…...
MYSQL在不删除数据的情况下,重置主键自增id
MYSQL在不删除数据的情况下,重置主键自增id 方法一: SET num : 0; UPDATE table_name SET id num : (num1); ALTER TABLE table_name AUTO_INCREMENT 1; 方法二: 背景(mysql 数据在进行多次删除新增之后id变得很大,但是并没…...
SpringMVC-servlet交互
servlet交互 1.1 引入servlet依赖 <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency>1.2 创建testservl…...
DICOM 文件中,VR,VL,SQ,图像二进制的几个注意点
DICOM 文件的结构,在网上有很多的学习资料,这里只介绍些容易混淆的概念,作为回看笔记。 1. 传输语法 每个传输语法,起都是表达的三个概念:大小端、显隐式、压缩算法 DICOM Implicit VR Little Endian: 1.2.840.1000…...
git 的使用
git reset详解-CSDN博客 git reset 命令详解 git revert命令详解。-CSDN博客 关于Git分支中HEAD和Master的理解 - 知乎 (zhihu.com) 一文带你精通 Git(Git 安装与使用、Git 命令精讲、项目的推送与克隆)-CSDN博客 Git 常用操作(5ÿ…...
详解—【C++】lambda表达式
目录 前言 一、lambda表达式 二、lambda表达式语法 2.1. lambda表达式各部分说明 2.2. 捕获列表说明 三、函数对象与lambda表达式 前言 在C98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。 #include <algorithm> #i…...
Qt Desktop Widgets 控件绘图原理逐步分析拆解
Qt 是目前C语言首选的框架库。之所以称为框架库而不单单是GUI库,是因为Qt提供了远远超过GUI的功能封装,即使不使用GUI的后台服务,也可以用Qt大大提高跨平台的能力。 仅就界面来说,Qt 保持各个平台绘图等效果的统一,并…...
什么是rocketmq❓
在大规模分布式系统中,各个服务之间的通信是至关重要的,而RocketMQ作为一款分布式消息中间件,为解决这一问题提供了强大的解决方案。本文将深入探讨RocketMQ的基本概念、用途,以及在实际分布式系统中的作用,并对Produc…...
【网络安全】HTTP Slowloris攻击原理解析
文章目录 Slowloris攻击的概念Slowloris攻击原理Slowloris攻击的步骤其他的DDoS攻击类型UDP FloodICMP (Ping) FloodSYN FloodPing of DeathNTP AmplificationHTTP FloodZero-day DDoS 攻击 推荐阅读 Slowloris攻击的概念 Slowloris是在2009年由著名Web安全专家RSnake提出的一…...
从最近爆火的ChatGPT,我看到了电商的下一个形态
爆火的ChatGPT似乎让每个行业有了改造的可能性,电商行业也不例外。 在讨论了很多流量红利消失的话题后,我们看到互联网电商行业不再性感,从淘宝天猫,京东,到拼多多,再到抖音,快手,电…...
云原生向量计算引擎 PieCloudVector:为大模型提供独特记忆
拓数派大模型数据计算系统(PieDataComputingSystem,缩写:πDataCS)在10月24日程序员节「大模型数据计算系统」2023拓数派年度技术论坛正式发布。πDataCS 以云原生技术重构数据存储和计算,「一份存储,多引擎…...
大创项目推荐 深度学习 opencv python 实现中国交通标志识别
文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…...
深度学习实战67-基于Stable-diffusion的图像生成应用模型的搭建,在Kaggle平台的搭建部署,解决本地没有算力资源问题
大家好,我是微学AI,今天给大家介绍一下深度学习实战67-基于Stable-diffusion的图像生成应用模型的搭建,在Kaggle平台的搭建部署,解决本地没有算力资源问题。稳定扩散模型(Stable Diffusion Model)是一种用于图像增强和去噪的计算机视觉算法。它通过对输入图像进行扩散过程…...
云原生之深入解析Kubernetes本地持久化存储方案OpenEBS LocalPV的最佳实践
一、K8s 本地存储 K8s 支持多达 20 种类型的持久化存储,如常见的 CephFS 、Glusterfs 等,不过这些大都是分布式存储,随着社区的发展,越来越多的用户期望将 K8s 集群中工作节点上挂载的数据盘利用起来,于是就有了 loca…...
设计模式-策略(Strategy)模式
又被称为政策(方针)模式策略模式(Strategy Design Pattern):封装可以互换的行为,并使用委托来决定要使用哪一个策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中&#x…...
Star 4.1k!Gitee GVP开源项目!新一代桌面应用开发框架 ElectronEgg!
前言 随着现代技术的快速升级迭代及发展,桌面应用开发已经变得越来越普及。然而对于非专业桌面应用开发工程师在面对这项任务时,可能会感到无从下手,甚至觉得这是一项困难的挑战。 本篇文章将分享一种新型桌面应用开发框架 ElectronEgg&…...
node.js学习(简单聊天室)
在掘金查看该文章 1. TCP服务搭建 1.1 socket 先来粗略了解下socket 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中&am…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
