深入理解Java中的String

前言

在Java中,String类是一个非常重要的内置类,用于处理字符串数据。字符串是不可变的(immutable),这意味着一旦创建,字符串的内容不能被修改。作为Java中最为基础和常用的类之一,String类在内存管理、线程安全、性能优化等方面都有着独特的设计和实现。本文将详细介绍Java中String类的特点、用途、主要方法以及常见用法,并通过代码示例展示如何在Java中创建、操作和使用String类。

1. String类的特点

1.1 不可变性

String对象一旦创建,其内容就不能被更改。这种不可变性是通过final关键字修饰字符数组value来实现的。String类中没有提供修改字符数组的方法,任何对字符串的修改操作(如拼接、替换等)都会创建一个新的String对象。

String str1 = "Hello";
String str2 = str1.concat(" World"); // 创建一个新的字符串对象"Hello World"
System.out.println(str1); // 输出: Hello
System.out.println(str2); // 输出: Hello World

不可变性带来了多个好处:

  • 安全性:在多线程环境中,多个线程可以安全地共享同一个字符串对象,而不用担心数据被修改。
  • 内存效率:不可变字符串可以被共享。当在常量池中创建一个字符串字面量时,后续对同一内容的引用会直接指向常量池中的对象,而不是创建新的对象,从而减少了内存消耗。
  • 哈希码缓存:由于字符串不可变,它们的哈希码只计算一次,并可以被缓存。这使得字符串在作为哈希表的键时更加高效,因为不需要在每次查找时重新计算哈希值。

1.2 字符串常量池

字符串常量池是Java中一个特殊的内存区域,用于存储字符串字面量和一些常量字符串。当使用字面量方式创建String对象时,JVM会首先检查字符串常量池中是否已经存在相同内容的字符串对象。如果存在,则直接返回常量池中该字符串对象的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。

String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // 输出: true,str1和str2引用的是同一个字符串对象

通过intern()方法,可以手动将一个String对象放入字符串常量池中。

String str1 = new String("Hello");
String str2 = str1.intern();
String str3 = "Hello";
System.out.println(str2 == str3); // 输出: true,str2和str3引用的是同一个字符串对象

1.3 内存结构

String对象在内存中的存储结构主要包括字符数组value、哈希码hash以及偏移量offset(在JDK 9及以后版本中,如果字符串内容都在Latin-1字符集内,则使用byte数组存储,并结合编码标识来区分)。

  • 字符数组value:用于存储字符串的字符序列。在JDK 9之前,使用的是UTF-16编码的字符数组;在JDK 9及以后版本中,如果字符串内容都在Latin-1字符集内,则使用byte数组存储。
  • 哈希码hash:用于缓存字符串的哈希码,以避免在每次调用hashCode()方法时重新计算。
  • 偏移量offset(在JDK 9及以后版本中):用于标识字符串在byte数组中的起始位置(如果字符串使用byte数组存储)。

1.4 JDK版本更新对String类的影响

随着JDK版本的更新,String类的底层设计和优化也在不断发展。

  • JDK 6:字符串常量池位于永久代(方法区)中,存储的是对象本身。
  • JDK 7:将常量池从永久代移到了堆内存中,存储的是对象的引用。
  • JDK 8:引入了元空间(Metaspace)来取代永久代,优化了字符串的创建和intern()方法的行为。
  • JDK 9:对String类进行了重大的底层优化,改用byte数组存储字符串数据(如果字符串内容都在Latin-1字符集内),并引入了coder字段和COMPACT_STRINGS属性来控制字符串的紧凑存储功能。

2. String类的用途

2.1 表示文本数据

字符串是计算机科学中用来表示文本数据的标准方式。在Java中,字符串常用于表示用户的姓名、地址等个人信息,以及程序中的各种文本信息。

2.2 处理文本数据

Java提供了丰富的内置函数和方法,使开发者能够轻松地进行文本数据的处理。例如,可以使用String类的各种方法来查找子字符串、替换字符、分割字符串等。

2.3 内存管理和性能优化

通过字符串常量池和不可变性设计,String类在内存管理和性能优化方面表现出色。它减少了内存中的重复字符串存储,提高了内存利用率和访问效率,并允许JVM对字符串进行缓存和重用,减少了创建新对象的开销。

3. String类的主要方法

3.1 创建字符串

