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

Java中的字符串——String,StringBuilder,StringBuffer

1. 字符串基础(String类)

  • Java中的字符串是String类的对象;String类位于java.lang包中,无需显式导入;字符串是不可变的(immutable),一旦创建就不能修改

创建字符串的方式: 

// 方式1:直接赋值(使用字符串常量池)
String s1 = "Hello";// 方式2:使用new关键字(在堆内存中创建新对象)
String s2 = new String("Hello");

 字符串存储机制:

  • 字符串常量池(String Pool)

        位于方法区(Java 8及之前)或堆内存(Java 8之后),用于存储字符串字面量,相同内容的字符串字面量会共享常量池中的同一个对象

  • 字符串创建的内存分配

  String s1 = "Hello": 检查常量池,存在则直接引用,不存在则创建并放入常量池;String s2 = new String("Hello"): 先在常量池查找"Hello"(存在则引用),然后在堆中创建新对象

String s1 = "Hello";  // 常量池中创建"Hello"
String s2 = new String("Hello");  // 堆中创建新对象,但共享常量池的字符数组System.out.println(s1 == s2);  // false,因为一个是常量池对象,一个是堆对象
System.out.println(s1.equals(s2));  // true,内容相同
System.out.println(s1 == "Hello");  // true,都指向常量池中的同一个对象
System.out.println(s2 == "Hello");  // false,s2是堆对象
  1. ​字符串字面量"Hello"的处理​​:

    当JVM遇到字符串字面量"Hello"时,首先会检查字符串常量池中是否已经存在内容相同的字符串;如果常量池中已经存在"Hello"(比如之前有String s1 = "Hello"这样的语句),则不会在常量池中创建新的对象,而是直接引用常量池中已有的"Hello"对象
  2. ​new String("Hello")的执行​​:

       new关键字会在堆内存中创建一个新的String对象,这个新创建的String对象的内容(字符数组)会指向常量池中已有的"Hello"的字符数组(Java 6及之前是复制一份,Java 7+是共享字符数组)(注意:这个新创建的String对象本身不会被放入字符串常量池)

String类型的内部实现:

        在Java中,String类内部使用​final char[]​(字符数组,Java 9之前)或​byte[]​(字节数组,Java 9及之后)来存储字符串的字符数据。

// Java 8及之前的String内部实现(简化版)
public final class String {private final char value[];  // 存储字符的数组private int hash;            // 缓存哈希值// ...
}

        ​从Java 9开始,为了节省内存(特别是ASCII字符串占用的空间),String内部改用byte[]存储,并新增一个coder字段标识编码方式:

// Java 9+的String内部实现(简化版)
public final class String {private final byte[] value;  // 存储字符的字节数组private final byte coder;    // 0=LATIN1(ASCII), 1=UTF16private int hash;            // 缓存哈希值// ...
}
  • coder​:

    • 0LATIN1编码(1字节/字符,适用于纯ASCII字符串)

    • 1UTF16编码(2字节/字符,适用于包含非ASCII字符的字符串)

2.Java字符串操作方法汇总表​(String类)

​分类​

​方法​

​说明​

​示例​

​基本信息​

length()

返回字符串长度(字符数)

"Hello".length()→ 5

isEmpty()

判断字符串是否为空(长度为0)

"".isEmpty()→ true

​字符访问​

charAt(int index)

返回指定索引处的字符(索引从0开始)

"Java".charAt(1)→ 'a'

codePointAt(int index)

返回指定索引处的Unicode代码点

"A".codePointAt(0)→ 65

​字符串比较​

equals(Object obj)

比较字符串内容是否相等(区分大小写)

"Hello".equals("hello")→ false

equalsIgnoreCase(String str)

比较字符串内容是否相等(不区分大小写)

"Hello".equalsIgnoreCase("hello")→ true

compareTo(String str)

按字典顺序比较字符串(返回int值)

"apple".compareTo("banana")→ 负数("apple" < "banana")

compareToIgnoreCase(String str)

不区分大小写的字典顺序比较

"Apple".compareToIgnoreCase("apple")→ 0

​查找操作​

indexOf(int ch)

返回指定字符首次出现的索引(未找到返回-1)

"Hello".indexOf('l')→ 2

indexOf(String str)

返回指定子字符串首次出现的索引

"Hello World".indexOf("World")→ 6

lastIndexOf(int ch)

返回指定字符最后一次出现的索引

"Hello".lastIndexOf('l')→ 3

lastIndexOf(String str)

