java基础之 String\StringBuffer\ StringBuilder

文章目录

  • String
    • 字符串的创建
    • 为什么说String是不可变的?
    • 创建后的字符串存储在哪里?
    • 字符串的拼接
    • String类的常用方法
  • StringBuilder & StringBuffer
    • 使用方法
    • 验证StringBuffer和StringBuilder的线程安全问题
  • 总结
    • 三者区别
    • 什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder 对象的 append 方法连接字符串性能更好?

String

String不是基本数据类型,是引用类型。

字符串的创建

  • 方法一:直接赋值
    String s01 = "helloWorld";
    
  • 方法二:通过new对象创建
    String s02 = new String("helloWorld");
    
  • 方法三:拼接创建
    String s03 = "hello"+"World";
    
  • 方法四:通过数组创建
    byte[] arr = {10,20,30,40};
    String s04 = newString(arr);
    

为什么说String是不可变的?

  • String类型是不可变的

    其类是由final修饰的,这就导致了在修改字符串内容是会创建新的String对象,会增加内存的使用和开销。
    (下方为源码截图)

    在这里插入图片描述

    如果需要修改字符串而不产生新的对象,我们可以使用StringBuffer 或者StringBuilder

  • String类型是线程安全的

    因为String是不可变的,意味着一旦创建了String对象,字符串的内容就不能被更改。任何对字符串的修改都会生成一个新的String对象

  • 为什么需要设置成不可变的?

    因为java设计者认为字符串使用是比较频繁的,设置为不可变的可以允许多个客户端同时共享这一字符串,从而保证了线程安全。

创建后的字符串存储在哪里?

  • 字符串创建后存在于运行时常量池中。
    (在jdk1.7以前,运行时常量池存在于方法区中。从jdk1.7开始,就存在于堆中。)

  • 当创建一个字符串对象时,如果字符串内容在常量池中已经存在,JVM会返回常量池中的引用;如果不存在,JVM会在常量池中创建一个新的字符串对象‌

字符串的拼接

  • 我们先看一下代码

    String s01 = "aaa";
    String s02 = "bbb";String ss01 = "aaabbb";
    String ss02 = s01+s02;
    System.out.println(ss01==ss02);  // (1)结果分析看下方String ss03 = ss02.intern();
    System.out.println(ss01==ss03); // (2)结果分析看下方String ss04 = "aaa"+"bbb";
    System.out.println(ss01==ss04); // (3)结果分析看下方String ss05 = s01.concat(s02);
    System.out.println(ss01==ss05); // (4)结果分析看下方
    
  • (1)运行结果为:false

    ① s01和s02为字符串变量
    ② 字符串变量拼接是会现在在堆中new一个对象,并将地址赋值给ss02,然后再让ss02指向常量池。
    ③ 因为无论连接后的数据是否在常量池,ss02执行的都是堆空间中的地址,故为false

  • (2)运行结果为:true

    String.intern()用于优化字符串的内存使用,可以减少内存消耗。它可以确保相同内容的字符串只存储一次。
    即,intern()方法的作用是把字符串加入到常量池中,然后返回这个字符串在常量池中的地址。

  • (3)运行结果为:true

    ① 字符串相加时,都是静态字符串,相加的结果会添加到常量池中。
    ② 如果常量池中有则返回其引用;如果没有则创建该字符串再返回其引用。
    ③ “aaabbb”在创建ss01时就已经存在常量池中了,所以ss04在创建的时候会发现已经存在"aaabbb"了,返回的是同一个引用,即ss01和ss04都是"aaabbb"在常量池中的引用,故是相等的。

  • (4)运行结果为:false

    追加的字符串如果不是空,则会产生一个新的对象。
    下方为concat方法的源码。即如果追加的字符串是空,返回当前对象,如果不是空,则会创建一个新的对象,而新对象是在堆中创建的,故ss05的地址是在堆中

    public String concat(String str) {int otherLen = str.length();if (otherLen == 0) {return this;}int len = value.length;char buf[] = Arrays.copyOf(value, len + otherLen);str.getChars(buf, len);return new String(buf, true);}
    

String类的常用方法