3.1.1 直接赋值

String str = "Hello, World!";

这种方式创建的字符串对象会被放入字符串常量池中。

3.1.2 使用new关键字

String str = new String("Hello, World!");

这种方式会在堆内存中创建一个新的String对象,而不会放入字符串常量池中(除非通过intern()方法手动放入)。

3.1.3 字符数组转String

char[] array = {'a', 'b', 'c'};
String str = new String(array);

3.2 字符串比较

3.2.1 == 比较

String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");System.out.println(str1 == str2); // 输出: true,str1和str2引用的是同一个字符串对象
System.out.println(str1 == str3); // 输出: false,str1和str3引用的是不同的字符串对象

== 比较的是字符串对象的引用是否相同。

3.2.2 equals方法

System.out.println(str1.equals(str3)); // 输出: true,str1和str3的内容相同

equals方法比较的是字符串对象的内容是否相同。

3.2.3 compareTo方法

String str4 = "World";
System.out.println(str1.compareTo(str4)); // 输出: -11,按照字典顺序,"Hello"小于"World"

compareTo方法按字典顺序比较两个字符串的大小关系,返回值为int类型。

3.2.4 compareToIgnoreCase方法

System.out.println(str1.compareToIgnoreCase(str4)); // 输出: -11,忽略大小写比较

compareToIgnoreCase方法与compareTo方法类似,但在比较时忽略字母大小写。

3.3 获取长度

String str = "Hello, World!";
int length = str.length(); // 返回字符串的长度
System.out.println(length); // 输出: 13

3.4 字符串查找

3.4.1 charAt方法

char ch = str.charAt(7); // 返回指定索引位置上的字符
System.out.println(ch); // 输出: W

charAt方法返回指定索引位置上的字符,索引范围从0开始。

3.4.2 indexOf方法

int index = str.indexOf("World"); // 返回指定子字符串第一次出现的位置
System.out.println(index); // 输出: 7index = str.indexOf("Java", 8); // 从指定索引开始查找
System.out.println(index); // 输出: -1,未找到

indexOf方法返回指定子字符串第一次出现的位置,如果未找到则返回-1。

3.4.3 lastIndexOf方法

int lastIndex = str.lastIndexOf("o"); // 返回指定字符最后一次出现的位置
System.out.println(lastIndex); // 输出: 8lastIndex = str.lastIndexOf("o", 7); // 从指定索引开始反向搜索
System.out.println(lastIndex); // 输出: 4

lastIndexOf方法返回指定字符或子字符串最后一次出现的位置,如果未找到则返回-1。

3.4.4 contains方法

boolean containsHello = str.contains("Hello"); // 判断字符串是否包含指定的字符序列
System.out.println(containsHello); // 输出: true

contains方法用于判断字符串是否包含指定的字符序列。

3.5 子字符串

3.5.1 substring方法

String subStr = str.substring(7, 12); // 返回从beginIndex开始到endIndex-1的子字符串
System.out.println(subStr); // 输出: World

substring方法返回从beginIndex开始到endIndex-1的子字符串。

3.6 字符串替换

3.6.1 replace方法

String replacedStr = str.replace("World", "Java"); // 替换字符串中的字符
System.out.println(replacedStr); // 输出: Hello, Java!

replace方法用于替换字符串中的字符或子字符串。

3.6.2 replaceAll方法

String replacedAllStr = str.replaceAll("o", "0"); // 使用正则表达式替换字符串中的内容
System.out.println(replacedAllStr); // 输出: Hell0, W0rld!

replaceAll方法使用正则表达式替换字符串中的所有匹配项。

3.6.3 replaceFirst方法

String replacedFirstStr = str.replaceFirst("o", "0"); // 替换字符串中的第一个匹配项
System.out.println(replacedFirstStr); // 输出: Hello, W0rld!

replaceFirst方法替换字符串中的第一个匹配项。

3.7 大小写转换

3.7.1 toLowerCase方法

String lowerStr = str.toLowerCase(); // 将字符串转换为小写
System.out.println(lowerStr); // 输出: hello, world!

toLowerCase方法将字符串转换为小写。

3.7.2 toUpperCase方法

String upperStr = str.toUpperCase(); // 将字符串转换为大写
System.out.println(upperStr); // 输出: HELLO, WORLD!

toUpperCase方法将字符串转换为大写。

