源本科技 | 码上会

Java 中的 String 类

2025/12/26
22
0

学习目标

  • 理解 String 类的核心特性:不可变性、线程安全与接口实现

  • 掌握 String 的常用构造方法及其适用场景

  • 能区分字面量创建与 new 关键字创建字符串的内存差异

  • 了解 String 在 Java 体系中的基础地位与设计哲学


String 核心特性

不可变性

String 对象一旦创建,其内容无法被修改。这是 Java 字符串设计的基石。

public class Main {
    public static void main(String[] args) {
        String text = "hello";
        // text.charAt(0) = 'H'; // ❌ 编译错误!
    }
}

🔍 原因解析

  • charAt(0) 返回的是一个 char 值(右值),不是可赋值的变量(左值)

  • 所有“修改”操作(如 toUpperCase()replace())都返回新对象,原对象不变

优势

  • 支持字符串常量池(节省内存)

  • 天然线程安全

  • 可作为 HashMap 的可靠键


线程安全

由于不可变,多个线程可并发读取同一个 String 对象而无需加锁。

// 安全地在多线程中共享
public static final String CONFIG_PATH = "/app/config.json";

// 任何线程调用都不会导致数据不一致

丰富的工具方法

Stringjava.lang 包中的预定义 final 类,提供大量实用方法:

String str = "hello coder";
System.out.println("长度: " + str.length());         // 11
System.out.println("转大写: " + str.toUpperCase()); // HELLO CODER

常用方法包括:length()charAt()substring()equals()indexOf()、trim()

详见《Java 字符串常用方法详解


实现的关键接口

String 类实现了三个重要接口,赋予其强大能力:

接口

作用

CharSequence

提供统一的字符序列访问(charAt(), length()

Comparable<String>

支持字典序比较(compareTo()

Serializable

支持序列化(可转换为字节流,用于网络传输或持久化)

// 因为实现了 Comparable,可以直接排序
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names); // 按字母顺序排序

String 构造方法

虽然通常使用字面量(如 String s = "Hello";),但 String 类提供了多种构造器以满足不同数据源的转换需求。

基础示例

public class CoderHub {
    public static void main(String[] args) {
        // 1. 使用 new 关键字(不推荐常规使用)
        String s1 = new String("Hello Java");
        
        // 2. 从字符数组创建
        char[] chars = {'J', 'A', 'V', 'A'};
        String s2 = new String(chars);
        
        // 3. 从字节数组创建(默认平台编码)
        byte[] bytes = {72, 101, 108, 108, 111}; // ASCII: H e l l o
        String s3 = new String(bytes);

        System.out.println(s1); // Hello Java
        System.out.println(s2); // JAVA
        System.out.println(s3); // Hello
    }
}

⚠️ 注意:new String("text") 会在堆中创建新对象,即使常量池中已存在相同内容,通常应避免。


构造方法速查

构造方法

说明

String(byte[] bytes)

使用平台默认字符集解码字节数组

String(byte[] bytes, Charset charset)

指定字符集解码(推荐,避免乱码)

String(byte[] bytes, int offset, int length)

从字节数组指定位置截取

String(char[] value)

从整个字符数组创建字符串

String(char[] value, int offset, int count)

从字符数组指定位置截取

String(int[] codePoints, int offset, int count)

从 Unicode 码点数组创建(支持 emoji 等)

String(StringBuffer buffer)

StringBuffer 创建(线程安全)

String(StringBuilder builder)

StringBuilder 创建(高效)


实战:安全处理字节流

import java.nio.charset.StandardCharsets;

public class SafeStringCreation {
    public static void main(String[] args) {
        byte[] data = {(byte)0xE4, (byte)0xBD, (byte)0xA0, (byte)0xE5, (byte)0xA5, (byte)0xBD}; // "你好" 的 UTF-8 字节
        
        // ✅ 显式指定 UTF-8,避免平台依赖导致乱码
        String safe = new String(data, StandardCharsets.UTF_8);
        System.out.println(safe); // 你好
        
        // ❌ 危险:依赖默认编码(Windows 可能是 GBK,Linux 是 UTF-8)
        // String unsafe = new String(data);
    }
}

重点总结

  • 不可变性String 设计的核心,带来安全性、线程安全与性能优化

  • 线程安全源于状态不可变,无需同步机制

  • 丰富方法覆盖文本处理绝大多数场景

  • 多接口实现使其能融入 Java 集合、IO、序列化等体系

  • 构造方法多样,适用于字节流、字符数组等底层数据转换

  • 内存管理需理解常量池与堆的区别,避免不必要的 new

设计哲学:“简单性、安全性、一致性” —— String 的不可变设计体现了 Java 对可靠性优先于灵活性的工程取舍。


思考题

  1. 为什么 String 被设计为 final 类?如果允许继承,可能带来哪些安全风险?

  2. 在网络编程中接收字节流时,为什么必须显式指定字符集(如 UTF-8)来构造 String?请举例说明默认编码可能导致的问题。

  3. 给定以下代码,共创建了多少个 String 对象?分别位于内存哪个区域?

    String s1 = "Code";
    String s2 = new String("Code");
    String s3 = s2.intern();
    String s4 = "Code";