String str = "Hello,who are you?";
// (一) 返回指定索引处的char值
char c = str.charAt(10); // 打印结果为:H。如果超出范围则会报错
// (二)将指定字符串连接到此字符串的结尾
String newStr = str.concat("Joy");  // 打印结果为:Hello,who are you?Joy
// (三)测试字符串是否已指定的后缀结束
Boolean flag = str.endsWith("hello"); // 打印结果为:false
// (四)将string编码为byte序列,并将结果存储到一个新的byte数组中
byte[] bytes = str.getBytes(); // bytes = {72,101,108,108,111,44,119,104,111,32,97,114,101,32,121,111,117,63};
// (五)返回指定字符在该字符串中第一次出现的索引  或指定字符串第一次出现的索引
int index = str.indexOf(101);   // 101是e的字符值,其所在的索引位置是 1
int index01 = str.indexOf("you");   // 14
// (六)返回字符串的长度
int len = str.length();  // 18
// (七)替换字符串中的内容,第一个参数为被替换的内容,后一个参数为替换的新内容
String str01 = str.replace("who","where");   // str01打印结果:Hello,where are you?
// (八)拆分字符串
String[] strings = str.split("");  // String[] strings = {H,e,l,l,o,,,w,h,o, ,a,r,e, ,y,o,u,?};
String[] strings1 = str.split(" "); // String[] strings1 = {Hello,who,are,you?};
String[] strings2 = str.split(","); // String[] strings2 = {Hello,who are you?};
// (九)将字符串转换为一个新的字符数组
char[] chars = str.toCharArray(); // char[] chars = {H,e,l,l,o,,,w,h,o, ,a,r,e, ,y,o,u,?};
// (十)忽略字符串的前后空白
String str2 = " hello world  ";
String str3 = str2.trim();   // String str3 = "hello world";
// (十一)大小写转换
String str02 = str.toLowerCase();  // str02 = hello,who are you?
String str03 = str.toUpperCase();   // str03 = HELLO,WHO ARE YOU?
// (十二)判断是否包含指定的字符串序列
Boolean flag1 = str.contains("who");  // flag1 = true;
// (十三)判断字符串是否为空
Boolean flag2 = str.isEmpty();  // flag2 = false;

StringBuilder & StringBuffer

  • 针对于String的不可变性,StringBuilder和StringBuffer是可变的,其内容可以随意修改
  • StringBuilder和StringBuffer的对象被多次修改后,不陈胜新的未使用对象,即每次都会对对象本身进行操作,故对字符串进行修改推荐使用

使用方法

  • StringBuilder和StringBuffer使用的功能是一样的,这里就一起来整理,不分开了

  • 其他功能同String,再次也就不一一列举了

       StringBuilder builder = new StringBuilder("hello");StringBuffer buffer = new StringBuffer("hello");// 追加内容不能为空,可以追加其他基本类型的内容。注意添加后的内容是紧紧挨着的,没有空格的builder.append(3411);   buffer.append(3411);builder.append("world,hello");    // 打印结果:helloworldbuffer.append("world,hello");// 在索引2处添加"java",打印结果:hejavalloworldbuilder.insert(2,"java");  buffer.insert(2,"java");// 删除索引2到索引6之间的内容,左闭右开;打印结果:helloworldbuilder.delete(2,6);    buffer.delete(2,6);// 反转字符串builder.reverse();  buffer.reverse();// 两者扩容机制是一样的。初始容量为 16+字符串长度。扩容计算公式为:(capacity+1)*2int builderCapacity = builder.capacity();  // 打印结果:21。int bufferCapacity = buffer.capacity();int builderLen = builder.length();  // 打印结果:15int bufferLen = buffer.length();
    

验证StringBuffer和StringBuilder的线程安全问题

StringBuilder builder = new StringBuilder();
StringBuffer buffer = new StringBuffer();
Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<1000000;i++){builder.append("b");buffer.append("b");}}
});
t1.start();
Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<1000000;i++){builder.append("a");buffer.append("a");}}
});
t2.start();try {t1.join();t2.join();
} catch (InterruptedException e) {e.printStackTrace();
}finally {System.out.println(builder.length());System.out.println(buffer.length());
}

打印结果是buffer的永远是2000000,但是builder的会一直变化
(自己可以拷贝代码试试看哦)

总结

