首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《Java 程序设计》第 6 章 - 字符串

《Java 程序设计》第 6 章 - 字符串

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:46:53
发布2026-01-21 12:46:53
1250
举报

引言

        字符串是 Java 编程中最常用的数据类型之一,无论是用户输入处理、数据展示还是日志记录,都离不开字符串操作。本章将系统讲解 Java 中字符串的核心类(StringStringBuilderStringBuffer)的使用方法、特性及实战技巧,帮助你掌握字符串处理的精髓。

本章知识思维导图

6.1 String 类

  String类是 Java 中处理字符串的核心类,位于java.lang包下,无需手动导入即可使用。字符串本质上是字符序列,在 Java 中被定义为char类型的数组(JDK9 + 后改为byte数组优化存储)。

6.1.1 创建 String 类对象

Java 中创建String对象有两种常用方式,两者在内存存储上有本质区别:

方式 1:直接赋值(推荐)
代码语言:javascript
复制
String str1 = "Hello Java"; // 字符串常量,存储在常量池
方式 2:通过new关键字创建
代码语言:javascript
复制
String str2 = new String("Hello Java"); // 对象存储在堆内存,字符串内容在常量池

        两者区别:直接赋值会优先检查常量池,若存在相同字符串则直接引用,避免重复创建;new关键字会强制在堆中创建新对象,即使常量池已有相同内容。

代码示例

代码语言:javascript
复制
public class StringCreateDemo {
    public static void main(String[] args) {
        // 方式1:直接赋值(常量池)
        String s1 = "Java";
        String s2 = "Java";
        
        // 方式2:new关键字(堆内存)
        String s3 = new String("Java");
        String s4 = new String("Java");
        
        // 比较引用地址(==)
        System.out.println("s1 == s2: " + (s1 == s2)); // true(同常量池引用)
        System.out.println("s3 == s4: " + (s3 == s4)); // false(不同堆对象)
        System.out.println("s1 == s3: " + (s1 == s3)); // false(常量池vs堆)
        
        // 比较内容(equals())
        System.out.println("s1.equals(s3): " + s1.equals(s3)); // true(内容相同)
    }
}
6.1.2 字符串基本操作

String类提供了丰富的方法用于字符串基本操作,常用的有:

方法名

功能描述

length()

获取字符串长度

charAt(int index)

获取指定索引的字符

concat(String str)

拼接字符串(等价于+)

isEmpty()

判断字符串是否为空

代码示例

代码语言:javascript
复制
public class StringBasicOps {
    public static void main(String[] args) {
        String str = "Hello World";
        
        // 获取长度
        System.out.println("字符串长度:" + str.length()); // 输出:11
        
        // 获取指定索引字符(索引从0开始)
        char c = str.charAt(6);
        System.out.println("索引6的字符:" + c); // 输出:W
        
        // 字符串拼接
        String newStr = str.concat("!").concat(" Java is great");
        System.out.println("拼接后:" + newStr); // 输出:Hello World! Java is great
        
        // 判断是否为空
        String emptyStr = "";
        System.out.println("emptyStr是否为空:" + emptyStr.isEmpty()); // 输出:true
    }
}
6.1.3 字符串查找

常用查找方法用于定位子字符串或字符的位置:

方法名

功能描述

indexOf(String str)

返回子串首次出现的索引,无则返回 - 1

lastIndexOf(String str)

返回子串最后出现的索引,无则返回 - 1

startsWith(String prefix)

判断是否以指定前缀开头

endsWith(String suffix)

判断是否以指定后缀结尾

代码示例

代码语言:javascript
复制
public class StringSearch {
    public static void main(String[] args) {
        String str = "Java Programming: Java is fun!";
        
        // 查找子串首次出现位置
        int firstPos = str.indexOf("Java");
        System.out.println("Java首次出现位置:" + firstPos); // 输出:0
        
        // 从索引5开始查找
        int posFrom5 = str.indexOf("Java", 5);
        System.out.println("从索引5开始Java出现位置:" + posFrom5); // 输出:19
        
        // 查找最后出现位置
        int lastPos = str.lastIndexOf("Java");
        System.out.println("Java最后出现位置:" + lastPos); // 输出:19
        
        // 判断前缀和后缀
        boolean startWithJava = str.startsWith("Java");
        boolean endWithFun = str.endsWith("fun!");
        System.out.println("以Java开头:" + startWithJava); // 输出:true
        System.out.println("以fun!结尾:" + endWithFun); // 输出:true
    }
}
6.1.4 字符串转换为数组

