Java 之深入理解 String、StringBuilder、StringBuffer

前言

        由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到,这里就顺便总结一下:本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析


 ✨上期回顾:Java 哈希表


 ✨目录

前言

 String 介绍

String 的不可变性

String 在字符串常量池中的表示

字符串常量池没有该字符串:

字符串常量池有该字符串:

总结

StringBuilder 与 StringBuffer

 效率的比较

线程安全的比较

模拟面试

 String 介绍

String 的不可变性

        跳转到 String 的实现就会发现:

String 类 不能被继承:该类被 final 修饰
String 类是不可变的:value[ ] 被 final 修饰,表明 value[ ] 自身的值不能改变
String 类可以序列化,可以和其他 String 比较其大小:实现了 Comparable 接口

   通过下述代码,你就会发现 String 类中每一个看起来会修改 String 值的方法,实际上都是创建了一个全新的 String 对象包含修改后的字符串内容。而最初的 String 对象则丝毫未动 

class Test{static String Func(String s){return s.toUpperCase();}public static void main(String[] args) {String str = "hello world";System.out.println(str);String ret = Func(str);System.out.println(ret);System.out.println(str);}
}//输出结果:
hello world
HELLO WORLD
hello world

         当把 str 传给 Func 方法时,实际传递的是引用的一个拷贝。其实,每当把 String 对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过

String 在字符串常量池中的表示

        先从一段代码开始吧,以下这行代码总共创建了几个对象呢?

String str = new String("Hello");

        我想很多人看到会不暇思索的回答:“这不是一个吗?”,其实并不然它创建了两个对象:

字符串常量池没有该字符串:

        如果字符串常量池中没有 Hello 这个字符,先在字符串常量池中创建一个 ‘Hello’ 的字符串对象,然后再在堆中创建一个 ‘Hello’ 的字符串对象,然后将堆中这个 ‘Hello’ 的字符串对象地址返回赋值给变量 str。因此需要创建个对象。


字符串常量池有该字符串:

        String str = new String("Hello World");String ret = new String("Hello World");

        Java 虚拟机会先在字符串常量池中查找有没有 ‘Hello’ 这个字符串对象,如果有,就不会在字符串常量池中创建 ‘Hello’ 这个对象了,直接在堆中创建一个‘Hello’ 的字符串对象,然后将堆中这个 ‘Hello’ 的对象地址返回赋值给变量 str。因此只需要创建个对象。

(注意:ret 所指向的字符是 Hello,不是HHllo,画到最后没存档回不去了,将就着看吧)

        为什么要先在字符串常量池中创建对象,然后再在堆上创建呢

        由于字符串的使用频率实在是太高了,所以 Java 虚拟机为了提高性能和减少内存开销,在创建字符串对象的时候进行了一些优化,特意为字符串开辟了一块空间 -- 也就是字符串常量池

        通常情况下我们会采用双引号的方式来创建字符串对象,而不是通过 new 关键字的方式,因为 new 会强制创建对象会对资源造成浪费。

        如果我们采用双引号创建对象,如下图所示:

String str1 = "Hello";

String str2 = "World";

        Java 虚拟机会先在字符串常量池中查找是否存在该字符串,如果存在则不创建任何对象直接返回常量池中的对象引用如果不存在,则在常量池中创建该字符串,并返回对象引用。这样做的好处是避免了重复创建多个相同的字符串对象,减少了内存的开销。

        接下来我们来研究一个经典的面试问题:

    public static void main(String[] args) {String a = "abc";String b = new String("abc");String c = new String("abc");String d = b.intern();System.out.println(a == b);System.out.println(b == c);System.out.println(a == d);}

     请问上述程序打印的结构是什么?

// 打印结构为:
false
false
true

        通过 String a = "abc" 这样创建一个字符串对象时,JVM会首先在字符串常量池中寻找这个字符串,我们发现 "abc" 不存在,则在常量池中创建该字符串并将 a 指向它


        通过 String b = new String("abc") 这样创建字符串时,情况就不一样了,同样先在字符串常量池中寻找这个字符串,我们发现 "abc" 存在。它会在堆区创建该字符串对象并使 b 指向它,同样调用 String c = new String("abc") 时,也会在堆区再创建一个 String 对象并使 c 指向它。由于我们字符串中 “==” 比较的是地址,而我们的 b、c 创建的是两个不同的对象所以返回 false。a、b 同理返回 false。


        当调用 String d = b.intern() 时,intern方法(该方法为 native 方法)会在字符串常量池中查找是否存在该字符串对象,如果存在,则将 d 指向该常量池中的字符串对象,如果不存在则在常量池中创建该字符串并指向它,所以 a == d 返回 true。