三者区别

  • 可变性 or 不可变性

    String是不可变的,是只读字符串。
    StringBuffer和StringBuilder是可变的。

  • 安全性

    StringBuffer是线程安全的;
    StringBilder是线程不安全的.
    因为StringBuilder没有被synchronize修饰,属于单线程下的,减少了线程切换的开销,所以其效率要比StringBuffer效率高

  • 性能及使用场景

    ‌String每次操作都会生成新的对象,性能较低,尤其是在频繁修改字符串的场景下;
    StringBuilder‌在单线程环境中,没有同步机制,性能较高。适合在单线程环境中频繁修改字符串的场景‌;
    ‌StringBuffer‌在单线程环境中,由于所有方法都是同步的,性能较低。但在多线程环境中,它可以安全地被多个线程使用‌

什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder 对象的 append 方法连接字符串性能更好?

  • 如果使用少量的字符串操作,使用 (+运算符)连接字符串;
  • 如果频繁的对大量字符串进行操作,则使用
    • 1:全局变量或者需要多线程支持则使用StringBuffer;
    • 2:局部变量或者单线程不涉及线程安全则使有StringBuilder。

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

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

相关文章

告别繁琐统计,一键掌握微信数据

微信数据管理的挑战在数字时代&#xff0c;微信已成为我们日常沟通和商业活动的重要工具。然而&#xff0c;随着微信号数量的增加&#xff0c;手动统计每个账号的数据变得越来越繁琐。从好友数量到会话记录&#xff0c;再到转账和红包&#xff0c;每一项都需要耗费大量的时间和…

bert-base-chinese模型使用教程

