授课语音

Java字符串

1. 介绍

Java的字符串String对象在实际开发中非常广泛地使用。字符串是不可变的、线程安全的,这意味着一旦创建,字符串的内容就不能被修改。如果需要修改字符串(如拼接、替换等),Java会创建一个新的字符串对象,因此不建议频繁修改字符串,以避免性能损失。

比较

字符串的比较包括内容比较和引用比较。在实际开发中,需要根据业务场景做出正确的选择:

  • 内容比较:使用equals()方法来比较字符串内容是否相同。
  • 引用比较:使用==运算符来比较两个字符串对象的引用是否相同。

字符串常量池

Java有一个内存优化机制,称为字符串常量池,用于管理字符串字面量。字符串常量池是一个特殊的内存区域,存放字符串的字面量。当创建新的字符串时,JVM会检查常量池,如果池中已经存在该字符串,就直接返回其引用,从而提升性能。

StringBuilder和StringBuffer

这两者都是可变的字符串类:

  • StringBuilder是非线程安全的,适合在单线程环境中频繁修改字符串。
  • StringBuffer是线程安全的,适合在多线程环境中使用。

2. 代码案例

基本操作案例

package com.zhilitech.strings;

import java.util.Arrays;

public class ComprehensiveStringExample {

    public static void main(String[] args) {
        // 1. 创建字符串
        String s1 = "Hello"; // 字符串的字面量,放入字符串常量池
        String s2 = new String("World"); // 使用new关键字创建的字符串,不放入常量池

        // 2. 字符串长度
        int lengthS1 = s1.length(); // 结果是5
        System.out.println("Length of s1: " + lengthS1); // 输出: Length of s1: 5

        // 3. 获取字符
        char firstChar = s1.charAt(0); // 结果是 'H'
        System.out.println("First character of s1: " + firstChar); // 输出: First character of s1: H

        // 4. 子字符串
        String subString = s1.substring(1, 4); // 结果是 "ell"
        System.out.println("Substring of s1: " + subString); // 输出: Substring of s1: ell

        // 5. 拼接字符串
        String concatenated = s1.concat(" ").concat(s2); // 结果是 "Hello World"
        System.out.println("Concatenated string: " + concatenated); // 输出: Concatenated string: Hello World

        // 6. 字符串比较
        // 这里记住不能使用`==`,因为它比较的是对象的引用地址
        boolean isEqual = s1.equals("Hello"); // true
        boolean isEqualIgnoreCase = s1.equalsIgnoreCase("HELLO"); // true
        System.out.println("Is s1 equal to 'Hello': " + isEqual); // 输出: Is s1 equal to 'Hello': true
        System.out.println("Is s1 equal to 'HELLO' (ignore case): " + isEqualIgnoreCase); // 输出: Is s1 equal to 'HELLO' (ignore case): true

        // 7. 查找子串的位置
        int indexOfW = concatenated.indexOf("W"); // 结果是6
        System.out.println("Index of 'W': " + indexOfW); // 输出: Index of 'W': 6

        // 8. 替换字符串
        String replacedString = concatenated.replace("World", "Java"); // 结果是 "Hello Java"
        System.out.println("Replaced string: " + replacedString); // 输出: Replaced string: Hello Java

        // 9. 转换大小写
        String upperCase = s1.toUpperCase(); // 结果是 "HELLO"
        String lowerCase = s1.toLowerCase(); // 结果是 "hello"
        System.out.println("Uppercase s1: " + upperCase); // 输出: Uppercase s1: HELLO
        System.out.println("Lowercase s1: " + lowerCase); // 输出: Lowercase s1: hello

        // 10. 去除前后空格
        String withSpaces = "   Hello World   ";
        String trimmed = withSpaces.trim(); // 结果是 "Hello World"
        System.out.println("Trimmed string: '" + trimmed + "'"); // 输出: Trimmed string: 'Hello World'

        // 11. 分割字符串
        String[] parts = concatenated.split(" "); // 根据空格分割
        System.out.println("Splitted parts:");
        for (String part : parts) {
            System.out.println(part);
        }
        // 输出:
        // Splitted parts:
        // Hello
        // World

        // 12. 正则表达式替换
        String replacedWithRegex = concatenated.replaceAll("\\s", "_"); // 将所有空格替换为下划线
        System.out.println("Replaced with regex: " + replacedWithRegex); // 输出: Replaced with regex: Hello_Java

        // 13. 字符串池示例
        String s3 = "Hello";
        String s4 = new String("Hello").intern(); // intern()方法将s4的内容加入字符串池
        boolean isSameReference = s1 == s3; // true,s1和s3指向字符串池中的同一个对象
        boolean isDifferentReference = s1 == s4; // true,s1和s4也指向字符串池中的同一个对象
        System.out.println("s1 and s3 reference equality: " + isSameReference); // 输出: s1 and s3 reference equality: true
        System.out.println("s1 and s4 reference equality: " + isDifferentReference); // 输出: s1 and s4 reference equality: true

        // 14. 字符串与数组的转换
        String[] array = {"Java", "Python", "C++"};
        String joined = String.join(", ", array); // 使用逗号和空格连接数组元素
        System.out.println("Joined string from array: " + joined); // 输出: Joined string from array: Java, Python, C++

        String arrayToString = Arrays.toString(array); // 将数组转换为字符串
        System.out.println("Array to string: " + arrayToString); // 输出: Array to string: [Java, Python, C++]

        // 15. 格式化字符串
        int number = 42;
        String formattedString = String.format("The number is %d and the word is %s.", number, s1);
        System.out.println("Formatted string: " + formattedString); // 输出: Formatted string: The number is 42 and the word is Hello.

        // 16. StringBuilder 示例
        StringBuilder sb = new StringBuilder();
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        sb.append("!");
        System.out.println("StringBuilder result: " + sb); // 输出: StringBuilder result: Hello World!

        // 17. StringBuffer 示例
        StringBuffer sbf = new StringBuffer();
        sbf.append("Java");
        sbf.append(" ");
        sbf.append("Programming");
        sbf.append("!");
        System.out.println("StringBuffer result: " + sbf); // 输出: StringBuffer result: Java Programming!
    }
}