总结

使用双引号声明的字符串对象会保存在字符串常量池中
使用 new 关键字创建的字符串对象会先从字符串常量池中找,如果没找到就创建一个,然后再在堆中创建字符串对象;如果找到了,就直接在堆中创建字符串对象
在存在字符串常量池的前提下,使用 new 关键字但是不想创建对象,可以使用 intern 方法直接获取字符串常量池的引用

StringBuilder 与 StringBuffer

 效率的比较

        通过以上内容,相信你已经对 String 有一定了解。由于字符串是不可变的,所以当遇到字符串的拼接(尤其是使用 + 号操作符)的时候,就需要考量性能的问题,你不能毫无顾虑地生产太多 String 对象,对珍贵的内存造成不必要的压力

        于是 Java 就设计了两个专门用来解决此问题的 StringBuilder、StringBuffer 类 ~

        可能有人会问 String 能做的事情干嘛还要用 StringBuilder、StringBuffer 呢?我们可以对一个字符进行多次拼接查看程序的运行效率,如下述代码:

    public static void main(String[] args) {String s = "";long st = System.currentTimeMillis();for(int i = 0; i < 100000; i++) {s += "a";}long ed = System.currentTimeMillis();System.out.println("String时间:" + (ed - st) + "毫秒");st = System.currentTimeMillis();StringBuilder sb = new StringBuilder();for(int i = 0; i < 100000; i++) {sb.append("a");}ed = System.currentTimeMillis();System.out.println("StringBuilder时间:" + (ed - st) + "毫秒");st = System.currentTimeMillis();StringBuffer sf = new StringBuffer();for(int i = 0; i < 100000; i++) {sf.append("a");}ed = System.currentTimeMillis();System.out.println("StringBuffer时间:" + (ed - st) + "毫秒");}

代码运行结果:

String时间:827毫秒
StringBuilder时间:1毫秒
StringBuffer时间:3毫秒

        可以看出,在大量对字符串进行连接操作的情况下,StringBuilder、StringBuffer 优势非常明显。因为 String 拼接会产生大量对象,而 StringBuilder、StringBuffer 无论是创建、拼接、修改、删除都是直接作用于原字符串,并不会产生多余的对象。其次 StringBuilder 比 StringBuffer 的效率稍微高一点也是有原因的:这就涉及到线程安全问题,待会再讲。

 

线程安全的比较

        我们可以来对比一下它们的底层源码,再来做分析:

//String
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];...
}//StringBuilder
public final class StringBuilderextends AbstractStringBuilderimplements Serializable, CharSequence
{@Overridepublic StringBuilder append(Object obj) {}@IntrinsicCandidatepublic String toString() {...}...
}//StringBuffer
public final class StringBufferextends AbstractStringBuilderimplements Serializable, CharSequence
{//方法有synchronized关键字@Overridepublic synchronized StringBuffer append(Object obj){...}@IntrinsicCandidatepublic synchronized String toString() {...}...
}//AbstractStringBuilder 
abstract sealed class AbstractStringBuilder implements Appendable, CharSequence permits StringBuilder, StringBuffer {byte[] value;...
}

 

        <1> 我们可以看到在 String 中,value 是被 final 修饰的是不可变的;StringBuilder、StringBuffer 都继承于 AbstractStringBuilder 这个类,而这个类中 的 value 是可变数组,所以我们进行拼接等操作时是直接作用于原字符串实现的,这就是效率高的由来。

 

        <2> 我们观察 StringBuilder、StringBuffer  的 toString、append 方法:由于 StringBuffer 操作字符串的方法加了synchronized 进行了同步,所以每次操作字符串时都会加锁,所以线程安全、但是性能低。这就是 StringBuilder 比 StringBuffer 运行效率略高的原因。

总结:

String 类

不可变性:一旦创建,内容不可改变
线程安全:由于不可变性,String 对象天生线程安全
性能:频繁的字符串操作会导致大量的对象创建和内存消耗

StringBuilder 类

可变性:内容可以被改变
非线程安全:适用于单线程环境
性能:比 String 更适合频繁的字符串操作,因为不会创建大量的中间对象

StringBuffer 类

可变性:内容可以被改变
线程安全:所有方法都是同步的,适用于多线程环境
性能:由于同步机制,性能略低于 StringBuilder

模拟面试

如果HR问你:String、StringBuffer、StringBuilder 的区别?(你会怎么回答)