向量编码和向量相似度展示 import torch from transformers import BertTokenizer, BertModel import numpy as npmodel_name "C:/Users/Administrator.DESKTOP-TPJL4TC/.cache/modelscope/hub/tiansz/bert-base-chinese"sentences [春眠不觉晓, 大梦谁先觉, 浓睡…

HTML+CSS科技感时钟(附源码!!!)

预览效果 源码(直接复制使用) <!DOCTYPE html> <html lang"zh-Hans"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>科技感时钟</…

PyQt5实战——UTF-8编码器功能的实现(六)

个人博客&#xff1a;苏三有春的博客 系类往期文章&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; PyQt5实战——多脚本集合包&#xff0c;UI以及工程布局&#xff08;二&#xff09; PyQt5实战——多脚本集合包&#xff0c;程序…

‌MySQL中‌between and的基本用法‌

文章目录 一、between and语法二、使用示例2.1、between and数值查询2.2、between and时间范围查询2.3、not between and示例 BETWEEN AND操作符可以用于数值、日期等类型的字段&#xff0c;包括边界值。 一、between and语法 MySQL中的BETWEEN AND操作符用于在两个值之间选择…

微服务系列一:基础拆分实践

目录 前言 一、认识微服务 1.1 单体架构 VS 微服务架构 1.2 微服务的集大成者&#xff1a;SpringCloud 1.3 微服务拆分原则 1.4 微服务拆分方式 二、微服务拆分入门步骤 &#xff1a;以拆分商品模块为例 三、服务注册订阅与远程调用&#xff1a;以拆分购物车为例 3.1 …

jvm学习笔记-轻量级锁内存模型

一&#xff0c;轻量级锁 LockRecord的那个第一个成员变量是拷贝对应锁定了的java对象资源的MarkWord&#xff0c;Lock Record有一个Ptr指针刚开始指向自己&#xff0c;后面这个指针存储在锁定资源的java对象的markword中&#xff0c;后续可以通过java对象的MarkWord快速定位到…

【力扣专题栏】面试题 01.02. 判定是否互为字符重排,如何利用数组模拟哈希表解决两字符串互排问题?

题解目录 1、题目描述解释2、算法原理解析3、代码编写(1)、两个数组分别模拟哈希表解决(2)、利用一个数组模拟哈希表解决问题 1、题目描述解释 2、算法原理解析 3、代码编写 (1)、两个数组分别模拟哈希表解决 class Solution { public:bool CheckPermutation(string s1, stri…

指针和内存地址的关系(uint8_t和uint32_t的指针有什么区别)

指针在我们的学习中非常常见&#xff0c;有些人只是了解指针的基本概念&#xff0c;却不知道他的原理&#xff0c;到时候使用起来一头雾水&#xff0c;接下来我将对指针做出一些解释说明。 一、数据是如何存储的&#xff1a; 我们初始化一个int变量a,变量a会存储到内存中&#…

flutter 专题四 Flutter渲染流程

一、 Widget - Element - RenderObject关系 二、 Widget 、Element 、RenderObject 分别表示什么 2.1 Widget Widget描述和配置子树的样子 Widget就是一个个描述文件&#xff0c;这些描述文件在我们进行状态改变时会不断的build。但是对于渲染对象来说&#xff0c;只会使用最…

高效内容营销策略提升品牌影响力与客户忠诚度

内容概要 内容营销是一种通过创造和分享有价值的内容&#xff0c;以吸引特定目标受众并促进品牌发展的策略。这种营销形式不仅仅注重产品的直接推广&#xff0c;更着眼于与受众之间建立长期的信任关系。有效的内容营销能够提升品牌在市场中的影响力和客户的忠诚度&#xff0c;…

git 入门作业

任务1: 破冰活动&#xff1a;自我介绍任务2: 实践项目&#xff1a;构建个人项目 git使用流程&#xff1a; 1.将本项目直接fork到自己的账号下&#xff0c;这样就可以直接在自己的账号下进行修改和提交。 这里插一条我遇到的问题&#xff0c;在fork的时候没有将那个only camp4的…

NumPy Ndarray学习

1.NumPy Ndarray 对象简介 NumPy 最重要的特点是其 N 维数组对象 ndarray&#xff0c;它是一系列同类型数据的集合&#xff0c;以 0 下标为开始进行集合中元素的索引。ndarray 对象是用于存放同类型元素的多维数组。ndarray 中的每个元素在内存中都有相同存储大小的区域。 2.N…

网络层3——IP数据报转发的过程

目录 一、基于终点的转发 1、理解 2、IP数据报转发过程 二、最长前缀匹配 1、理解 2、主机路由 3、默认路由 三、二叉线索查找 一、基于终点的转发 1、理解 理解什么叫终点转发 IP数据报的传递&#xff0c;交给路由器后 可不可以做到直接发送给目的主机呢&#xff1f;…

【UGUI】为射击游戏添加动态显示的分数和血量到UI界面

项目背景 在这个项目中&#xff0c;我们希望实现一个简单的游戏系统&#xff0c;其中玩家可以通过击中目标来获得分数&#xff0c;同时通过与怪物碰撞来减少血量。分数和血量需要在游戏界面上实时显示&#xff0c;以便玩家能够随时了解自己的状态。 技术实现 1. 静态变量的使…

「C/C++」C/C++标准库 之 #include<cstdlib> 通用工具函数库

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

CTF压缩包破解神器bkcrack教程和详细使用过程

kali安装bkcrack教程和详细使用过程 1.bkcrack介绍&#xff1a;2.bkcrack功能&#xff1a;3.bkcrack安装&#xff1a;Linux-Kali下&#xff1a;测试&#xff1a;Windows下安装&#xff1a; 4.bkcrack的使用方法&#xff1a;4.1查看相关参数4.2恢复内部密钥从 zip 档案中加载数据…

基于Python的乡村居民信息管理系统【附源码】

基于Python的乡村居民信息管理系统 效果如下&#xff1a; 系统主页面 系统登录页面 管理员主页面 居民管理页面 政务学习页面 土地信息管理页面 个人信息管理页面 居民登陆页面 村委人员主页面 研究背景 随着信息技术的飞速发展和乡村振兴战略的深入实施&#xff0c;传统的乡…

UI设计公司—兰亭妙微—提供轨道交通行业UI设计

蓝蓝设计工作室2008年开始&#xff0c;2011年正式成立北京兰亭妙微科技有限公司&#xff0c;主创清华团队&#xff0c;专注软件和互联网ui设计开发&#xff0c;擅长企业信息化管理、监控、大数据软件UIUE咨询和设计开发服务。立足UI&#xff0c;一直在学习进步。交通行业UE UI解…

2-Ubuntu/Windows系统启动盘制作

学习目标&#xff1a; 掌握使用Win32DiskImager、Rufus等工具制作系统启动盘的基本步骤。独立将ISO镜像文件写入USB闪存驱动器&#xff0c;确保在需要时顺利安装或修复系统。通过学习如何选择正确的源文件和目标驱动器&#xff0c;理解启动盘的使用场景和注意事项&#xff0c;…