通过toCharArray()方法可将字符串转换为字符数组,便于逐个操作字符:

代码示例

代码语言:javascript
复制
public class StringToArray {
    public static void main(String[] args) {
        String str = "Hello";
        
        // 字符串转字符数组
        char[] chars = str.toCharArray();
        
        // 遍历字符数组
        System.out.println("字符数组内容:");
        for (int i = 0; i < chars.length; i++) {
            System.out.println("索引" + i + ":" + chars[i]);
        }
        
        // 字符数组转字符串
        String newStr = new String(chars);
        System.out.println("字符数组转回字符串:" + newStr); // 输出:Hello
    }
}
6.1.5 字符串比较

字符串比较是高频操作,需区分引用比较和内容比较:

比较方式

说明

==

比较对象引用地址(是否为同一对象)

equals()

比较字符串内容是否相同(区分大小写)

equalsIgnoreCase()

比较内容是否相同(不区分大小写)

compareTo()

按字典顺序比较,返回差值(正数 / 负数 / 0)

代码示例

代码语言:javascript
复制
public class StringCompare {
    public static void main(String[] args) {
        String s1 = "apple";
        String s2 = "apple";
        String s3 = new String("apple");
        String s4 = "APPLE";
        
        // 引用比较(==)
        System.out.println("s1 == s2: " + (s1 == s2)); // true(同常量池对象)
        System.out.println("s1 == s3: " + (s1 == s3)); // false(不同对象)
        
        // 内容比较(equals())
        System.out.println("s1.equals(s3): " + s1.equals(s3)); // true(内容相同)
        System.out.println("s1.equals(s4): " + s1.equals(s4)); // false(大小写不同)
        
        // 忽略大小写比较
        System.out.println("s1.equalsIgnoreCase(s4): " + s1.equalsIgnoreCase(s4)); // true
        
        // 字典顺序比较(compareTo)
        System.out.println("s1.compareTo(s4): " + s1.compareTo(s4)); // 32('a'比'A'大32)
        System.out.println("s1.compareTo(s2): " + s1.compareTo(s2)); // 0(相等)
    }
}
6.1.6 字符串的拆分与组合
  • 拆分:split(String regex) 按正则表达式拆分字符串为数组
  • 组合:String.join(CharSequence delimiter, CharSequence... elements) 按分隔符拼接数组

代码示例

代码语言:javascript
复制
public class StringSplitJoin {
    public static void main(String[] args) {
        // 字符串拆分
        String str = "啊阿狸不会拉杆,20,男,Java开发";
        String[] parts = str.split(","); // 按逗号拆分

        System.out.println("拆分结果:");
        for (String part : parts) {
            System.out.println(part);
        }

        // 字符串组合
        String[] fruits = {"苹果", "香蕉", "橙子"};
        String fruitStr = String.join(" | ", fruits); // 用" | "拼接
        System.out.println("组合结果:" + fruitStr); // 输出:苹果 | 香蕉 | 橙子

        // 复杂拆分(按数字拆分)
        String text = "Hello123World456Java";
        String[] textParts = text.split("\\d+"); // 按1个以上数字拆分(注意转义)
        System.out.println("按数字拆分结果:");
        for (String p : textParts) {
            System.out.println(p);
        }
    }
}
6.1.7 String 对象的不变性

    String对象一旦创建,其内容不可修改!所有看似修改的操作(如拼接、替换)都会创建新的String对象。

          原理String类内部的字符数组被final修饰,无法修改引用指向的数组;且没有提供修改数组元素的方法。

代码演示不变性