返回指定子字符串最后一次出现的索引

"Hello Hello".lastIndexOf("Hello")→ 6

contains(CharSequence s)

判断是否包含指定字符序列

"Java".contains("av")→ true

​转换操作​

toLowerCase()

转换为全小写

"Hello".toLowerCase()→ "hello"

toUpperCase()

转换为全大写

"Hello".toUpperCase()→ "HELLO"

trim()

去除首尾空白字符

" Java ".trim()→ "Java"

replace(char oldChar, char newChar)

替换所有指定字符

"Java".replace('a', 'o')→ "Jovo"

replace(String target, String replacement)

替换所有指定子字符串

"Java".replace("a", "o")→ "Jovo"

replaceAll(String regex, String replacement)

使用正则表达式替换所有匹配项

"a1b2".replaceAll("\\d", "x")→ "axbx"

split(String regex)

根据正则表达式分割字符串

"a,b,c".split(",")→ ["a", "b", "c"]

​子字符串​

substring(int beginIndex)

从指定索引开始到末尾的子字符串

"Hello".substring(2)→ "llo"

substring(int beginIndex, int endIndex)

截取从beginIndex到endIndex-1的子字符串

"Hello".substring(1, 3)→ "el"

​前缀/后缀判断​

startsWith(String prefix)

判断是否以指定前缀开头

"Hello".startsWith("He")→ true

endsWith(String suffix)

判断是否以指定后缀结尾

"Hello".endsWith("lo")→ true

​数值转换​

valueOf(...)

将各种类型转换为字符串(静态方法)

String.valueOf(123)→ "123"

​格式化​

format(String format, Object... args)

格式化字符串(类似C的printf)

String.format("Name: %s, Age: %d", "Alice", 25)→ "Name: Alice, Age: 25"

​连接字符串​

join(CharSequence delimiter, CharSequence... elements)

用分隔符连接多个字符串

String.join("-", "2023", "01", "01")→ "2023-01-01"

​其他实用方法​

matches(String regex)

判断字符串是否匹配正则表达式

"a1b2".matches("\\w\\d\\w\\d")→ true

lines()(Java 11+)

按行分割字符串为Stream

"a\nb\nc".lines().count()→ 3

repeat(int count)(Java 11+)

重复字符串指定次数

"Hi".repeat(3)→ "HiHiHi"

注意:不可变性​​:所有字符串操作方法均返回新字符串对象,原始字符串不会被修改。 


3.StringBuilder和StringBuffer 

        StringBuilderStringBuffer都是Java中用于高效处理可变字符串的类,它们解决了String类不可变带来的性能问题。 

1. 基本概念

特性

StringBuilder

StringBuffer

​可变性​

可变

可变

​线程安全​

非线程安全

线程安全

​引入版本​

Java 5

Java 1.0

  • ​共同点​​:

    • 都是可变的字符序列

    • 都提供了比String更高效的字符串操作方法

    • 都继承自AbstractStringBuilder类

  • ​主要区别​​:

    • StringBuffer是线程安全的,StringBuilder不是

    • StringBuffer的方法使用synchronized关键字修饰

    • StringBuilder性能优于StringBuffer

2. 构造方法

两者都提供了多种构造方法:

// 默认初始容量为16个字符
StringBuilder sb1 = new StringBuilder();        // 默认初始容量16
StringBuffer sbf1 = new StringBuffer();         // 默认初始容量16// 指定初始容量
StringBuilder sb2 = new StringBuilder(100);
StringBuffer sbf2 = new StringBuffer(100);// 用指定字符串初始化
StringBuilder sb3 = new StringBuilder("Hello");
StringBuffer sbf3 = new StringBuffer("World");

3. 核心方法对比

3.1 基本操作方法

方法

StringBuilder

StringBuffer

说明

append(String str)

✔️

✔️

追加字符串

insert(int offset, String str)

✔️

✔️

在指定位置插入字符串

delete(int start, int end)

✔️

✔️

删除子字符串

replace(int start, int end, String str)

✔️

✔️

替换子字符串

reverse()

✔️

✔️

反转字符串

setCharAt(int index, char ch)

✔️

✔️

设置指定位置的字符

3.2 线程安全相关

方法

StringBuilder

StringBuffer

说明

所有公共方法

无同步

有synchronized修饰

StringBuffer的方法都是线程安全的

4. 性能比较

