Java中String类常用用法详解:从创建、比较到操作

键盘旁的猫
键盘旁的猫
发布时间:2026-10-16 | 4人浏览过
原创

本文将全面解析Java语言中String类的使用技巧。内容涵盖字符串的多种创建途径、引用与内容的比较逻辑、常量池运行机制,以及substring、split等核心API的操作规范。同时针对高频的字符串拼接场景,详细对比了String、StringBuilder和StringBuffer三者的特性与适用环境。

一、创建字符串

在Java中,创建字符串对象主要有以下三种常见方式:

  1. 直接赋值:通过双引号直接定义。
  2. 构造器实例化:使用 new String() 显式创建对象。
  3. 数组转换:利用字符数组或字节数组作为参数进行构建。

以下为具体的代码示例:

// 方式一:直接赋值(最简洁)
String str = "Hello Bit";

// 方式二:使用构造器
String str2 = new String("Hello Bit");

// 方式三:字符数组转字符串
char[] array = {'a', 'b', 'c'};
String str3 = new String(array);

第三种方式的核心在于底层调用了数组拷贝逻辑,将传入的字符数组内容逐一读取并组装成字符串。理解这一过程有助于后续掌握字符串不可变性的实现基础。

二、字符、字节与字符串的转换

第1步:字符与字符串互转

由于字符串本质上是字符序列,二者之间的转换主要依赖构造方法与内置方法。

字符数组转字符串:

可以通过构造函数指定范围。若只传入数组,则全部转化;若传入偏移量(offset)和长度(count),则截取对应部分。

char[] val = {'a','b','c','d','e'};
// 从下标0开始,取3个字符 -> "abc"
String str1 = new String(val, 0, 3);
System.out.println(str1);

// 从下标1开始,取3个字符 -> "bcd"
String str2 = new String(val, 1, 3);
System.out.println(str2);

字符串转字符:

  • 获取单个字符:使用 charAt(index) 方法,返回指定索引处的 char。
  • 转为字符数组:使用 toCharArray() 方法,将完整字符串拆解为 char[]。
String str = "abc";
System.out.println(str.charAt(1)); // 输出 b
char[] chars = str.toCharArray();

第2步:字节与字符串互转

涉及网络传输或文件读写时,常需要进行字符编码与字节的转换。

字节数组转字符串:

同样支持指定起始位置和长度的构造方式,默认使用系统平台编码(如UTF-8或GBK),也可指定字符集。

byte[] bytes = {97, 98, 99, 100};
String str1 = new String(bytes, 0, 3); // "abc"
String str2 = new String(bytes, 1, 3); // "bcd"

字符串转字节数组:

调用 getBytes() 方法可将字符串按默认编码转为对应的ASCII码值数组。

String s = "abc";
byte[] data = s.getBytes();
// 结果通常打印为: [97, 98, 99]

三、字符串的比较与常量池

初学者常混淆 == 运算符与 equals() 方法的区别。对于基本数据类型,== 比较值;而对于 String 引用类型,== 比较的是内存地址(引用是否指向同一对象),而 equals() 比较的是字符串的内容。

1. 字符串常量池 (String Pool)

自JDK 1.7起,字符串常量池被移至堆内存中,其底层实现可视为一个哈希表结构,用于高效复用已存在的字符串常量,节省内存空间。

  • 直接赋值:编译器会在编译期检查常量池,若存在相同值的对象则直接返回引用(即栈内存中的变量指向堆中的池对象)。
  • new关键字:强制在堆中新建一个对象,即使池中已有该字符串,两者地址仍不相同。

简单验证:

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true,指向池中同一对象

String s3 = "hello";
String s4 = new String("hello");
System.out.println(s3 == s4); // false,s4指向堆中全新对象

手动入池 (intern()):

intern() 方法可以将当前字符串尝试放入常量池。若池中已有,则返回池中的引用;若无,则将当前字符串存入池中并返回引用。这使得 new String(...).intern() 的结果与直接赋值的变量相等。

String s = new String("hello").intern();
System.out.println(s == "hello"); // true

2. 编译期优化与反编译查看

对于静态字符串常量,Java编译器在编译阶段会执行常量折叠(Constant Folding),直接将 "he" + "llo" 优化为 "hello"。

操作步骤:

  1. 编译项目(Build Project)生成 .class 文件。
  2. 打开命令行终端(PowerShell/CMD),定位到 out/production/包名/ 目录下。
  3. 使用命令 javap -c 类名.class 查看字节码,确认拼接是否已在编译时完成。

运行期拼接:

如果涉及变量拼接,例如 new String("1") + new String("1"),编译器无法在运行前确定结果,因此会在运行期创建 StringBuilder(或 StringBuffer)进行动态拼接。此类生成的字符串对象默认不会自动进入常量池,除非显式调用 intern()。

3. 字符串内容比较方法总结

  • equals(Object obj):严格区分大小写比较内容。
  • equalsIgnoreCase(String anotherString):忽略大小写比较内容。
  • compareTo(String anotherString):字典序比较。若前者大返回正数,后者大返回负数,相等返回0。计算差值为两个字符串第一个不同字符的 ASCII 差值,若前缀相同则返回长度差。

四、常用字符串操作方法

1. 查找与判断

方法作用说明
contains(CharSequence s)判断是否包含子串,返回 boolean
indexOf(String str)从头查找子串首次出现的索引,找不到返回 -1
lastIndexOf(String str)从后往前查找子串首次出现的位置(正向索引),支持指定起始位置搜索
startsWith(String prefix)判断是否以某子串开头
endsWith(String suffix)判断是否以某子串结尾

示例:

String s = "abbabcacc";
boolean hasSub = s.contains("abc"); // true
int idx = s.indexOf("abc"); // 3
int lastIndex = s.lastIndexOf("ac", 6); // 6

2. 替换 (Replace)

由于 String 是不可变的,所有替换方法均返回新的字符串对象,原对象不变。

  • replaceAll(String regex, String replacement):正则匹配全局替换。
  • replaceFirst(String regex, String replacement):仅替换第一次匹配到的子串。
String s = "helloworld";
System.out.println(s.replaceFirst("l", "_")); // he_loworld

3. 拆分 (Split)

split(String regex) 接收正则表达式作为分割符。

  • 处理元字符:点 .、管道 |、星号 * 等在正则中有特殊含义,使用前需转义。例如 IP 地址分割应使用 \\.。
  • 多条件分隔:可使用 | 连接多个正则,如 split(" |&|#")。
  • 限制分割次数:第二个参数 limit 控制最大分割块数。若 limit <= 0 则全部分割;正整数表示最多保留该数量的组。
String ip = "192.168.1.1";
String[] parts = ip.split("\\."); 
// 结果为 [192, 168, 1, 1]

4. 截取 (Substring)

采用“左闭右开”原则(包含 start,不包含 end)。

  • substring(int beginIndex):从指定位置截取至末尾。
  • substring(int beginIndex, int endIndex):截取指定范围。
String s = "helloworld";
s.substring(5);       // "world" (从索引5 'w' 开始)
s.substring(0, 5);    // "hello" (0到4)

5. 其他便捷方法

  • trim():去除首尾空白字符。
  • toUpperCase() / toLowerCase():大小写转换,非字母字符保持不变。
  • concat(String str):连接字符串(等效于 +)。
  • length():返回字符数量(注意是方法而非属性)。
  • isEmpty():判断字符串是否为空(长度为0)。

五、可变字符串:StringBuilder 与 StringBuffer

频繁的字符串修改若继续使用 String,会导致大量临时对象产生并触发垃圾回收。StringBuilder 和 StringBuffer 是可变的字符序列,适合在高并发拼接场景中使用。

1. 核心区别

两者的 API 几乎完全一致,唯一核心区别在于 StringBuffer 的方法加上了 synchronized 关键字,属于线程安全类;而 StringBuilder 未做同步处理,线程不安全但效率更高。建议在单线程环境下优先使用 StringBuilder。

2. 常用操作演示

这两个类支持在原有内存缓冲区上进行增删改,避免频繁创建新对象。

StringBuilder sb = new StringBuilder("hello");
// 追加
sb.append(" world"); 
// 反转
sb.reverse(); // dlrow olleh
// 删除指定区间
sb.delete(0, 6); 
// 插入
sb.insert(0, "hi ");

六、关于引用的易错点解析

在 Java 方法传参时,实参是形参的一份副本。对于引用类型的变量:

  • 基本类型/不可变对象:若在方法内改变引用(如指向新对象),外部变量不受影响。
  • 可变对象(如数组、StringBuilder):若在方法内修改对象内部状态(如改变数组元素或 append),外部能看到变化。

代码验证:

public static void func(String str1, char[] chars) {
    str1 = "hello";     // 只是改变了局部变量 str1 的指向
    chars[0] = 'g';     // 改变了数组对象内部的数据
}
public static void main(String[] args) {
    String s = "abcd";
    char[] c = {'h', 'e', 'l', 'l', 'o'};
    func(s, c);
    System.out.println(s);     // 依然输出 abcd
    System.out.println(Arrays.toString(c)); // 输出 [g, e, l, l, o]
}

以上就是Java中String类以及各种字符串工具类用法的详细内容,更多关于Java基础资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表