答:关于String、StringBuffer、StringBuilder的区别,我有四个方面来说:

        第一个是可变性,String 内部的 value 是 final 修饰的,所以它是一个不可变的类,所以每次修改 String 的值的时候都会产生一个新的对象。而 StringBuffer、StringBuilder 是一个可变类,字符串的变更不会产生新的对象。

        第二个是线程的安全性,因为 String 是一个不可变的类,所以它是线程安全的;而 StringBuffer 也是线程安全的,因为它的每个操作方法中都有一个 synchronized  一个同步关键字;StringBuilder 不是线程安全的,所以在多线程环境下对字符串进行操作的时候我们应该使用 StringBuffer 否者使用 StringBuilder。

        第三个是性能方面,String 效率是最低的,因为其不可变性导致做字符串的拼接或者修改的时候,我们需要创建新的对象,以及分配内存;其次是 StringBuffer  比 String 的效率更高一点,因为它的可变性意味值字符串可以直接被修改;最后性能最高的是 StringBuilder ,因为 StringBuilder 比 StringBuffer 的性能要高,因为 StringBuffer 加了同步锁意味着对性能产生了影响。

        第四个是存储方面,String 存储在字符串常量池中,而 StringBuffer、StringBuilder 则是存储在堆的内存空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/880925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全局安装cnpm并设置其使用淘宝镜像的仓库地址(地址最新版)

npm、cnpm和pnpm基本概念 首先介绍一下npm和cnpm是什么&#xff0c;顺便说一下pnpm。 npm npm&#xff08;Node Package Manager&#xff09;是Node.js的默认包管理器&#xff0c;用于安装、管理和分享JavaScript代码包。它是全球最大的开源库生态系统之一&#xff0c;提供了数…

如何使用ssm实现基于HTML的中国传统面食介绍网站的搭建+vue

TOC ssm758基于HTML的中国传统面食介绍网站的搭建vue 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔…

微服务SpringGateway解析部署使用全流程

官网地址&#xff1a; Spring Cloud Gateway 目录 1、SpringGateway简介 1、什么是网关 2、为什么用网关【为了转发】 2、应用&#xff1a; 1.启动nacos 2.创建网关项目 3.网关配置1 4.网关配置2【了解】 5.过滤器配置【了解】 1、SpringGateway简介 核心功能有三个&…

Webpack模式-Resolve-本地服务器

目录 ResolveMode配置搭本地服务器区分环境配置 Resolve 前面学习时使用了各种各样的模块依赖&#xff0c;这些模块可能来自于自己编写的代码&#xff0c;也可能来自第三方库&#xff0c;在 Webpack 中&#xff0c;resolve 是用于解析模块依赖的配置项&#xff0c;它决定了 We…

每日OJ题_牛客_DP13[NOIP2002 普及组]过河卒_路径dp_C++_Java

目录 牛客_DP13[NOIP2002 普及组]过河卒_路径dp 题目解析 C代码1 C代码2 Java代码 牛客_DP13[NOIP2002 普及组]过河卒_路径dp [NOIP2002 普及组] 过河卒_牛客题霸_牛客网 (nowcoder.com) 描述&#xff1a; 棋盘上 A点有一个过河卒&#xff0c;需要走到目标 B点。卒行走的…

业务封装与映射 -- 业务映射路径

为什么需要封装映射 OTN网络客户业务种类繁多&#xff08;例如SDH、以太网、视频&#xff09;&#xff0c;且业务大小不一&#xff08;例如STM-1、STM-4、STM-16&#xff09;&#xff0c;为了便于传输、管理客户业务&#xff0c;保证设备间互联互通&#xff0c;ITU-T定义了OTN接…

《深度学习》OpenCV 角点检测、特征提取SIFT 原理及案例解析

目录 一、角点检测 1、什么是角点检测 2、检测流程 1&#xff09;输入图像 2&#xff09;图像预处理 3&#xff09;特征提取 4&#xff09;角点检测 5&#xff09;角点定位和标记 6&#xff09;角点筛选或后处理&#xff08;可选&#xff09; 7&#xff09;输出结果 3、邻域…

物联网智能项目全面解析

目录 引言 一、物联网概述 1.1 什么是物联网 1.2 物联网的历史与发展 二、物联网智能项目分类 三、关键组件与技术 3.1 传感器和执行器 3.2 连接技术 3.3 数据处理与分析 3.4 用户界面 四、物联网智能项目案例分析 4.1 智能家居 4.2 智慧城市 4.3 工业物联网 4.4…

