Java中String类常用用法详解:从创建、比较到操作
本文将全面解析Java语言中String类的使用技巧。内容涵盖字符串的多种创建途径、引用与内容的比较逻辑、常量池运行机制,以及substring、split等核心API的操作规范。同时针对高频的字符串拼接场景,详细对比了String、StringBuilder和StringBuffer三者的特性与适用环境。
一、创建字符串
在Java中,创建字符串对象主要有以下三种常见方式:
- 直接赋值:通过双引号直接定义。
- 构造器实例化:使用
new String()显式创建对象。 - 数组转换:利用字符数组或字节数组作为参数进行构建。
以下为具体的代码示例:
// 方式一:直接赋值(最简洁)
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"。
操作步骤:
- 编译项目(Build Project)生成
.class文件。 - 打开命令行终端(PowerShell/CMD),定位到
out/production/包名/目录下。 - 使用命令
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基础资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