由于StringBuffer的方法都使用了synchronized关键字进行同步,在单线程环境下:

  • ​StringBuilder性能更好​​:因为它不需要处理线程同步的开销

  • ​StringBuffer性能稍差​​:因为每次方法调用都需要获取和释放锁

多线程环境下:

  • ​StringBuffer更安全​​:可以保证线程安全

  • ​StringBuilder不安全​​:可能导致数据不一致

5. 使用场景建议及示例

使用StringBuilder的场景:

  1. 单线程环境下的字符串拼接操作

  2. 性能要求较高的字符串处理

  3. 方法内部的临时字符串操作

使用StringBuffer的场景:

  1. 多线程环境下的字符串操作

  2. 需要线程安全的字符串缓冲区

  3. 多个线程可能同时修改同一个字符串缓冲区的情况

StringBuilder示例(单线程):

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
sb.insert(5, ",");
String result = sb.toString(); // "Hello, World"

StringBuffer示例(多线程):

StringBuffer sbf = new StringBuffer();// 线程1
new Thread(() -> {for (int i = 0; i < 100; i++) {sbf.append("A");}
}).start();// 线程2
new Thread(() -> {for (int i = 0; i < 100; i++) {sbf.append("B");}
}).start();// 最终sbf的内容是200个字符,包含A和B的混合

6. 内部实现原理

两者都继承自AbstractStringBuilder类,内部使用字符数组(java9之前)或字节数组(java9+)存储数据:

abstract class AbstractStringBuilder implements Appendable, CharSequence {/*** The value is used for character storage.*/char[] value;/*** The count is the number of characters used.*/int count;
abstract class AbstractStringBuilder implements Appendable, CharSequence {/*** The value is used for character storage.*/byte[] value;/*** The id of the encoding used to encode the bytes in {@code value}.*/byte coder;/*** The count is the number of characters used.*/int count;
....

当容量不足时,会自动扩容(通常是当前容量的2倍+2)。

7. 最佳实践

  1. ​优先使用StringBuilder​​:在大多数现代应用中,字符串操作通常在单线程环境下进行,应优先使用StringBuilder。

  2. ​预估初始容量​​:如果能预估最终字符串的大致长度,应在构造时指定初始容量,避免频繁扩容。

  3. ​避免在循环中使用String的+操作​​:

    // 不好 - 每次循环都创建新的String对象
    String result = "";
    for (int i = 0; i < 100; i++) {result += i; 
    }// 好 - 使用StringBuilder
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 100; i++) {sb.append(i);
    }
    String result = sb.toString();
  4. ​多线程环境下必须使用StringBuffer​​:当多个线程可能同时修改同一个字符串缓冲区时,必须使用StringBuffer。

http://www.lryc.cn/news/591247.html

相关文章:

  • 基于邻域统计分析的点云去噪方法
  • 【测试100问】没有接口文档的情况下,如何做接口测试?
  • TC500R立式加工中心主轴箱机械结构设计cad【11张】三维图+设计说明书
  • 【后端】.NET Core API框架搭建(7) --配置使用Redis
  • Android本地浏览PDF(Android PDF.js 简要学习手册)
  • React hooks——useReducer
  • 面试Redis篇-深入理解Redis缓存穿透
  • 基于YOLOv11的水面垃圾智能检测系统
  • halcon 模板匹配
  • 高精度加法模版介绍
  • 阿里云-通义灵码:隐私保护机制—为数据安全筑起铜墙铁壁
  • USRP中心频率与采样率联合设置
  • MyBatis 之配置与映射核心要点解析
  • CPU架构、三级缓存以及内存优化屏障
  • 指针数组和数组指针的应用案例
  • 「Trae IDE 全流程实战」——从 0 下载安装,到在本地跑起一个可玩的 2048 小游戏
  • SpringBoot使用ThreadLocal共享数据
  • 永磁同步电机MTPA与MTPV曲线具体仿真实现
  • 大语言模型Gemini Deep Research 全流程解读+使用攻略
  • 杨耀东老师在ICML2025上对齐教程:《语言模型的对齐方法:一种机器学习视角》
  • 死信队列:springboot+RabbitMQ实现死信队列
  • GitHub Jekyll博客本地Win开发环境搭建
  • NumPy 数组存储字符串的方法
  • 算法提升之字符串练习-02(字符串哈希)
  • 【leetcode】852. 山脉数组的封顶索引
  • React 18 vs Vue3:状态管理方案深度对比
  • 深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()
  • 我爱学算法之—— 前缀和(下)
  • 第十四章 gin基础
  • 深入理解React Hooks:从使用到原理