常见注意事项案例

package com.zhilitech.strings;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class ComprehensiveStringExample2 {

    // 多线程环境的验证
    static void runTest(final Appendable appendable) throws InterruptedException {
        final int THREAD_COUNT = 10; // 线程数量
        final int APPEND_COUNT = 1000; // 每个线程追加的字符数量

        Runnable task = () -> {
            for (int i = 0; i < APPEND_COUNT; i++) {
                try {
                    appendable.append("X"); // 向appendable对象追加字符
                } catch (IOException e) {
                    // 处理Appendable可能抛出的IOException
                    e.printStackTrace();
                }
            }
        };

        Thread[] threads = new Thread[THREAD_COUNT]; // 创建线程数组
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(task); // 创建线程任务
            threads[i].start(); // 启动线程
        }

        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i].join(); // 等待所有线程执行完毕
        }

        System.out.println("Length of result: " + appendable.toString().length()); // 输出结果的长度
    }

    public static void main(String[] args) throws InterruptedException {
        // 1. 字符编码
        String originalString = "Hello, 世界!"; // 原始字符串
        byte[] utf8Bytes = originalString.getBytes(StandardCharsets.UTF_8); // UTF-8编码
        byte[] iso88591Bytes = originalString.getBytes(StandardCharsets.ISO_8859_1); // ISO-

8859-1编码

        System.out.println("Original String: " + originalString);
        System.out.println("UTF-8 Bytes: " + Arrays.toString(utf8Bytes)); // 输出UTF-8字节数组
        System.out.println("ISO-8859-1 Bytes: " + Arrays.toString(iso88591Bytes)); // 输出ISO-8859-1字节数组

        // 2. 字符串常量池
        String s1 = "Hello"; // 字符串常量池中的字符串
        String s2 = "Hello"; // 引用相同的字符串常量
        String s3 = new String("Hello"); // 使用new关键字创建的字符串,不在常量池中
        String s4 = s3.intern(); // 将s3的内容放入常量池

        System.out.println("s1 == s2: " + (s1 == s2)); // true,s1和s2引用相同
        System.out.println("s1 == s3: " + (s1 == s3)); // false,s1和s3引用不同
        System.out.println("s1 == s4: " + (s1 == s4)); // true,s1和s4引用相同

        // 3. 字符串比较陷阱
        String a = new String("test"); // 创建新字符串对象
        String b = "test"; // 字符串常量池中的字符串

        // 注意:虽然内容相同,但对象不同
        System.out.println("a == b: " + (a == b)); // false
        System.out.println("a.equals(b): " + a.equals(b)); // true,内容相同

        // 4. StringBuilder 和 StringBuffer 示例
        /*
         * StringBuffer 是线程安全的,但由于其方法被同步,性能上不如 StringBuilder。
         * StringBuilder 不保证线程安全,适合单线程场景,性能更优。
         * 在多线程环境中,StringBuilder 和 StringBuffer 的行为会有所不同。
         * StringBuilder 是非线程安全的,而 StringBuffer 是线程安全的,表现会有显著差异。
         */
        StringBuilder sb = new StringBuilder();
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        sb.append("!");
        System.out.println("StringBuilder result: " + sb); // 输出: StringBuilder result: Hello World!

        // 使用 StringBuilder 的多线程环境,线程不安全,输出长度不一致
        System.out.println("Using StringBuilder:");
        runTest(new StringBuilder());

        // 使用 StringBuffer 的多线程环境,线程安全,每次输出都是一致的
        System.out.println("\nUsing StringBuffer:");
        runTest(new StringBuffer());

        // 5. 常见陷阱 - 不正确使用 intern()
        String s5 = new String("Interned"); // 创建新字符串对象
        String s6 = s5.intern(); // s6 已经在常量池中了
        System.out.println("s5 == s6: " + (s5 == s6)); // false,s5 和 s6 不相同
        String s7 = "Interned"; // 从常量池中获取字符串
        System.out.println("s7 == s6: " + (s7 == s6)); // true,s7 和 s6 相同
    }
}

以上就是关于Java字符串的介绍和代码示例,希望能帮助你更好地理解字符串的用法及其特性!

去1:1私密咨询

系列课程: