聊聊 String 的常用方法

本文深入解析Java中String类的内部实现原理,包括其不可变性、字符存储方式及实现的接口。同时,全面介绍了String类的常用方法,如length()、charAt()、toLowerCase()、toUpperCase()、equals()、concat()、substring()等,并通过示例代码演示了这些方法的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看 String 的底层源码:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

String 类代表字符串,字符串是常量 ,用双引号引起来表示。 它的值在创建之后不能更改 。

由final修饰,代表不可变的字符序列 ;
实现了序列化、Comparable接口和 CharSequence 接口;
String 对象的字符内容是存储在一个 char 型的数组中
常用方法:

int length() 返回此字符串的长度
char charAt(int index) 返回 char指定索引处的值
boolean isEmpty() 判断是否是空字符串
String toLowerCase() 将 String 中的所有字符转换为小写
String toUpperCase() 将 String 中的所有字符转换为大写
boolean equalsIgnoreCase(String anotherString) 判断是否相等,忽略大小写
boolean equals(Object obj) 比较字符串的内容是否相同
String trim() 返回一个字符串,其值为此字符串,并删除任何前导和尾随空格
String concat(String str) 将指定的字符串连接到该字符串的末尾
String substring(int beginIndex) 返回一个字符串,该字符串是此字符串的子字符串
String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串
int compareTo(String anotherString) 按字典顺序比较两个字符串
测试代码:

public class StringMethodTest1 {
    public static void main(String[] args) {
        String s = "HelloWorld";
        // int length() 返回此字符串的长度
        System.out.println(s.length()); // 10

        // char charAt(int index) 返回 char指定索引处的值
        System.out.println(s.charAt(1)); // e

        // boolean isEmpty() 判断是否是空字符串
        System.out.println(s.isEmpty()); // false

        // String toLowerCase() 将 String 中的所有字符转换为小写
        System.out.println(s.toLowerCase()); // helloworld

        // String toUpperCase() 将 String 中的所有字符转换为大写
        System.out.println(s.toUpperCase()); // HELLOWORLD

        // boolean equalsIgnoreCase(String anotherString) 判断是否相等,忽略大小写
        String s1 = "HELLOworld";
        System.out.println(s.equalsIgnoreCase(s1)); // true

        // boolean equals(Object obj) 比较字符串的内容是否相同
        System.out.println(s.equals(s1)); // false

        String s2 = " HelloChina ";
        // String trim() 返回一个字符串,其值为此字符串,并删除任何前导和尾随空格
        System.out.println(s2.trim()); // HelloChina

        // String concat(String str) 将指定的字符串连接到该字符串的末尾
        System.out.println(s.concat(s2)); // HelloWorld HelloChina

        // String substring(int beginIndex) 返回一个字符串,该字符串是此字符串的子字符串
        System.out.println(s.substring(5)); // world

        // String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串
        System.out.println(s.substring(5, 8)); // wor

        // int compareTo(String anotherString) 按字典顺序比较两个字符串
        System.out.println(s.compareTo(s1)); // 32

    }
}

代码执行结果:

10
e
false
helloworld
HELLOWORLD
true
false
HelloChina
HelloWorld HelloChina 
World
Wor
32

boolean contains(CharSequence s) 当且仅当此字符串包含指定的char值序列时才返回true
int indexOf(String str) 返回指定子字符串第一次出现的字符串内的索引
int indexOf(String str, int fromIndex) 返回指定子串的第一次出现的字符串中的索引,从指定的索引开始
int lastIndexOf(String str) 返回指定子字符串最后一次出现的字符串中的索引
int lastIndexOf(String str, int fromIndex) 返回指定子字符串的最后一次出现的字符串中的索引,从指定索引开始反向搜索
boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开头
boolean startsWith(String prefix, int toffset) 测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头
boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结尾
测试代码:

public class StringMethodTest2 {
    public static void main(String[] args) {
        String s = "HelloWorld";

        // boolean contains(CharSequence s) 当且仅当此字符串包含指定的char值序列时才返回true。
        System.out.println(s.contains("or")); // true

        // int indexOf(String str) 返回指定子字符串第一次出现的字符串内的索引
        System.out.println(s.indexOf("ll")); // 2

        // int indexOf(String str, int fromIndex) 返回指定子串的第一次出现的字符串中的索引,从指定的索引开始
        System.out.println(s.indexOf("o", 5)); // 6

        // int lastIndexOf(String str) 返回指定子字符串最后一次出现的字符串中的索引。
        System.out.println(s.lastIndexOf("o")); // 6

        // int lastIndexOf(String str, int fromIndex) 返回指定子字符串的最后一次出现的字符串中的索引,从指定索引开始反向搜索。
        System.out.println(s.lastIndexOf("o", 5)); // 4

        // boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开头
        System.out.println(s.startsWith("He")); // true

        // boolean startsWith(String prefix, int toffset) 测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头
        System.out.println(s.startsWith("W", 5)); // true

        // boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结尾
        System.out.println(s.endsWith("ld")); // true

    }
}

代码执行结果:

true
2
6
6
4
true
true
true

String replace(char oldChar, char newChar) 字符串替换,返回一个新的字符串
String replace(CharSequence target, CharSequence replacement) 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列
boolean matches(String regex) 告诉这个字符串是否匹配给定的 regular expression
String[] split(String regex) 将此字符串分割为给定的 regular expression的匹配
测试代码:

public class StringMethodTest3 {
    public static void main(String[] args) {

        String s = "HelloWorld";

        // String replace(char oldChar, char newChar) 字符串替换,返回一个新的字符串
        System.out.println(s.replace("H", "A")); // AelloWorld

        // String replace(CharSequence target, CharSequence replacement)
        // 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
        String s1 = s.replace("World", "China");
        System.out.println(s); // HelloWorld
        System.out.println(s1); // HelloChina

        // boolean matches(String regex) 告诉这个字符串是否匹配给定的 regular expression
        String s2 = "123,456";
        boolean matches = s2.matches("\\d+");
        System.out.println(matches); // false

        // String[] split(String regex) 将此字符串分割为给定的 regular expression的匹配。
        String[] split = s2.split(",");

        for (String str : split) {
            System.out.print(str + " "); // 123 456
        }

    }
}

代码执行结果:

AelloWorld
HelloWorld
HelloChina
false
123 456

 

<think>好的,用户让我详细聊一下Java中的record类型。首先,我需要回忆一下record类型的基本概念,它是Java 14引入的一个预览特性,后来在Java 16中正式成为标准特性。用户可能已经有一定的Java基础,但可能对record不太熟悉,或者想深入了解其设计哲学和使用场景。 接下来,我需要确定用户的需求。他们可能是在学习Java的新特性,或者在实际开发中遇到了需要使用不可变数据的情况,想要了解如何更高效地处理数据载体类。也有可能他们在比较record和传统的类(如POJO)之间的区别,想知道使用record的好处和限制。 然后,我要考虑用户可能的疑问点。例如,record和普通类有什么不同?为什么需要引入record?它解决了什么问题?在实际项目中如何正确使用?是否存在什么局限性?这些都需要在回答中覆盖到,确保用户得到全面的信息。 我还需要检查是否有相关的系统级指令需要注意。比如,用户之前提到过结构化并发,但这次的问题是关于record类型的,所以不需要涉及之前的主题。同时,要确保使用正确的中文术语,避免技术术语的混淆,可能需要用一些例子来帮助理解。 接下来,我需要组织回答的结构。通常,介绍一个概念可以从定义开始,然后到设计动机、核心特性、使用场景、与传统方式的对比、注意事项等。这样结构清晰,用户能逐步理解。同时,要加入代码示例,让用户看到实际应用,这样更容易理解理论部分。 在准备代码示例时,要注意对比传统的类和record的不同写法,突出record的简洁性。例如,展示一个传统的数据载体类需要手动编写构造函数、getter、equals、hashCode、toString方法,而用record只需一行代码,这样对比明显,用户能直观感受到便利。 同时,要提醒用户record的限制,比如不可变性、无法继承其他类等,这样他们在实际使用中能避免踩坑。比如,如果需要可变的数据对象,或者需要扩展其他类,record可能不是最佳选择。 还要考虑到用户可能的进阶问题,比如record是否支持自定义方法,如何在record中添加验证逻辑等。这些可以通过示例说明,展示record的灵活性,即虽然它是不可变的,但可以在构造函数中添加验证,或者定义额外的方法。 最后,总结适用场景,帮助用户判断何时使用record,何时仍需要使用传统类。这样用户能根据实际需求做出合理选择,避免滥用或误用。 检查是否有遗漏的重要点,比如record的底层实现原理,或者与其他JVM语言的类似特性(如Kotlin的数据类)的对比。不过根据用户的问题,可能不需要深入到底层细节,但简要提及可以增加回答的全面性。 确保语言口语化,避免过于技术化的表达,让不同层次的读者都能理解。同时,按照用户之前提供的结构化并发的回答格式,保持类似的风格,比如分章节、使用代码块和表格对比,这样用户阅读起来更连贯。 最后,通读整个思考过程,确保逻辑清晰,覆盖了用户可能关心的所有方面,没有遗漏关键信息,并且示例准确无误。这样整理出的回答才能既全面又易懂,帮助用户真正掌握record类型的使用和优势。</think>以下是关于 Java **Record 类型** 的详细解析(涵盖设计目标、核心特性、使用场景及与传统类的对比): --- ### **1. 什么是 Record 类型?** **目标**:通过 **不可变数据载体类** 的声明式语法,简化 Java 中纯数据传输对象(DTO)的编写,消除传统 POJO 类中冗余的模板代码(如 `equals()`、`hashCode()`、`toString()` 等)。 **核心思想**: - **不可变性**:所有字段默认 `final`,对象一旦创建后不可修改。 - **透明数据建模**:自动生成字段访问方法、`equals()`、`hashCode()` 和 `toString()`。 示例: ```java // 传统 POJO(需手动编写大量代码) public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } // 手动生成 getter、equals、hashCode、toString... } // 使用 Record 替代(一行代码等价) public record Person(String name, int age) {} ``` --- ### **2. 设计动机** #### **传统 POJO 的问题** - **冗余代码**:每个 DTO 需重复编写构造函数、getter、`equals()` 等方法。 - **可维护性差**:字段增减时需同步修改多个方法,易出错。 - **语义不明确**:无法直接从类声明看出其核心目的是承载数据。 #### **Record 的优势** - **代码简洁性**:声明即定义,自动生成标准方法。 - **不可变性**:天然线程安全,避免副作用。 - **意图清晰**:明确表示该类是数据载体。 --- ### **3. 核心特性** #### **(1) 自动生成方法** - **规范方法**:自动生成 `equals()`、`hashCode()`、`toString()`。 - **构造方法**:生成包含所有字段的规范构造函数(Canonical Constructor)。 - **访问方法**:字段访问方法名为 `字段名()`(如 `name()` 而非 `getName()`)。 #### **(2) 不可变性** - 所有字段隐式为 `final`,不可通过反射修改(JDK 15+ 限制)。 - 若需“修改”对象,需通过构造新实例实现: ```java Person newPerson = new Person(oldPerson.name(), newAge); ``` #### **(3) 自定义扩展** - **自定义构造函数**:可添加校验逻辑或非规范构造方法。 ```java public record Person(String name, int age) { public Person { // 紧凑构造函数(无需参数列表) if (age < 0) throw new IllegalArgumentException("Age cannot be negative"); } } ``` - **添加方法**:可定义额外方法。 ```java public record Person(String name, int age) { public String description() { return name + " (" + age + ")"; } } ``` #### **(4) 限制** - **不可继承**:不能显式继承其他类(隐含 `final`)。 - **不可被继承**:不能有子类(隐含 `final`)。 - **不可添加实例字段**:只能在头部声明字段。 --- ### **4. 适用场景** 1. **DTO/VO 对象**:如 API 请求/响应体、数据库实体映射。 2. **不可变配置**:如线程池参数、系统配置项。 3. **模式匹配**(Java 17+):结合 `instanceof` 和 `switch` 模式匹配。 ```java if (obj instanceof Person(String name, int age)) { System.out.println(name + " is " + age + " years old"); } ``` --- ### **5. 与传统 POJO 的对比** | **维度** | **Record 类型** | **传统 POJO 类** | |-------------------|-----------------------------------------|-----------------------------------| | **代码量** | 声明即完整定义(1 行 vs 数十行) | 需手动编写构造函数、getter 等方法 | | **可变性** | 不可变 | 可自由设计为可变或不可变 | | **继承能力** | 不能继承其他类 | 可继承其他类 | | **字段扩展性** | 只能在头部声明字段 | 可任意添加字段和方法 | | **语义明确性** | 明确表示为数据载体 | 需通过文档或命名约定暗示 | --- ### **6. 代码示例** #### **(1) 基础使用** ```java public record Point(int x, int y) {} // 使用 Point p = new Point(10, 20); System.out.println(p.x()); // 输出 10 System.out.println(p); // 输出 Point[x=10, y=20] ``` #### **(2) 自定义校验** ```java public record Email(String value) { public Email { if (!value.matches("^[a-zA-Z0-9_+&*-]+@[a-zA-Z0-9-]+\\.[a-zA-Z]{2,7}$")) { throw new IllegalArgumentException("Invalid email"); } } } ``` --- ### **7. 注意事项** 1. **不可变性的代价**:频繁修改需创建新对象,可能影响性能(需权衡场景)。 2. **避免滥用**:仅适用于纯数据载体,不适用于需要复杂行为或状态的类。 3. **序列化支持**:默认支持序列化,但需确保所有字段类型可序列化。 --- ### **8. 底层实现** - **编译时生成**:编译器自动生成规范方法,字节码与手动编写的 POJO 类似。 - **隐式父类**:所有 Record 隐式继承 `java.lang.Record`(类似枚举继承 `java.lang.Enum`)。 --- ### **9. 与其他语言的对比** | **语言** | **类似特性** | **区别** | |------------|--------------------|----------------------------------------| | **Kotlin** | `data class` | Kotlin 支持可变属性和继承 | | **Scala** | `case class` | Scala 支持模式匹配和更灵活的方法扩展 | | **C#** | `record`(9.0+) | C# 支持可变性和继承 | --- Record 类型通过减少模板代码和强制不可变性,显著提升了数据建模的效率和安全性。适用于需要简洁、安全、不可变数据模型的场景,但在需要灵活扩展或可变状态时仍需使用传统类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值