代码语言:javascript
复制
public class StringImmutable {
    public static void main(String[] args) {
        String str = "Hello";
        System.out.println("原字符串地址:" + System.identityHashCode(str)); // 原地址

        // 看似修改,实际创建新对象
        str = str + " World";
        System.out.println("修改后字符串地址:" + System.identityHashCode(str)); // 新地址

        // 验证:通过反射强制修改(仅做教学演示,生产环境严禁使用)
        try {
            // 获取String类的value字段(JDK 9+为byte[],JDK 8及以下为char[])
            java.lang.reflect.Field field = String.class.getDeclaredField("value");
            field.setAccessible(true); // 暴力访问私有字段

            // 根据JDK版本处理不同的内部存储结构
            if (field.getType() == byte[].class) {
                // JDK 9+使用byte[]存储(UTF-8编码)
                byte[] value = (byte[]) field.get(str);
                // 原字符串是"Hello World",索引5的位置是空格(ASCII码32)
                value[5] = (byte) '!'; // 将空格替换为感叹号
            } else {
                // JDK 8及以下使用char[]存储
                char[] value = (char[]) field.get(str);
                value[5] = '!';
            }

            System.out.println("反射修改后字符串:" + str); // 输出:Hello!World
        } catch (NoSuchFieldException e) {
            System.err.println("未找到value字段:" + e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println("访问被拒绝,请检查VM参数是否正确配置:");
            System.err.println("需要添加JVM参数:--add-opens java.base/java.lang=ALL-UNNAMED");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:反射修改字符串是破坏封装的危险操作,实际开发中严禁使用!

6.1.8 命令行参数

main方法的String[] args参数用于接收命令行传递的参数,可实现程序动态输入。

代码示例

代码语言:javascript
复制
public class CommandLineArgs {
    public static void main(String[] args) {
        // 输出参数数量
        System.out.println("参数个数:" + args.length);
        
        // 遍历参数
        for (int i = 0; i < args.length; i++) {
            System.out.println("参数" + i + ":" + args[i]);
        }
        
        // 示例:计算参数中数字的和(若参数为数字)
        if (args.length == 0) {
            System.out.println("请传递数字参数,例如:java CommandLineArgs 10 20 30");
            return;
        }
        
        int sum = 0;
        for (String arg : args) {
            try {
                sum += Integer.parseInt(arg);
            } catch (NumberFormatException e) {
                System.out.println("参数'" + arg + "'不是有效数字,已忽略");
            }
        }
        System.out.println("数字参数的和:" + sum);
    }
}

运行方法

  1. 编译:javac CommandLineArgs.java
  2. 运行并传参:java CommandLineArgs 10 20 30 abc 40
  3. 输出:参数和为 100(忽略无效参数 abc)

6.2 格式化输出

Java 提供printf方法和Formatter类实现格式化输出,支持多种数据类型的格式化。

常用格式占位符

占位符

描述

示例

%d

整数

%5d(占 5 位,右对齐)

%f

浮点数

%.2f(保留 2 位小数)

%s

字符串

%10s(占 10 位,右对齐)

%c

字符

%c

%b

布尔值

%b

%tF

日期(年 - 月 - 日)

%tF

代码示例

代码语言:javascript
复制
import java.util.Date;

public class FormatOutput {
    public static void main(String[] args) {
        String name = "啊阿狸不会拉杆";
        int age = 20;
        double salary = 9876.5432;
        boolean isMarried = false;
        Date today = new Date();

        // 使用printf格式化输出
        System.out.printf("姓名:%s,年龄:%d岁%n", name, age);
        System.out.printf("薪资:%.2f元/月(保留2位小数)%n", salary);
        System.out.printf("是否已婚:%b%n", isMarried);
        System.out.printf("当前日期:%tF%n", today); // %tF 格式为年-月-日

        // 控制宽度和对齐
        System.out.println("\n===== 对齐演示 =====");
        System.out.printf("|%10s|%5d|%10.2f|%n", "雷军", 25, 9876.54); // 右对齐
        System.out.printf("|%-10s|%-5d|%-10.2f|%n", "萧炎", 30, 12345.67); // 左对齐(加-)
    }
}

输出结果

6.3 StringBuilder 类和 StringBuffer 类

         由于String的不变性,频繁修改字符串会产生大量临时对象,效率低下。StringBuilderStringBuffer是可变字符串类,适合频繁修改场景。

类图(PlantUML)
代码语言:javascript
复制
@startuml
class Object {
  + equals()
  + hashCode()
  + toString()
}

class CharSequence {
  + length(): int
  + charAt(int): char
  + subSequence(int, int): CharSequence
  + toString(): String
}

class String {
  - final char[] value
  + length(): int
  + equals(): boolean
  + concat(): String
  + indexOf(): int
}

class AbstractStringBuilder {
  - char[] value
  - int count
  + length(): int
  + append(): AbstractStringBuilder
  + insert(): AbstractStringBuilder
  + delete(): AbstractStringBuilder
}

class StringBuilder {
  + StringBuilder()
  + append(): StringBuilder
  + insert(): StringBuilder
  + delete(): StringBuilder
  + reverse(): StringBuilder
}

class StringBuffer {
  + StringBuffer()
  + synchronized append(): StringBuffer
  + synchronized insert(): StringBuffer
  + synchronized delete(): StringBuffer
  + synchronized reverse(): StringBuffer
}

Object <|-- String
Object <|-- AbstractStringBuilder
CharSequence <|-- String
CharSequence <|-- StringBuilder
CharSequence <|-- StringBuffer
AbstractStringBuilder <|-- StringBuilder
AbstractStringBuilder <|-- StringBuffer
@enduml
核心区别

特性

String

StringBuilder

StringBuffer

可变性

不可变

可变

可变

线程安全

非线程安全

线程安全(方法加synchronized)

效率

低(频繁修改时)

中(同步开销)

适用场景

少量修改 / 常量字符串

单线程频繁修改

多线程频繁修改

6.3.1 创建 StringBuilder 对象
代码语言:javascript
复制
public class StringBuilderCreate {
    public static void main(String[] args) {
        // 方式1:创建空对象
        StringBuilder sb1 = new StringBuilder();
        
        // 方式2:指定初始容量(推荐,避免扩容开销)
        StringBuilder sb2 = new StringBuilder(100); // 初始容量100
        
        // 方式3:用字符串初始化
        StringBuilder sb3 = new StringBuilder("Hello");
        
        System.out.println("sb3初始内容:" + sb3); // 输出:Hello
        System.out.println("当前长度:" + sb3.length()); // 输出:5
        System.out.println("当前容量:" + sb3.capacity()); // 输出:21(默认容量=初始字符串长度+16)
    }
}
6.3.2 StringBuilder 的访问和修改

常用方法:

  • append():追加内容(支持所有数据类型)
  • insert(int offset, ...):在指定位置插入内容
  • delete(int start, int end):删除指定范围内容
  • reverse():反转字符串
  • toString():转换为String对象

代码示例

代码语言:javascript
复制
public class StringBuilderOps {
    public static void main(String[] args) {
        // 初始化
        StringBuilder sb = new StringBuilder("Java");
        System.out.println("初始内容:" + sb); // 输出:Java
        
        // 追加内容
        sb.append(" Programming");
        sb.append(" is fun!");
        System.out.println("追加后:" + sb); // 输出:Java Programming is fun!
        
        // 插入内容
        sb.insert(5, "SE 8 "); // 在索引5处插入"SE 8 "
        System.out.println("插入后:" + sb); // 输出:Java SE 8 Programming is fun!
        
        // 修改指定位置字符
        sb.setCharAt(5, 's'); // 将索引5的'S'改为's'
        System.out.println("修改字符后:" + sb); // 输出:Java se 8 Programming is fun!
        
        // 删除内容(删除" is fun!")
        int start = sb.indexOf(" is fun!");
        if (start != -1) {
            sb.delete(start, start + " is fun!".length());
        }
        System.out.println("删除后:" + sb); // 输出:Java se 8 Programming
        
        // 反转
        sb.reverse();
        System.out.println("反转后:" + sb); // 输出:gnimmargorP 8 es avaJ
        
        // 转换为String
        String result = sb.toString();
        System.out.println("最终String:" + result);
    }
}
6.3.3 运算符 “+” 的重载

Java 中String+运算符是语法糖,编译后会被转换为StringBuilderappend操作(但有例外)。

示例解析

代码语言:javascript
复制
public class StringPlusOverload {
    public static void main(String[] args) {
        // 编译前
        String a = "Hello";
        String b = "World";
        String c = a + b + "!";
        
        // 编译后等价于
        String d = new StringBuilder().append(a).append(b).append("!").toString();
        System.out.println(c); // 输出:HelloWorld!
        System.out.println(d); // 输出:HelloWorld!
        
        // 注意:循环中使用+会创建多个StringBuilder,效率低
        long start = System.currentTimeMillis();
        String loopStr = "";
        for (int i = 0; i < 10000; i++) {
            loopStr += i; // 每次循环创建新StringBuilder
        }
        System.out.println("循环用+耗时:" + (System.currentTimeMillis() - start) + "ms");
        
        // 推荐:循环中显式用StringBuilder
        start = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            sb.append(i);
        }
        String loopStr2 = sb.toString();
        System.out.println("循环用StringBuilder耗时:" + (System.currentTimeMillis() - start) + "ms");
    }
}

输出结果

结论:循环中拼接字符串必须用StringBuilder,避免性能问题!

6.4 小结

  1. String 类:不可变字符串,适合内容固定的场景,核心方法包括创建、查找、比较、拆分等。
  2. String 的不变性:修改字符串会创建新对象,频繁修改需谨慎。
  3. 格式化输出printf方法支持多种格式占位符,便于格式化展示数据。
  4. 可变字符串
    • StringBuilder:非线程安全,效率高,适合单线程。
    • StringBuffer:线程安全,效率较低,适合多线程。
  5. 性能优化:频繁修改字符串优先用StringBuilder,避免用String+运算符。

编程练习

练习 1:字符串反转工具

实现一个工具类,提供字符串反转功能,支持多种输入方式。

代码语言:javascript
复制
import java.util.Scanner;

/**
 * 字符串反转工具类
 */
public class StringReverseTool {
    /**
     * 反转字符串(StringBuilder实现)
     * @param str 待反转字符串
     * @return 反转后的字符串
     */
    public static String reverseWithBuilder(String str) {
        if (str == null) {
            return null;
        }
        return new StringBuilder(str).reverse().toString();
    }
    
    /**
     * 反转字符串(手动实现)
     * @param str 待反转字符串
     * @return 反转后的字符串
     */
    public static String reverseManual(String str) {
        if (str == null) {
            return null;
        }
        char[] chars = str.toCharArray();
        int left = 0;
        int right = chars.length - 1;
        // 交换左右指针的字符
        while (left < right) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
        return new String(chars);
    }
    
    public static void main(String[] args) {
        // 方式1:从命令行参数获取
        if (args.length > 0) {
            String input = String.join(" ", args);
            System.out.println("命令行输入反转:" + reverseWithBuilder(input));
        }
        
        // 方式2:从控制台输入
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要反转的字符串:");
        String userInput = scanner.nextLine();
        scanner.close();
        
        System.out.println("Builder反转结果:" + reverseWithBuilder(userInput));
        System.out.println("手动反转结果:" + reverseManual(userInput));
    }
}
练习 2:统计字符出现次数

统计字符串中指定字符(或所有字符)的出现次数。

代码语言:javascript
复制
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * 字符出现次数统计工具
 */
public class CharCountTool {
    /**
     * 统计字符串中所有字符的出现次数
     * @param str 目标字符串
     * @return 字符-次数映射表
     */
    public static Map<Character, Integer> countAllChars(String str) {
        Map<Character, Integer> countMap = new HashMap<>();
        if (str == null || str.isEmpty()) {
            return countMap;
        }
        
        for (char c : str.toCharArray()) {
            // 若已存在则次数+1,否则初始化为1
            countMap.put(c, countMap.getOrDefault(c, 0) + 1);
        }
        return countMap;
    }
    
    /**
     * 统计指定字符的出现次数
     * @param str 目标字符串
     * @param target 目标字符
     * @return 出现次数
     */
    public static int countTargetChar(String str, char target) {
        if (str == null || str.isEmpty()) {
            return 0;
        }
        
        int count = 0;
        for (char c : str.toCharArray()) {
            if (c == target) {
                count++;
            }
        }
        return count;
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入字符串:");
        String input = scanner.nextLine();
        
        // 统计所有字符
        Map<Character, Integer> allCounts = countAllChars(input);
        System.out.println("\n所有字符出现次数:");
        for (Map.Entry<Character, Integer> entry : allCounts.entrySet()) {
            System.out.println("字符 '" + entry.getKey() + "':" + entry.getValue() + "次");
        }
        
        // 统计指定字符
        System.out.print("\n请输入要查询的字符:");
        char target = scanner.nextLine().charAt(0); // 简化处理,假设输入单个字符
        int targetCount = countTargetChar(input, target);
        System.out.println("字符 '" + target + "' 出现次数:" + targetCount + "次");
        
        scanner.close();
    }
}

结语

        本章详细讲解了 Java 字符串的核心操作和最佳实践,掌握StringStringBuilderStringBuffer的特性和适用场景,能显著提升字符串处理的效率和代码质量。建议多动手练习文中的示例,加深对字符串操作的理解!

如果有任何问题或建议,欢迎在评论区留言交流~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 本章知识思维导图
  • 6.1 String 类
    • 6.1.1 创建 String 类对象
      • 方式 1:直接赋值(推荐)
      • 方式 2:通过new关键字创建
    • 6.1.2 字符串基本操作
    • 6.1.3 字符串查找
    • 6.1.4 字符串转换为数组
    • 6.1.5 字符串比较
    • 6.1.6 字符串的拆分与组合
    • 6.1.7 String 对象的不变性
    • 6.1.8 命令行参数
  • 6.2 格式化输出
    • 常用格式占位符
  • 6.3 StringBuilder 类和 StringBuffer 类
    • 类图(PlantUML)
    • 核心区别
    • 6.3.1 创建 StringBuilder 对象
    • 6.3.2 StringBuilder 的访问和修改
    • 6.3.3 运算符 “+” 的重载
  • 6.4 小结
  • 编程练习
    • 练习 1:字符串反转工具
    • 练习 2:统计字符出现次数
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档