杰瑞科技汇

java string 字符数组

核心区别:String vs char[]

虽然 String 在底层是通过字符数组实现的,但它们在 Java 中是完全不同的两个概念。

java string 字符数组-图1
(图片来源网络,侵删)
特性 String char[]
本质 一个不可变的类 一个可变的数组
存储位置 存储在字符串常量池堆内存中。 作为对象,存储在堆内存中。
功能丰富度 提供了大量丰富的方法来处理字符串(如 length(), indexOf(), split(), matches() 等)。 功能相对基础,主要是对字符数组的操作(如 length, get, set)。
安全性 线程安全,因为不可变,所以多个线程可以同时访问一个 String 而无需同步。 非线程安全,在多线程环境下需要额外同步机制来保证安全。
性能 频繁的修改操作会创建大量新对象,可能导致性能下降内存浪费(GC 压力)。 修改效率高,因为是在原数组上进行操作,不会创建新对象。

Stringchar[] 的相互转换

1 char[]String

有几种方法可以实现,其中最常用和推荐的是使用 String 的构造函数。

使用构造函数(推荐)

这是最直接、最清晰的方式。

char[] chars = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
// 使用 String(char[]) 构造函数
String str1 = new String(chars);
System.out.println(str1); // 输出: Hello World
// 使用 String(char[], offset, count) 构造函数,截取部分字符
// 从索引 2 开始,取 3 个字符
String str2 = new String(chars, 2, 3);
System.out.println(str2); // 输出: llo

使用 String.valueOf() (静态方法)

java string 字符数组-图2
(图片来源网络,侵删)

这个方法更简洁,功能上等同于构造函数。

char[] chars = {'J', 'a', 'v', 'a'};
String str = String.valueOf(chars);
System.out.println(str); // 输出: Java

注意: 不要使用 toString() 方法 char[] 数组继承自 Object,其 toString() 方法返回的是类似 "[C@15db9742" 的内存地址表示,而不是字符序列。

char[] chars = {'J', 'a', 'v', 'a'};
System.out.println(chars.toString()); // 输出类似 [C@15db9742,这是错误的

2 Stringchar[]

使用 String 类的 toCharArray() 方法即可。

String str = "Java Programming";
// 将 String 转换为 char[]
char[] chars = str.toCharArray();
// 打印字符数组
System.out.println(Arrays.toString(chars)); 
// 输出: [J, a, v, a,  , P, r, o, g, r, a, m, m, i, n, g]
// 访问特定字符
System.out.println("第一个字符是: " + chars[0]); // 输出: J
System.out.println("最后一个字符是: " + chars[chars.length - 1]); // 输出: g

为什么 String 要设计成不可变?

String 的不可变性是 Java 设计中的一个核心决策,主要有以下几个原因:

java string 字符数组-图3
(图片来源网络,侵删)
  1. 线程安全

    • 因为 String 对象一旦创建就不能改变,所以它在任何环境下都是线程安全的,多个线程可以同时读取一个 String 而无需担心数据被其他线程修改,这对于在并发编程中广泛使用的字符串至关重要。
  2. 哈希码缓存

    • String 对象的 hashCode() 在对象创建时就被计算并缓存了,因为内容不变,所以哈希码也永远不变,这使得 String 非常适合作为 HashMapHashSetHashtable 等哈希表的键。String 可变,那么它的哈希码也可能改变,这会导致在哈希表中找不到它,从而破坏数据结构。
  3. 字符串常量池

    • 为了提高性能和减少内存占用,Java 使用了一个特殊的内存区域——字符串常量池
    • 当你创建一个字符串字面量(如 String s = "hello";)时,JVM 首先会在池中查找是否已经存在 "hello",如果存在,就直接返回引用;如果不存在,就创建一个并放入池中。
    • 因为 String 不可变,JVM 可以安全地让多个变量引用池中的同一个字符串对象,而不用担心一个变量的修改会影响另一个。String 可变,这种机制将无法实现。
  4. 安全性

    • 许多 Java 的安全类(如加载器、安全管理器)都使用 String 来表示文件路径、类名、权限等信息。String 是可变的,恶意代码可以修改这些字符串,从而绕过安全检查,一个不可变的文件路径字符串可以安全地传递给不同的模块,而不必担心它被意外或恶意地修改为指向敏感文件。

实际应用场景示例

场景1:需要频繁修改字符内容

错误/低效的做法:使用 String

// 错误示范:频繁修改 String,会创建大量新对象,性能差
String text = "Hello";
text += " ";
text += "World";
text += "!";
// 这段代码实际上创建了 4 个 String 对象,并产生了 3 次不必要的对象创建和垃圾回收。

正确的做法:使用 StringBuilderStringBuffer

StringBuilder 内部就是一个可变的字符数组,专门用于处理字符串的拼接和修改。

// 正确示范:使用 StringBuilder,性能高
StringBuilder sb = new StringBuilder(); // 内部维护一个 char[]
sb.append("Hello");
sb.append(" ");
sb.append("World");
sb.append("!");
String result = sb.toString();
System.out.println(result); // 输出: Hello World!

场景2:需要随机访问和修改单个字符

正确的做法:使用 char[]

char[] name = {'J', 'a', 'v', 'a'};
// 修改第二个字符为大写
name[1] = 'A'; // 直接在原数组上修改,非常高效
String newName = new String(name);
System.out.println(newName); // 输出: JAvA

如果用 String,每次修改都需要创建新对象,非常低效。


需求 推荐的数据结构 原因
存储文本,且内容不变 String 利用不可变性带来的线程安全、性能优化(常量池)和安全性。
需要频繁拼接、修改字符串 StringBuilder (单线程) / StringBuffer (多线程) 内部使用可变字符数组,修改效率高,避免创建大量临时对象。
需要随机访问和修改单个字符 char[] 数组的特性,可以通过索引直接访问和修改,效率最高。
需要将字符串作为键在 HashMap 中使用 String 不可变性保证了哈希码的稳定,使其成为完美的键。

理解 Stringchar[] 的区别和联系,是编写高效、健壮的 Java 代码的基础。

分享:
扫描分享到社交APP
上一篇
下一篇