3.8 去除字符串首尾空格

String trimmedStr = str.trim(); // 去除字符串两端的空白字符
System.out.println(trimmedStr); // 输出: Hello, World!(假设原字符串两端没有空格)

trim方法用于去除字符串两端的空白字符(包括空格、制表符、换行符等)。

3.9 字符串拆分

String[] splitStrs = str.split(", "); // 根据正则表达式拆分字符串
for (String s : splitStrs) {System.out.println(s);
}
// 输出:
// Hello
// World!

split方法根据给定的正则表达式拆分字符串,并返回一个字符串数组。

3.10 字符串连接

3.10.1 使用+操作符

String concatenatedStr = "Hello" + " " + "World!"; // 使用+操作符连接字符串
System.out.println(concatenatedStr); // 输出: Hello World!

在编译时,Java会将多个字符串字面量的拼接优化为一个StringBuilder的append操作。

3.10.2 使用StringBuilder或StringBuffer

StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("World!");
String concatenatedStr = sb.toString();
System.out.println(concatenatedStr); // 输出: Hello World!

StringBuilder和StringBuffer都提供了可变字符串的操作,但StringBuilder是非线程安全的,而StringBuffer是线程安全的。在单线程环境中,建议使用StringBuilder以提高性能。

3.11 其他常用方法

3.11.1 startsWith方法

boolean startsWithHello = str.startsWith("Hello"); // 判断字符串是否以指定的前缀开始
System.out.println(startsWithHello); // 输出: true

startsWith方法用于判断字符串是否以指定的前缀开始。

3.11.2 endsWith方法

boolean endsWithExclamation = str.endsWith("!"); // 判断字符串是否以指定的后缀结束
System.out.println(endsWithExclamation); // 输出: true

endsWith方法用于判断字符串是否以指定的后缀结束。

3.11.3 toCharArray方法

char[] charArray = str.toCharArray(); // 将字符串转换为字符数组
for (char c : charArray) {System.out.print(c);
}
// 输出: Hello, World!

toCharArray方法将字符串转换为字符数组。

3.11.4 split(String regex, int limit)方法

String[] splitStrsWithLimit = str.split(", ", 2); // 根据正则表达式拆分字符串,并限制拆分次数
for (String s : splitStrsWithLimit) {System.out.println(s);
}
// 输出:
// Hello
// World!

split(String regex, int limit)方法根据给定的正则表达式拆分字符串,并限制拆分的次数。

4. String类的常见用法

4.1 字符串拼接

在Java中,字符串拼接是一个常见的操作。由于String的不可变性,直接使用+操作符进行字符串拼接可能会导致性能问题(特别是在循环中多次拼接字符串时)。因此,建议使用StringBuilder或StringBuffer来进行字符串拼接。

// 使用+操作符进行字符串拼接(不推荐在循环中使用)
String result = "";
for (int i = 0; i < 1000; i++) {result += "Hello ";
}
System.out.println(result);// 使用StringBuilder进行字符串拼接(推荐)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {sb.append("Hello ");
}
String resultWithStringBuilder = sb.toString();
System.out.println(resultWithStringBuilder);

4.2 字符串比较

在Java中,字符串比较通常使用equals方法而不是 ==操作符。因为==操作符比较的是字符串对象的引用是否相同,而equals方法比较的是字符串对象的内容是否相同。

String str1 = "Hello";
String str2 = new String("Hello");System.out.println(str1 == str2); // 输出: false,str1和str2引用的是不同的字符串对象
System.out.println(str1.equals(str2)); // 输出: true,str1和str2的内容相同

4.3 字符串查找和替换

在文本处理中,经常需要查找子字符串或替换字符串中的某些字符。String类提供了丰富的方法来支持这些操作。

String text = "Hello, welcome to the world of Java!";// 查找子字符串
int index = text.indexOf("welcome");
System.out.println("Index of 'welcome': " + index); // 输出: 7// 替换字符串中的字符
String replacedText = text.replace("Java", "Programming");
System.out.println(replacedText); // 输出: Hello, welcome to the world of Programming!

4.4 字符串拆分

在处理CSV文件或解析复杂字符串时,经常需要将字符串拆分为多个部分。String类的split方法提供了方便的方式来实现这一点。

String csvLine = "name,age,city";
String[] fields = csvLine.split(",");for (String field : fields) {System.out.println(field);
}
// 输出:
// name
// age
// city