前端编程艺术(2)----CSS

目录 1.CSS 2.CSS引入 3.选择器 1.标签选择器 2.类选择器 3.id选择器 4.属性选择器 5.后代选择器 5.直接子元素选择器 6.伪类选择器 链接相关 动态伪类 结构化伪类 否定伪类 其他伪类 UI元素状态伪类 4.字体 1.font-family 2.font-size 3.font-style 4.fo…

C++之多线程

前言 多线程和多进程是并发编程的两个核心概念,它们在现代计算中都非常重要,尤其是在需要处理大量数据、提高程序性能和响应能力的场景中。 多线程的重要性: 资源利用率:多线程可以在单个进程中同时执行多个任务,这可以更有效地利用CPU资源,特别是在多核处理器上。 性…

奔驰EQS450suv升级增强AR抬头显示HUD案例分享

以下是奔驰 EQS450 SUV 升级增强版 AR 抬头显示的一般改装案例步骤及相关信息&#xff1a; 配件&#xff1a;通常包括显示屏、仪表模块、饰板等。 安装步骤&#xff1a; 1. 拆下中控的仪表。 2. 在仪表上预留位置切割出合适的孔位&#xff0c;用于安装显示器。 3. 将显示器…

(IDEA)spring项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案

系列文章目录 文章目录 系列文章目录一、&#xff08;IDEA&#xff09;spring项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案1.资料 一、&#xff08;IDEA&#xff09;spring项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案 1.资料…

Python案例--水仙花数的探索之旅

一、引言 水仙花数&#xff0c;也称为阿姆斯特朗数&#xff0c;是一种特殊的三位数&#xff0c;其各位数字的立方和等于其本身。例如&#xff0c;153就是一个水仙花数&#xff0c;因为 135333153135333153。这种数字的发现不仅展示了数字的内在美&#xff0c;也激发了人们对数…

大学学校用电安全远程监测预警系统

1.概述&#xff1a; 该系统是基于移动互联网、云计算技术&#xff0c;通过物联网传感终端&#xff0c;将办公建筑、学校、医院、工厂、体育场馆、宾馆、福利院等人员密集场所的电气安全数据&#xff0c;实时传输至安全用申管理服务器&#xff0c;为用户提供不间断的数据跟踪&a…

五子棋双人对战项目(4)——匹配模块(解读代码)

目录 一、约定前后端交互接口的参数 1、websocket连接路径 2、构造请求、响应对象 二、用户在线状态管理 三、房间管理 1、房间类&#xff1a; 2、房间管理器&#xff1a; 四、匹配器(Matcher) 1、玩家实力划分 2、加入匹配队列&#xff08;add&#xff09; 3、移除…

解决ModuleNotFoundError: No module named ‘torchcrf‘

运行深度学习程序时候&#xff0c;出现报错&#xff1a;ModuleNotFoundError: No module named torchcrf 将 from torchcrf import CRF 改为 from TorchCRF import CRF

C#案例 | 基于C#语言在Excel中进行二次开发(一):简单系统搭建:打印输出“Hello Excel C#”

基于C#语言在Excel中进行二次开发&#xff08;一&#xff09;&#xff1a;简单系统搭建&#xff1a;打印输出”Hello Excel & C#” 实现效果第一步&#xff1a;前期准备第二步&#xff1a;打开VS 2022&#xff0c;创建项目第三步&#xff1a;程序界面设计 实现效果 在Exce…

《蓝桥杯算法入门》(C/C++、Java、Python三个版本)24年10月出版

推荐&#xff1a;《算法竞赛》&#xff0c;算法竞赛大全书&#xff0c;网购&#xff1a;京东 天猫  当当 文章目录 《蓝桥杯算法入门》内容简介本书读者对象作者简介联系与交流《蓝桥杯算法入门 C/C》版目录 《蓝桥杯算法入门 Java》版目录 《蓝桥杯算法入门 Python》版目录 …

STM32器件支持包安装,STLINK/JLINK驱动安装

一、支持包安装 1、离线安装 先下载支持包之后&#xff0c;再进行安装。如下图要安装STM32F1系列&#xff0c;双击 出现如下&#xff0c;会自动锁定安装路径&#xff0c;然后点击下一步&#xff0c;直接安装。 2、在线安装 首先需要电脑联网。如下。先点击第一个红框绿色按钮…

【EXCEL数据处理】000011 案列 EXCEL带有三角形图标的单元格转换,和文本日期格式转换。

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】000011 案列 EXCEL带有三角形图标的单元格转换。使用…