4.5 字符串格式化

在输出格式化字符串时,可以使用String.format方法或Formatter类。

String name = "Alice";
int age = 30;
String formattedString = String.format("Name: %s, Age: %d", name, age);
System.out.println(formattedString); // 输出: Name: Alice, Age: 30

总结

Java中的String类是一个重要的内置类,用于处理字符串数据。其特点包括不可变性、字符串常量池、特定的内存结构以及随JDK版本更新的优化。String类广泛用于表示和处理文本数据,并在内存管理和性能优化方面表现出色。主要方法包括创建字符串、字符串比较、获取长度、字符串查找、子字符串操作、字符串替换、大小写转换、去除首尾空格、字符串拆分和连接等。常见用法涉及字符串拼接、比较、查找和替换、拆分以及格式化。通过合理使用String类的方法,可以有效处理文本数据并优化程序性能。

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

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

相关文章

基于人脸识别的课堂考勤系统

该项目是一个基于人脸识别的课堂考勤系统&#xff0c;使用Python开发&#xff0c;结合了多种技术实现考勤功能。要开发类似的基于人脸识别的考勤系统&#xff0c;可参考以下步骤&#xff1a; 环境搭建&#xff1a;利用Anaconda创建虚拟环境&#xff0c;指定Python版本为3.8&am…

Ubuntu安装GitLab

在 Ubuntu 上安装 GitLab 的步骤如下。这里以 GitLab Community Edition&#xff08;CE&#xff09;为例&#xff1a; 前提条件 确保你的 Ubuntu 系统是 20.04 或更高版本。确保你的系统满足 GitLab 的硬件要求。 步骤 更新系统包&#xff1a; sudo apt update sudo apt upg…

Vue.js 的介绍与组件开发初步

Vue.js 的介绍与组件开发初步 Vue.js 的介绍与组件开发初步引言第一部分&#xff1a;Vue.js 基础入门1.1 什么是 Vue.js&#xff1f;1.2 搭建 Vue.js 开发环境安装 Node.js 和 npm安装 Vue CLI创建新项目运行示例 1.3 第一个 Vue.js 示例 第二部分&#xff1a;Vue.js 组件开发基…

架构技能(四):需求分析

需求分析&#xff0c;即分析需求&#xff0c;分析软件用户需要解决的问题。 需求分析的下一环节是软件的整体架构设计&#xff0c;需求是输入&#xff0c;架构是输出&#xff0c;需求决定了架构。 决定架构的是软件的所有需求吗&#xff1f;肯定不是&#xff0c;真正决定架构…

Linux:线程池和单例模式

一、普通线程池 1.1 线程池概念 线程池&#xff1a;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价&…

Leetcode598:区间加法 II

题目描述&#xff1a; 给你一个 m x n 的矩阵 M 和一个操作数组 op 。矩阵初始化时所有的单元格都为 0 。ops[i] [ai, bi] 意味着当所有的 0 < x < ai 和 0 < y < bi 时&#xff0c; M[x][y] 应该加 1。 在 执行完所有操作后 &#xff0c;计算并返回 矩阵中最大…

C++泛型编程指南04-(对默认调用参数的类型推断)

文章目录 问题描述解决方案示例代码 关键点解释进一步改进&#xff1a;结合概念约束 你提到的情况确实是一个常见的问题&#xff1a;在C中&#xff0c;类型推断不适用于默认调用参数。这意味着如果你希望函数模板能够通过默认参数来实例化&#xff0c;你需要为模板参数提供一个…

在 Ubuntu 上安装 Node.js 23.x

在 Ubuntu 上安装 Node.js 23.x 前提条件安装步骤1. 下载设置脚本2. 运行设置脚本3. 安装 Node.js4. 验证安装 参考链接总结 在现代 web 开发中&#xff0c;Node.js 是一个不可或缺的工具。它提供了一个强大的 JavaScript 运行时环境&#xff0c;使得开发人员可以在服务器端使用…

maven mysql jdk nvm node npm 环境安装

安装JDK 1.8 11 环境 maven环境安装 打开网站 下载 下载zip格式 解压 自己创建一个maven库 以后在idea 使用maven时候重新设置一下 这三个地方分别设置 这时候maven才算设置好 nvm 管理 npm nodejs nvm下载 安装 Releases coreybutler/nvm-windows GitHub 一键安装且若有…

【B站保姆级视频教程:Jetson配置YOLOv11环境(六)PyTorchTorchvision安装】

Jetson配置YOLOv11环境&#xff08;6&#xff09;PyTorch&Torchvision安装 文章目录 1. 安装PyTorch1.1安装依赖项1.2 下载torch wheel 安装包1.3 安装 2. 安装torchvisiion2.1 安装依赖2.2 编译安装torchvision2.2.1 Torchvisiion版本选择2.2.2 下载torchvisiion到Downloa…

数科OFD证照生成原理剖析与平替方案实现

数科OFD证照生成原理剖析及C#平替方案实现 1. OFD证照生成原理 OFD&#xff08;Open Fixed-layout Document&#xff09;是一种基于XML的固定版式文档格式&#xff0c;广泛应用于电子发票、电子证照等领域。数科OFD证照生成工具的核心原理包括以下几个方面&#xff1a; OFD文…

【算法-位运算】位运算遍历 LogTick 算法

文章目录 1. 引入2. LogTick 优化遍历过程3. 题目3.1 LeetCode3097 或值至少为 K 的最短子数组 II3.2 LeetCode2411 按位或最大的最小子数组长度3.3 LeetCode3209 子数组按位与值为 K 的数目3.4 LeetCode3171 找到按位或最接近 K 的子数组3.5 LeetCode1521 找到最接近目标值的函…

NLP深度学习 DAY5:Sequence-to-sequence 模型详解

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于处理输入和输出均为序列任务的深度学习模型。它最初被设计用于机器翻译&#xff0c;但后来广泛应用于其他任务&#xff0c;如文本摘要、对话系统、语音识别、问答系统等。 核心思想 Seq2Seq 模型的目标是将…

处理 **5万字(约7.5万-10万token,中文1字≈1.5-2token)** 的上下文

处理 5万字&#xff08;约7.5万-10万token&#xff0c;中文1字≈1.5-2token&#xff09; 的上下文&#xff0c;对模型的长文本处理能力和显存要求较高。以下是不同规模模型的适用性分析及推荐&#xff1a; 一、模型规模与上下文能力的关系 模型类型参数量最大上下文长度&#…

吴恩达深度学习——优化神经网络

本文来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 优化样本大小mini-batch 优化梯度下降法动量梯度下降法指数加权平均概念偏差纠正 动量梯度下降法 RMSpropAdam优化算法 优化学习率局部最优问题&#xff08;了解&#xff09; 优…

Shell篇-字符串处理

目录 1.变量引用 2.获取字符串长度 3.字符串截取 4.删除子字符串 5.字符串替换 总结&#xff1a; Bash&#xff08;Shell 脚本&#xff09;中的字符串处理语法。以下是对其的介绍和总结&#xff1a;Bash 变量可以使用不同的语法来获取、修改和删除字符串的内容。图片中列…

面经——C语言——指针大小,内存分配,野指针,大小端

文章目录 指针大小分段C语言内存分配1. 静态内存分配1.1 栈内存分配1.2 数据段内存分配 2. 动态内存分配2.1 malloc&#xff08;Memory Allocation&#xff09;2.2 free&#xff08;Free Memory&#xff09; 动态内存分配的流程例子 野指针野指针的原因&#xff1a;防止野指针的…

CMake项目编译与开源项目目录结构

Cmake 使用简单方便&#xff0c;可以跨平台构建项目编译环境&#xff0c;尤其比直接写makefile简单&#xff0c;可以通过简单的Cmake生成负责的Makefile文件。 如果没有使用cmake进行编译&#xff0c;需要如下命令&#xff1a;&#xff08;以muduo库echo服务器为例&#xff09;…

书生大模型实战营7

文章目录 L1——基础岛提示词工程实践什么是Prompt(提示词)什么是提示工程提示设计框架CRISPECO-STAR LangGPT结构化提示词LangGPT结构编写技巧构建全局思维链保持上下文语义一致性有机结合其他 Prompt 技巧 常用的提示词模块 浦语提示词工程实践(LangGPT版)自动化生成LangGPT提…

Shadow DOM举例

这东西具有隔离效果&#xff0c;对于一些插件需要append一些div倒是不错的选择 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"utf-8"> <title>演示例子</title> </head> <body> <style&g…