Java String类详解
String是Java中使用最广泛的类之一,用于表示字符串。虽然字符串在Java中作为对象处理,但Java提供了特殊的语法支持,使其用起来像基本数据类型一样方便。理解String类的特性和内部机制对于编写高效的Java程序至关重要。
String = 不可变性 + 字符串常量池 + 丰富的操作方法 + 性能优化
- 🔒 不可变性:保证线程安全,提升安全性和稳定性
- 📦 字符串常量池:优化内存使用,提高字符串处理效率
- 🛠️ 丰富的API:提供全面的字符串操作能力
- ⚡ 性能优化:灵活选择StringBuilder和StringBuffer提升性能
- 🌐 编码支持:完善的国际化和多语言支持
1. String类基础特性
- 不可变性
- 字符串常量池
- 内部结构
1.1 不可变性(Immutability)
String对象是不可变的(immutable),这意味着一旦创建,其内容就不能被修改。任何修改操作都会创建一个新的String对象。
1public class StringImmutabilityDemo {2 public static void main(String[] args) {3 // 基本不可变性演示4String s1 = "Hello";5s1.concat(" World"); // 创建了新的字符串"Hello World",但s1没有改变6System.out.println(s1); // 输出: Hello78// 如果要保存修改后的值,需要重新赋值9s1 = s1.concat(" World");10System.out.println(s1); // 输出: Hello World1112// 验证不可变性13String original = "Java";14String modified = original.toUpperCase(); // 创建新对象15System.out.println("Original: " + original); // Java16System.out.println("Modified: " + modified); // JAVA17System.out.println("Same object? " + (original == modified)); // false18 19 // 进一步验证不可变性20 String test = "Test";21 String testCopy = test; // testCopy和test指向同一个对象22 test = test + "Modified"; // test指向新对象,testCopy仍然指向原对象23 System.out.println("test: " + test); // TestModified24 System.out.println("testCopy: " + testCopy); // Test25 System.out.println("Same object? " + (test == testCopy)); // false26 }27}不可变性的好处:
- 线程安全
- 安全性
- 哈希缓存
- 字符串常量池
多个线程可以同时访问同一个字符串而不会导致数据一致性问题,不需要额外的同步机制。
用于存储密码、网络连接等敏感信息时更安全,因为内容无法被修改。
String对象的哈希值可以被缓存,无需重新计算,适合作为HashMap或HashSet的键。
不可变性使JVM能够优化内存使用,通过常量池复用相同内容的字符串对象。
不可变性虽然带来很多好处,但在频繁修改字符串的场景下可能影响性能。此时应考虑使用StringBuilder或StringBuffer。
1.2 字符串常量池(String Pool)
为了提高效率和减少内存消耗,Java为字符串维护了一个特殊的内存区域,称为"字符串常量池"。
1public class StringPoolDemo {2 public static void main(String[] args) {3 // 基本常量池演示4String s1 = "Hello"; // 创建一个字符串并放入常量池5String s2 = "Hello"; // 直接引用常量池中的字符串6String s3 = new String("Hello"); // 强制在堆中创建新对象78System.out.println(s1 == s2); // true,指向同一个对象9System.out.println(s1 == s3); // false,指向不同对象10System.out.println(s1.equals(s3)); // true,内容相同1112// 使用intern()方法将字符串放入常量池13String s4 = new String("World").intern();14String s5 = "World";15System.out.println(s4 == s5); // true16 17 // 动态创建的字符串18 String dynamic1 = "Dynamic";19 String dynamic2 = "Dynamic";20 String dynamic3 = new String("Dynamic");21 String dynamic4 = dynamic3.intern();22 23 System.out.println("dynamic1 == dynamic2: " + (dynamic1 == dynamic2)); // true24 System.out.println("dynamic1 == dynamic3: " + (dynamic1 == dynamic3)); // false25 System.out.println("dynamic1 == dynamic4: " + (dynamic1 == dynamic4)); // true26 }27}- 字符串创建方式
- 内存布局
- 字面量:使用双引号直接创建,如
String s = "Hello" - 构造函数:使用
new关键字创建,如String s = new String("Hello") - 字符串方法:通过其他字符串的方法创建,如
"Hello".concat(" World") - intern()方法:将字符串放入常量池并返回引用
1public class StringMemoryExample {2 public static void main(String[] args) {3 // 方式1:字面量 - 存储在常量池4 String literal1 = "Hello";5 String literal2 = "Hello";6 7 // 方式2:构造函数 - 存储在堆内存8 String constructor1 = new String("Hello");9 String constructor2 = new String("Hello");10 11 // 方式3:字符串连接 - 可能创建新对象12 String concat1 = "Hello" + " World";13 String concat2 = "Hello World";14 15 // 方式4:运行时连接16 String runtime1 = "Hello" + new String(" World");17 String runtime2 = "Hello World";18 19 // 比较引用20 System.out.println("literal1 == literal2: " + (literal1 == literal2)); // true21 System.out.println("constructor1 == constructor2: " + (constructor1 == constructor2)); // false22 System.out.println("concat1 == concat2: " + (concat1 == concat2)); // true(编译时优化)23 System.out.println("runtime1 == runtime2: " + (runtime1 == runtime2)); // false(运行时创建)24 25 // 比较内容26 System.out.println("literal1.equals(constructor1): " + literal1.equals(constructor1)); // true27 System.out.println("concat1.equals(runtime1): " + concat1.equals(runtime1)); // true28 }29}在Java 7之前,字符串常量池位于永久代(PermGen)。从Java 7开始,字符串常量池被移至堆内存,提高了性能并降低了内存溢出的风险。
1.3 String类的内部结构
从Java 9开始,String类的内部实现发生了变化,使用byte数组替代char数组来存储字符串内容。
1public class StringInternalStructure {2 public static void main(String[] args) {3 // 创建不同编码的字符串4 String asciiString = "Hello World"; // ASCII字符5 String chineseString = "你好世界"; // 中文字符6 String emojiString = "Hello 👋"; // 包含emoji7 8 // 获取字符串长度9 System.out.println("ASCII字符串长度: " + asciiString.length()); // 1110 System.out.println("中文字符串长度: " + chineseString.length()); // 411 System.out.println("Emoji字符串长度: " + emojiString.length()); // 712 13 // 获取字节数组14 byte[] asciiBytes = asciiString.getBytes();15 byte[] chineseBytes = chineseString.getBytes();16 byte[] emojiBytes = emojiString.getBytes();17 18 System.out.println("ASCII字节数: " + asciiBytes.length); // 1119 System.out.println("中文字节数: " + chineseBytes.length); // 12 (UTF-8编码)20 System.out.println("Emoji字节数: " + emojiBytes.length); // 10 (UTF-8编码)21 22 // 获取字符数组23 char[] asciiChars = asciiString.toCharArray();24 char[] chineseChars = chineseString.toCharArray();25 char[] emojiChars = emojiString.toCharArray();26 27 System.out.println("ASCII字符数: " + asciiChars.length); // 1128 System.out.println("中文字符数: " + chineseChars.length); // 429 System.out.println("Emoji字符数: " + emojiChars.length); // 730 }31}- Java 8及之前
- Java 9及以后
1// Java 8 中 String 的部分源码2public final class String3 implements java.io.Serializable, Comparable<String>, CharSequence {4 /** The value is used for character storage. */5 private final char value[];67 /** Cache the hash code for the string */8 private int hash; // Default to 09 10 // ...其他成员和方法...11}1// Java 9+ 中 String 的部分源码2public final class String3 implements java.io.Serializable, Comparable<String>, CharSequence {4 /** The value is used for character storage. */5 private final byte[] value;67 /** The identifier of the encoding used to encode the bytes in {@code value}. */8 private final byte coder;9 10 /** Cache the hash code for the string */11 private int hash; // Default to 012 13 // ...其他成员和方法...14}Java 9中String类使用byte[]而非char[]存储数据的主要原因:
- 节省内存:大多数字符串只包含Latin-1字符,每个字符只需要1个字节而非2个字节
- 使用coder字段标记编码方式:0代表Latin-1编码,1代表UTF-16编码
- 平均可节省约20%的内存空间
2. String类的构造方法
- 常用构造方法
- 编码相关构造方法
2.1 常用构造方法
String类提供了多种构造方法,用于不同的创建场景。
1public class StringConstructorsDemo {2 public static void main(String[] args) {3 // 1. 无参构造方法 - 创建空字符串4 String empty = new String();5 System.out.println("空字符串: '" + empty + "'");6 System.out.println("长度: " + empty.length());7 System.out.println("是否为空: " + empty.isEmpty());8 9 // 2. 字符串字面量构造10 String literal = new String("Hello World");11 System.out.println("字面量构造: " + literal);12 13 // 3. 字符数组构造14 char[] charArray = {'H', 'e', 'l', 'l', 'o'};15 String fromCharArray = new String(charArray);16 System.out.println("字符数组构造: " + fromCharArray);17 18 // 4. 字符数组部分构造19 String fromCharArrayPart = new String(charArray, 1, 3); // 从索引1开始,取3个字符20 System.out.println("字符数组部分构造: " + fromCharArrayPart); // ell21 22 // 5. 字节数组构造23 byte[] byteArray = {72, 101, 108, 108, 111}; // "Hello"的ASCII码24 String fromByteArray = new String(byteArray);25 System.out.println("字节数组构造: " + fromByteArray);26 27 // 6. 字节数组部分构造28 String fromByteArrayPart = new String(byteArray, 1, 3, "UTF-8");29 System.out.println("字节数组部分构造: " + fromByteArrayPart);30 31 // 7. StringBuffer构造32 StringBuffer stringBuffer = new StringBuffer("Hello");33 String fromStringBuffer = new String(stringBuffer);34 System.out.println("StringBuffer构造: " + fromStringBuffer);35 36 // 8. StringBuilder构造37 StringBuilder stringBuilder = new StringBuilder("World");38 String fromStringBuilder = new String(stringBuilder);39 System.out.println("StringBuilder构造: " + fromStringBuilder);40 }41}- 空字符串
- 字符串拷贝
- 数组构造
1// 创建空字符串2String s = new String();这是最简单的构造方法,创建一个空字符串,长度为0。等同于String s = ""。
1// 从现有字符串创建新字符串2String original = "Hello";3String copy = new String(original);这个构造方法创建一个与参数内容相同的新字符串对象。注意,这将创建一个新对象,不会重用常量池中的对象。
1// 从字符数组构造2char[] chars = {'H', 'e', 'l', 'l', 'o'};3String s1 = new String(chars);45// 从部分字符数组构造6String s2 = new String(chars, 1, 3); // "ell"78// 从字节数组构造9byte[] bytes = {72, 101, 108, 108, 111};10String s3 = new String(bytes);这些构造方法允许从字符数组或字节数组创建字符串,可以指定起始索引和长度。
2.2 编码相关构造方法
1public class StringEncodingDemo {2 public static void main(String[] args) throws Exception {3 String original = "Hello 世界";4 5 // 使用默认编码(通常是UTF-8)6 byte[] defaultBytes = original.getBytes();7 String fromDefaultBytes = new String(defaultBytes);8 System.out.println("默认编码: " + fromDefaultBytes);9 10 // 使用UTF-8编码11 byte[] utf8Bytes = original.getBytes("UTF-8");12 String fromUtf8Bytes = new String(utf8Bytes, "UTF-8");13 System.out.println("UTF-8编码: " + fromUtf8Bytes);14 15 // 使用GBK编码16 byte[] gbkBytes = original.getBytes("GBK");17 String fromGbkBytes = new String(gbkBytes, "GBK");18 System.out.println("GBK编码: " + fromGbkBytes);19 20 // 使用ISO-8859-1编码21 byte[] isoBytes = original.getBytes("ISO-8859-1");22 String fromIsoBytes = new String(isoBytes, "ISO-8859-1");23 System.out.println("ISO-8859-1编码: " + fromIsoBytes);24 25 // 编码问题演示26 System.out.println("UTF-8字节数: " + utf8Bytes.length);27 System.out.println("GBK字节数: " + gbkBytes.length);28 System.out.println("ISO-8859-1字节数: " + isoBytes.length);29 }30}- 常用编码对照表
- 编码构造方法
| 编码名称 | 特点 | 适用场景 | 中文支持 |
|---|---|---|---|
| UTF-8 | 变长编码,1-4字节 | 网页、国际化应用 | 支持(3字节) |
| UTF-16 | 大部分字符2字节 | Java内部编码 | 支持(2字节) |
| GBK | 中文优化编码 | 中文应用 | 支持(2字节) |
| ISO-8859-1 | 单字节编码 | 西欧语言 | 不支持 |
| ASCII | 单字节编码 | 纯英文 | 不支持 |
1// 指定字符集名称2byte[] bytes = "Hello".getBytes("UTF-8");3String s1 = new String(bytes, "UTF-8");45// 使用Charset对象6Charset utf8 = Charset.forName("UTF-8");7byte[] bytes2 = "Hello".getBytes(utf8);8String s2 = new String(bytes2, utf8);910// 标准字符集常量11byte[] bytes3 = "Hello".getBytes(StandardCharsets.UTF_8);12String s3 = new String(bytes3, StandardCharsets.UTF_8);不同编码间的转换如果处理不当,可能导致乱码,特别是对于中文、日文等非ASCII字符。始终使用相同的编码进行getBytes()和new String()操作。
- 尽量使用UTF-8作为默认编码
- 在处理外部数据时,总是显式指定编码,不要依赖默认编码
- 使用Java 7引入的StandardCharsets常量而不是字符串形式的编码名称
- 处理非ASCII字符时,注意验证编码转换的正确性
3. String类的常用方法
- 查询方法
- 提取和分割
- 修改方法
3.1 字符串查询方法
String类提供了丰富的查询方法来获取字符串的各种信息。
1public class StringQueryMethodsDemo {2 public static void main(String[] args) {3 String text = "Hello World Java Programming";4 5 // 1. 长度相关6 System.out.println("字符串长度: " + text.length());7 System.out.println("是否为空: " + text.isEmpty());8 System.out.println("是否为空白: " + text.isBlank()); // Java 11+9 10 // 2. 字符访问11 System.out.println("第一个字符: " + text.charAt(0));12 System.out.println("最后一个字符: " + text.charAt(text.length() - 1));13 14 // 3. 子字符串查找15 System.out.println("'World'的位置: " + text.indexOf("World"));16 System.out.println("'Java'的位置: " + text.indexOf("Java"));17 System.out.println("'o'第一次出现位置: " + text.indexOf('o'));18 System.out.println("'o'最后一次出现位置: " + text.lastIndexOf('o'));19 20 // 4. 字符串包含检查21 System.out.println("包含'Hello': " + text.contains("Hello"));22 System.out.println("包含'Python': " + text.contains("Python"));23 24 // 5. 字符串开始和结束检查25 System.out.println("以'Hello'开始: " + text.startsWith("Hello"));26 System.out.println("以'ing'结束: " + text.endsWith("ing"));27 System.out.println("从索引5开始以'World'开始: " + text.startsWith("World", 6));28 29 // 6. 字符串比较30 String text2 = "Hello World Java Programming";31 String text3 = "hello world java programming";32 33 System.out.println("text == text2: " + (text == text2)); // 引用比较34 System.out.println("text.equals(text2): " + text.equals(text2)); // 内容比较35 System.out.println("text.equalsIgnoreCase(text3): " + text.equalsIgnoreCase(text3)); // 忽略大小写比较36 37 // 7. 字符串比较(字典序)38 System.out.println("text.compareTo(text2): " + text.compareTo(text2)); // 039 System.out.println("text.compareTo(text3): " + text.compareTo(text3)); // 负数40 System.out.println("text.compareToIgnoreCase(text3): " + text.compareToIgnoreCase(text3)); // 041 }42}- 长度和空检查
- 索引和搜索
- 开始和结束检查
- 字符串比较
1// 字符串长度2int length = str.length();34// 空字符串检查5boolean isEmpty = str.isEmpty(); // 检查是否是空字符串 ""67// Java 11+: 空白检查8boolean isBlank = str.isBlank(); // 检查是否只包含空白字符1// 字符访问2char firstChar = str.charAt(0);3char lastChar = str.charAt(str.length() - 1);45// 子字符串搜索6int index = str.indexOf("Hello"); // 首次出现位置7int lastIndex = str.lastIndexOf("o"); // 最后一次出现位置89// 带起始位置的搜索10int fromIndex = str.indexOf("o", 5); // 从索引5开始搜索首次出现位置1112// 包含检查13boolean contains = str.contains("Java"); // 检查是否包含子串1// 开始检查2boolean startsWithHello = str.startsWith("Hello");34// 结束检查5boolean endsWithJava = str.endsWith("Java");67// 带偏移量的开始检查8boolean startsWithWorld = str.startsWith("World", 6); // 从索引6开始检查1// 相等性比较2boolean isEqual = str1.equals(str2); // 区分大小写3boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // 不区分大小写45// 字典顺序比较6int result = str1.compareTo(str2);7// 返回值: 负数表示str1小于str2,0表示相等,正数表示str1大于str289// 不区分大小写字典顺序比较10int resultIgnoreCase = str1.compareToIgnoreCase(str2);Java 11引入了isBlank()、strip()、stripLeading()、stripTrailing()和lines()等新方法,用于增强字符串处理能力。
3.2 字符串提取和分割方法
1public class StringExtractionDemo {2 public static void main(String[] args) {3 String text = "Hello,World,Java,Programming";4 5 // 1. 子字符串提取6 System.out.println("从索引0到5: " + text.substring(0, 5)); // Hello7 System.out.println("从索引6开始: " + text.substring(6)); // World,Java,Programming8 9 // 2. 字符串分割10 String[] parts = text.split(",");11 System.out.println("分割后的部分:");12 for (int i = 0; i < parts.length; i++) {13 System.out.println(" [" + i + "]: " + parts[i]);14 }15 16 // 3. 使用正则表达式分割17 String text2 = "Hello;World.Java:Programming";18 String[] parts2 = text2.split("[;.:]");19 System.out.println("正则分割后的部分:");20 for (int i = 0; i < parts2.length; i++) {21 System.out.println(" [" + i + "]: " + parts2[i]);22 }23 24 // 4. 限制分割次数25 String text3 = "a,b,c,d,e,f";26 String[] parts3 = text3.split(",", 3); // 最多分割3次27 System.out.println("限制分割次数:");28 for (int i = 0; i < parts3.length; i++) {29 System.out.println(" [" + i + "]: " + parts3[i]);30 }31 32 // 5. 字符串截取33 String longText = "This is a very long text that needs to be truncated";34 if (longText.length() > 20) {35 String truncated = longText.substring(0, 20) + "...";36 System.out.println("截取后: " + truncated);37 }38 }39}- 子字符串提取
- 分割字符串
- 空白处理
- 转换方法
1// 从索引beginIndex到字符串结束2String sub1 = str.substring(6);34// 从索引beginIndex到endIndex-15String sub2 = str.substring(0, 5); // 提取索引0,1,2,3,4的字符67// 安全截取(避免IndexOutOfBoundsException)8public static String safeSubstring(String str, int start, int end) {9 if (str == null) return "";10 int length = str.length();11 if (start < 0) start = 0;12 if (end > length) end = length;13 if (start > end) return "";14 return str.substring(start, end);15}1// 基本分割2String[] parts = "a,b,c".split(","); // 返回 ["a", "b", "c"]34// 使用正则表达式分割5String[] parts2 = "a;b.c:d".split("[;.:]"); // 返回 ["a", "b", "c", "d"]67// 限制分割次数8String[] parts3 = "a,b,c,d".split(",", 2); // 返回 ["a", "b,c,d"]910// 处理连续分隔符11String[] parts4 = "a,,b".split(","); // 返回 ["a", "", "b"]1// 去除前后空白(传统方法)2String trimmed = " Hello ".trim(); // 返回 "Hello"34// Java 11+ 新方法5String stripped = " Hello ".strip(); // 去除前后Unicode空白6String stripLeading = " Hello ".stripLeading(); // 去除前导空白7String stripTrailing = " Hello ".stripTrailing(); // 去除尾部空白1// 转换为字符数组2char[] chars = "Hello".toCharArray();34// 转换为字节数组5byte[] bytes = "Hello".getBytes();6byte[] utf8Bytes = "Hello".getBytes("UTF-8");78// 转换为代码点数组 (Java 9+)9int[] codePoints = "Hello😊".codePoints().toArray();3.3 字符串修改方法
1public class StringModificationDemo {2 public static void main(String[] args) {3 String original = " Hello World ";4 5 // 1. 去除空白字符6 System.out.println("原始字符串: '" + original + "'");7 System.out.println("去除前后空白: '" + original.trim() + "'");8 System.out.println("去除前导空白: '" + original.stripLeading() + "'"); // Java 11+9 System.out.println("去除尾部空白: '" + original.stripTrailing() + "'"); // Java 11+10 11 // 2. 大小写转换12 String text = "Hello World";13 System.out.println("原字符串: " + text);14 System.out.println("转大写: " + text.toUpperCase());15 System.out.println("转小写: " + text.toLowerCase());16 17 // 3. 字符串替换18 String replaceText = "Hello World Hello Java";19 System.out.println("原字符串: " + replaceText);20 System.out.println("替换Hello为Hi: " + replaceText.replace("Hello", "Hi"));21 System.out.println("替换第一个Hello: " + replaceText.replaceFirst("Hello", "Hi"));22 System.out.println("替换所有o为0: " + replaceText.replace('o', '0'));23 24 // 4. 正则表达式替换25 String regexText = "Hello123World456Java";26 System.out.println("原字符串: " + regexText);27 System.out.println("替换数字为#: " + regexText.replaceAll("\\d+", "#"));28 System.out.println("替换第一个数字序列: " + regexText.replaceFirst("\\d+", "#"));29 30 // 5. 字符串连接31 String s1 = "Hello";32 String s2 = "World";33 String s3 = "Java";3435// 使用concat方法36 String result1 = s1.concat(" ").concat(s2).concat(" ").concat(s3);37 System.out.println("concat结果: " + result1);38 39 // 使用+操作符40 String result2 = s1 + " " + s2 + " " + s3;41 System.out.println("+操作符结果: " + result2);42 43 // 使用String.join44 String result3 = String.join(" ", s1, s2, s3);45 System.out.println("String.join结果: " + result3);46 }47}- 大小写转换
- 替换操作
- 连接字符串
- 格式化
1// 转大写2String upper = "hello".toUpperCase(); // "HELLO"34// 转小写5String lower = "HELLO".toLowerCase(); // "hello"67// 特定语言环境的大小写转换8String turkishUpper = "istanbul".toUpperCase(Locale.forLanguageTag("tr-TR"));9// 在土耳其语中,i转大写为İ(带点的I)1// 替换单个字符2String replaced1 = "hello".replace('l', 'w'); // "hewwo"34// 替换子字符串5String replaced2 = "hello world".replace("world", "java"); // "hello java"67// 使用正则表达式替换所有匹配8String replaced3 = "hello123world456".replaceAll("\\d+", "#"); // "hello#world#"910// 替换第一个匹配11String replaced4 = "hello hello".replaceFirst("hello", "hi"); // "hi hello"1// 使用concat方法2String s1 = "hello".concat(" ").concat("world"); // "hello world"34// 使用+运算符(编译器会优化)5String s2 = "hello" + " " + "world"; // "hello world"67// 使用String.join (Java 8+)8String s3 = String.join(" ", "hello", "world"); // "hello world"9String s4 = String.join(",", List.of("a", "b", "c")); // "a,b,c"1011// 使用StringJoiner (Java 8+)12StringJoiner joiner = new StringJoiner(", ", "[", "]");13joiner.add("apple").add("banana").add("cherry");14String s5 = joiner.toString(); // "[apple, banana, cherry]"1// 基本格式化2String formatted = String.format("Hello, %s", "World"); // "Hello, World"34// 多参数格式化5String multi = String.format("Name: %s, Age: %d", "Alice", 30);67// 数字格式化8String number = String.format("%.2f", 123.456); // "123.46"910// Java 15+ 文本块11String textBlock = """12 Hello,13 World!14 """;- 对于简单的连接,使用
+运算符最为清晰 - 在循环中连接字符串,使用
StringBuilder而不是+ - 对集合元素进行连接,使用
String.join或StringJoiner - 对格式化输出,使用
String.format或printf
4. 字符串操作和性能
- 字符串连接性能分析
- StringBuilder和StringBuffer
- 字符串池优化
4.1 字符串连接性能分析
1public class StringConcatenationPerformance {2 public static void main(String[] args) {3 int iterations = 10000;4 5 // 1. 使用+操作符连接6 long startTime = System.currentTimeMillis();7 String result1 = "";8 for (int i = 0; i < iterations; i++) {9 result1 += "String" + i + " ";10 }11 long endTime = System.currentTimeMillis();12 System.out.println("+操作符耗时: " + (endTime - startTime) + "ms");13 14 // 2. 使用StringBuilder连接15 startTime = System.currentTimeMillis();16 StringBuilder sb = new StringBuilder();17 for (int i = 0; i < iterations; i++) {18 sb.append("String").append(i).append(" ");19 }20 String result2 = sb.toString();21 endTime = System.currentTimeMillis();22 System.out.println("StringBuilder耗时: " + (endTime - startTime) + "ms");23 24 // 3. 使用StringBuffer连接25 startTime = System.currentTimeMillis();26 StringBuffer sbf = new StringBuffer();27 for (int i = 0; i < iterations; i++) {28 sbf.append("String").append(i).append(" ");29 }30 String result3 = sbf.toString();31 endTime = System.currentTimeMillis();32 System.out.println("StringBuffer耗时: " + (endTime - startTime) + "ms");33 34 // 4. 使用String.join35 startTime = System.currentTimeMillis();36 String[] strings = new String[iterations];37 for (int i = 0; i < iterations; i++) {38 strings[i] = "String" + i;39 }40 String result4 = String.join(" ", strings);41 endTime = System.currentTimeMillis();42 System.out.println("String.join耗时: " + (endTime - startTime) + "ms");43 44 // 验证结果长度45 System.out.println("结果长度验证:");46 System.out.println(" +操作符: " + result1.length());47 System.out.println(" StringBuilder: " + result2.length());48 System.out.println(" StringBuffer: " + result3.length());49 System.out.println(" String.join: " + result4.length());50 }51}- + 操作符
- StringBuilder
- StringBuffer
- String.join
优点:
- 语法简洁,易于理解
- 少量字符串连接时性能可接受
- 编译时常量会被优化
缺点:
- 大量连接或在循环中使用性能极差
- 每次连接都创建新的String对象
- 循环中使用会导致O(n²)时间复杂度
适用场景:简单的少量字符串连接,编译时常量连接
1// 编译优化示例2String s = "Hello" + " " + "World"; // 编译器优化为 "Hello World"34// 不推荐的用法5String result = "";6for (int i = 0; i < 1000; i++) {7 result += i; // 非常低效!8}优点:
- 可变字符序列,避免创建多余对象
- 性能最佳(非线程安全环境)
- 支持链式调用
- 可预分配容量减少扩容成本
缺点:
- 非线程安全
- 代码冗长度高于+操作符
适用场景:单线程环境下的大量字符串操作,尤其是在循环中
1// 高效用法2StringBuilder sb = new StringBuilder(1000); // 预分配容量3for (int i = 0; i < 1000; i++) {4 sb.append(i);5}6String result = sb.toString();78// 链式调用9String message = new StringBuilder()10 .append("Hello, ")11 .append("World")12 .append("!")13 .toString();优点:
- 线程安全
- 可变字符序列
- 适合多线程环境
缺点:
- 性能低于StringBuilder (约慢10-15%)
- 代码冗长度高于+操作符
适用场景:多线程环境下的字符串操作
1// 多线程环境示例2StringBuffer buffer = new StringBuffer();34Runnable task = () -> {5 for (int i = 0; i < 100; i++) {6 buffer.append(i); // 线程安全7 }8};910Thread t1 = new Thread(task);11Thread t2 = new Thread(task);12t1.start(); t2.start();优点:
- 专为集合元素连接设计
- 清晰简洁的API
- 比循环+连接高效
缺点:
- Java 8+才可用
- 仅适用于已知元素的连接
- 不适合动态添加内容
适用场景:连接数组或集合中的元素
1// 数组元素连接2String[] fruits = {"Apple", "Banana", "Cherry"};3String result = String.join(", ", fruits);45// 集合元素连接6List<String> names = List.of("Alice", "Bob", "Charlie");7String nameList = String.join("; ", names);在循环中使用+或concat()连接字符串是常见的性能陷阱。这会导致每次迭代都创建新的字符串对象,时间复杂度为O(n²)。始终在此类场景使用StringBuilder。
4.2 StringBuilder和StringBuffer
1public class StringBuilderBufferDemo {2 public static void main(String[] args) {3 // 1. StringBuilder基本操作4 StringBuilder sb = new StringBuilder();5 sb.append("Hello");6 sb.append(" ");7 sb.append("World");8 sb.append(" ");9 sb.append("Java");10 11 System.out.println("StringBuilder结果: " + sb.toString());12 System.out.println("长度: " + sb.length());13 System.out.println("容量: " + sb.capacity());14 15 // 2. StringBuilder插入和删除16 sb.insert(5, " Beautiful ");17 System.out.println("插入后: " + sb.toString());18 19 sb.delete(5, 16);20 System.out.println("删除后: " + sb.toString());21 22 sb.replace(5, 10, "Amazing");23 System.out.println("替换后: " + sb.toString());24 25 // 3. StringBuilder反转26 sb.reverse();27 System.out.println("反转后: " + sb.toString());28 sb.reverse(); // 恢复原状29 30 // 4. StringBuilder设置长度31 sb.setLength(10);32 System.out.println("设置长度后: " + sb.toString());33 34 // 5. StringBuffer(线程安全版本)35 StringBuffer sbf = new StringBuffer("Thread Safe");36 sbf.append(" String Buffer");37 System.out.println("StringBuffer结果: " + sbf.toString());38 39 // 6. 性能对比40 int iterations = 100000;41 42 // StringBuilder性能43 long startTime = System.currentTimeMillis();44 StringBuilder sb2 = new StringBuilder();45 for (int i = 0; i < iterations; i++) {46 sb2.append(i);47 }48 long endTime = System.currentTimeMillis();49 System.out.println("StringBuilder耗时: " + (endTime - startTime) + "ms");50 51 // StringBuffer性能52 startTime = System.currentTimeMillis();53 StringBuffer sbf2 = new StringBuffer();54 for (int i = 0; i < iterations; i++) {55 sbf2.append(i);56 }57 endTime = System.currentTimeMillis();58 System.out.println("StringBuffer耗时: " + (endTime - startTime) + "ms");59 }60}- 常用操作
- 容量管理
- 线程安全性
创建
1// 默认容量构造2StringBuilder sb1 = new StringBuilder();34// 指定初始容量5StringBuilder sb2 = new StringBuilder(100);67// 初始字符串8StringBuilder sb3 = new StringBuilder("Hello");添加内容
1StringBuilder sb = new StringBuilder();2sb.append("Hello"); // 添加字符串3sb.append(' '); // 添加字符4sb.append(123); // 添加数字5sb.append(true); // 添加布尔值6sb.append(new char[]{'a', 'b', 'c'}); // 添加字符数组插入内容
1StringBuilder sb = new StringBuilder("HelloWorld");2sb.insert(5, ' '); // Hello World3sb.insert(0, "Start: "); // Start: Hello World删除内容
1StringBuilder sb = new StringBuilder("Hello World");2sb.delete(5, 6); // HelloWorld (删除空格)3sb.deleteCharAt(5); // HelloWorld (删除W)替换内容
1StringBuilder sb = new StringBuilder("Hello World");2sb.replace(6, 11, "Java"); // Hello Java容量原理
StringBuilder和StringBuffer内部维护一个字符数组,初始容量为16。当容量不足时,会自动扩容。默认的扩容策略是将容量翻倍+2。
1// 检查和管理容量2StringBuilder sb = new StringBuilder();3System.out.println("初始容量: " + sb.capacity()); // 通常为1645// 确保容量足够6sb.ensureCapacity(100);7System.out.println("扩容后: " + sb.capacity());89// 压缩容量到实际所需10sb.append("Hello");11sb.trimToSize();12System.out.println("压缩后: " + sb.capacity()); // 应为5自定义初始容量
如果预知大概需要的容量,最好在创建时指定,避免扩容开销:
1// 好的做法: 预估大小2StringBuilder sb = new StringBuilder(1000);3for (int i = 0; i < 1000; i++) {4 sb.append(i);5}67// 不好的做法: 频繁扩容8StringBuilder sb2 = new StringBuilder();9for (int i = 0; i < 1000; i++) {10 sb2.append(i);11}StringBuilder (非线程安全)
1// 单线程环境 - 正确2StringBuilder sb = new StringBuilder();3for (int i = 0; i < 1000; i++) {4 sb.append(i);5}67// 多线程环境 - 错误! 可能导致数据损坏或异常8StringBuilder sharedSb = new StringBuilder();9Runnable task = () -> {10 for (int i = 0; i < 1000; i++) {11 sharedSb.append(i); // 线程不安全!12 }13};14new Thread(task).start();15new Thread(task).start();StringBuffer (线程安全)
1// 多线程环境 - 正确2StringBuffer buffer = new StringBuffer();3Runnable task = () -> {4 for (int i = 0; i < 1000; i++) {5 buffer.append(i); // 线程安全6 }7};8new Thread(task).start();9new Thread(task).start();StringBuffer的线程安全是通过在每个方法上添加synchronized关键字实现的,这保证了多线程环境下的安全性,但也带来了约10-15%的性能损失。
4.3 字符串池优化
1public class StringPoolOptimization {2 public static void main(String[] args) {3 // 1. 编译时常量优化4 String s1 = "Hello" + "World"; // 编译时优化为"HelloWorld"5 String s2 = "HelloWorld";6 System.out.println("编译时优化: " + (s1 == s2)); // true7 8 // 2. 运行时连接9 String s3 = "Hello";10 String s4 = "World";11 String s5 = s3 + s4; // 运行时创建新对象12 String s6 = "HelloWorld";13 System.out.println("运行时连接: " + (s5 == s6)); // false14 15 // 3. final变量优化16 final String finalS1 = "Hello";17 final String finalS2 = "World";18 String s7 = finalS1 + finalS2; // 编译时优化19 System.out.println("final变量优化: " + (s7 == s6)); // true20 21 // 4. intern()方法使用22 String s8 = new String("InternTest");23 String s9 = s8.intern();24 String s10 = "InternTest";25 System.out.println("intern()优化: " + (s9 == s10)); // true26 27 // 5. 字符串池大小限制28 System.out.println("字符串池优化建议:");29 System.out.println(" - 对于频繁使用的字符串,使用字面量创建");30 System.out.println(" - 对于动态创建的字符串,考虑使用intern()");31 System.out.println(" - 避免在循环中创建大量字符串对象");32 System.out.println(" - 使用StringBuilder进行大量字符串操作");33 }34}- 编译时优化
- final变量优化
- intern()方法
编译器会在编译期间优化字符串常量表达式,将多个字符串字面量连接为一个:
1// 以下代码会被编译为 String s = "HelloWorld";2String s = "Hello" + "World";字符串常量折叠
这种优化称为"字符串常量折叠"(String constant folding),它只适用于编译时常量表达式:
1// 编译时优化示例2String s1 = "Hello" + "World" + "!"; // 编译为 "HelloWorld!"3String s2 = "HelloWorld!";4System.out.println(s1 == s2); // true56// 非编译时常量,不会优化7String hello = "Hello";8String world = "World";9String s3 = hello + world; // 运行时连接,创建新对象10System.out.println(s3 == s2); // false使用final修饰的变量在编译期间是已知的常量,编译器可以对它们进行优化:
1// final变量优化2final String hello = "Hello";3final String world = "World";4String s1 = hello + world; // 编译为 "HelloWorld"56// 非final变量不会优化7String h = "Hello";8String w = "World";9String s2 = h + w; // 运行时连接1011System.out.println(s1 == "HelloWorld"); // true12System.out.println(s2 == "HelloWorld"); // false如果查看编译后的字节码,你会发现final变量的连接已经在编译阶段被替换为常量"HelloWorld",而非final变量的连接会调用StringBuilder的append方法。
intern()方法可以将运行时创建的字符串添加到字符串常量池,并返回常量池中的引用:
1// 动态创建的字符串2String s1 = new String("Hello"); // 堆中的对象3String s2 = s1.intern(); // 常量池中的引用4String s3 = "Hello"; // 常量池中的引用56System.out.println(s1 == s2); // false7System.out.println(s2 == s3); // true何时使用intern()
- 大量相同内容的字符串对象
- 内存敏感应用
- 需要频繁比较字符串引用相等性的场景
1// 有效使用intern()的例子2Map<String, Data> dataCache = new HashMap<>();34void processData(String key, Data value) {5 // 使用intern()避免重复的字符串对象6 dataCache.put(key.intern(), value);7}intern()方法需要全局字符串表查找,可能影响性能。在Java 7之前,常量池在PermGen空间,大小有限,过度使用intern()可能导致OutOfMemoryError。Java 7及以后,常量池移至堆内存,这个问题有所缓解,但仍需谨慎使用。
- 优先使用字面量创建字符串,而不是构造函数
- 对于需要频繁使用且内容相同的字符串,考虑使用intern()
- 利用final变量的编译时优化特性
- 对于大量字符串操作,使用StringBuilder而非+运算符
- 使用
System.getProperty("java.lang.string.intern.count")查看常量池使用情况(JVM特定参数)
5. 高级字符串操作
5.1 正则表达式操作
1public class StringRegexDemo {2 public static void main(String[] args) {3 // 1. 基本正则表达式匹配4 String text = "Hello123World456Java789";5 6 // 匹配数字7 String[] numbers = text.split("\\D+");8 System.out.println("提取的数字:");9 for (String number : numbers) {10 if (!number.isEmpty()) {11 System.out.println(" " + number);12 }13 }14 15 // 匹配字母16 String[] letters = text.split("\\d+");17 System.out.println("提取的字母:");18 for (String letter : letters) {19 if (!letter.isEmpty()) {20 System.out.println(" " + letter);21 }22 }23 24 // 2. 复杂正则表达式25 String email = "user@example.com";26 String phone = "123-456-7890";27 String date = "2023-12-25";28 29 // 邮箱验证30 boolean isValidEmail = email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");31 System.out.println("邮箱格式有效: " + isValidEmail);32 33 // 电话号码验证34 boolean isValidPhone = phone.matches("^\\d{3}-\\d{3}-\\d{4}$");35 System.out.println("电话号码格式有效: " + isValidPhone);36 37 // 日期格式验证38 boolean isValidDate = date.matches("^\\d{4}-\\d{2}-\\d{2}$");39 System.out.println("日期格式有效: " + isValidDate);40 41 // 3. 正则表达式替换42 String htmlText = "<p>Hello <b>World</b> <i>Java</i></p>";43 String plainText = htmlText.replaceAll("<[^>]+>", "");44 System.out.println("HTML标签移除后: " + plainText);45 46 // 4. 捕获组使用47 String logEntry = "2023-12-25 10:30:45 [INFO] User login successful";48 String pattern = "(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}) \\[(\\w+)\\] (.+)";49 50 if (logEntry.matches(pattern)) {51 String[] groups = logEntry.replaceAll(pattern, "$1|$2|$3|$4").split("\\|");52 System.out.println("日志解析结果:");53 System.out.println(" 日期: " + groups[0]);54 System.out.println(" 时间: " + groups[1]);55 System.out.println(" 级别: " + groups[2]);56 System.out.println(" 消息: " + groups[3]);57 }58 }59}5.2 字符串格式化
1public class StringFormattingDemo {2 public static void main(String[] args) {3 // 1. String.format方法4 String name = "张三";5 int age = 25;6 double salary = 12345.67;7 8 String formatted1 = String.format("姓名: %s, 年龄: %d, 薪资: %.2f", name, age, salary);9 System.out.println("格式化结果1: " + formatted1);10 11 // 2. 数字格式化12 String numberFormat = String.format("整数: %d, 八进制: %o, 十六进制: %x, 科学计数: %e", 13 100, 100, 100, 1000000.0);14 System.out.println("数字格式化: " + numberFormat);15 16 // 3. 字符串对齐和填充17 String leftAlign = String.format("左对齐: %-10s", "Hello");18 String rightAlign = String.format("右对齐: %10s", "World");19 String centerAlign = String.format("居中: %10s", "Java");20 21 System.out.println(leftAlign + "|");22 System.out.println(rightAlign + "|");23 System.out.println(centerAlign + "|");24 25 // 4. 日期时间格式化26 java.util.Date now = new java.util.Date();27 String dateFormat = String.format("当前时间: %tF %tT", now, now);28 System.out.println("日期时间格式化: " + dateFormat);29 30 // 5. 货币格式化31 double amount = 1234567.89;32 String currencyFormat = String.format("金额: $%,.2f", amount);33 System.out.println("货币格式化: " + currencyFormat);34 35 // 6. 自定义格式化36 String customFormat = String.format("自定义: %1$s的%2$s是%3$d", "张三", "年龄", 25);37 System.out.println("自定义格式化: " + customFormat);38 }39}5.3 字符串编码和国际化
1public class StringEncodingDemo {2 public static void main(String[] args) throws Exception {3 // 1. 不同编码的字符串处理4 String original = "Hello 世界";5 6 // UTF-8编码7 byte[] utf8Bytes = original.getBytes("UTF-8");8 String utf8String = new String(utf8Bytes, "UTF-8");9 System.out.println("UTF-8编码: " + utf8String);10 System.out.println("UTF-8字节数: " + utf8Bytes.length);11 12 // GBK编码13 byte[] gbkBytes = original.getBytes("GBK");14 String gbkString = new String(gbkBytes, "GBK");15 System.out.println("GBK编码: " + gbkString);16 System.out.println("GBK字节数: " + gbkBytes.length);17 18 // 2. 编码问题演示19 String chineseText = "你好世界";20 21 // 错误的编码转换22 try {23 byte[] wrongBytes = chineseText.getBytes("ISO-8859-1");24 String wrongString = new String(wrongBytes, "ISO-8859-1");25 System.out.println("错误编码结果: " + wrongString);26 } catch (Exception e) {27 System.out.println("编码错误: " + e.getMessage());28 }29 30 // 3. 字符集检测31 String[] charsets = {"UTF-8", "GBK", "ISO-8859-1", "UTF-16"};32 String testString = "测试字符串";33 34 System.out.println("字符集兼容性测试:");35 for (String charset : charsets) {36 try {37 byte[] bytes = testString.getBytes(charset);38 String decoded = new String(bytes, charset);39 System.out.println(" " + charset + ": " + decoded + " (兼容)");40 } catch (Exception e) {41 System.out.println(" " + charset + ": 不兼容");42 }43 }44 45 // 4. 国际化支持46 java.util.Locale[] locales = {47 java.util.Locale.US,48 java.util.Locale.CHINA,49 java.util.Locale.JAPAN,50 java.util.Locale.GERMANY51 };52 53 System.out.println("国际化测试:");54 for (java.util.Locale locale : locales) {55 System.out.println(" " + locale.getDisplayCountry() + " (" + locale.getLanguage() + "): " + 56 java.text.NumberFormat.getCurrencyInstance(locale).format(1234.56));57 }58 }59}6. 实际应用场景
6.1 文本处理工具
1public class TextProcessingTools {2 public static void main(String[] args) {3 // 1. 文本统计工具4 String text = "Hello World! This is a sample text for demonstration. " +5 "It contains multiple sentences and various punctuation marks.";6 7 TextStats stats = analyzeText(text);8 System.out.println("文本统计结果:");9 System.out.println(" 字符数: " + stats.characterCount);10 System.out.println(" 单词数: " + stats.wordCount);11 System.out.println(" 句子数: " + stats.sentenceCount);12 System.out.println(" 行数: " + stats.lineCount);13 14 // 2. 文本清理工具15 String dirtyText = " Hello World ! \n\n This is dirty text. ";16 String cleanText = cleanText(dirtyText);17 System.out.println("清理前: '" + dirtyText + "'");18 System.out.println("清理后: '" + cleanText + "'");19 20 // 3. 文本搜索工具21 String searchText = "Java is a programming language. Java is widely used.";22 String searchTerm = "Java";23 int[] positions = findTextPositions(searchText, searchTerm);24 System.out.println("搜索词 '" + searchTerm + "' 出现位置:");25 for (int pos : positions) {26 System.out.println(" 位置: " + pos);27 }28 29 // 4. 文本替换工具30 String template = "Hello {name}, welcome to {company}!";31 String result = replacePlaceholders(template, "张三", "ABC公司");32 System.out.println("模板替换结果: " + result);33 }34 35 // 文本统计类36 static class TextStats {37 int characterCount, wordCount, sentenceCount, lineCount;38 }39 40 // 分析文本统计信息41 public static TextStats analyzeText(String text) {42 TextStats stats = new TextStats();43 44 if (text == null || text.isEmpty()) {45 return stats;46 }47 48 stats.characterCount = text.length();49 stats.wordCount = text.split("\\s+").length;50 stats.sentenceCount = text.split("[.!?]+").length;51 stats.lineCount = text.split("\n").length;52 53 return stats;54 }55 56 // 清理文本57 public static String cleanText(String text) {58 if (text == null) return "";59 60 return text.trim()61 .replaceAll("\\s+", " ") // 多个空白字符替换为单个空格62 .replaceAll("\\n\\s*\\n", "\n") // 多个空行替换为单个空行63 .trim();64 }65 66 // 查找文本位置67 public static int[] findTextPositions(String text, String searchTerm) {68 if (text == null || searchTerm == null || searchTerm.isEmpty()) {69 return new int[0];70 }71 72 java.util.List<Integer> positions = new java.util.ArrayList<>();73 int index = 0;74 75 while ((index = text.indexOf(searchTerm, index)) != -1) {76 positions.add(index);77 index += searchTerm.length();78 }79 80 return positions.stream().mapToInt(Integer::intValue).toArray();81 }82 83 // 替换占位符84 public static String replacePlaceholders(String template, String name, String company) {85 return template.replace("{name}", name).replace("{company}", company);86 }87}6.2 配置文件解析
1public class ConfigFileParser {2 public static void main(String[] args) {3 // 模拟配置文件内容4 String configContent = 5 "# 数据库配置\n" +6 "db.host=localhost\n" +7 "db.port=3306\n" +8 "db.username=admin\n" +9 "db.password=123456\n" +10 "\n" +11 "# 应用配置\n" +12 "app.name=MyApplication\n" +13 "app.version=1.0.0\n" +14 "app.debug=true\n" +15 "app.timeout=30000";16 17 // 解析配置文件18 java.util.Map<String, String> config = parseConfig(configContent);19 20 System.out.println("解析的配置:");21 for (java.util.Map.Entry<String, String> entry : config.entrySet()) {22 System.out.println(" " + entry.getKey() + " = " + entry.getValue());23 }24 25 // 获取特定配置26 System.out.println("\n数据库主机: " + config.get("db.host"));27 System.out.println("应用名称: " + config.get("app.name"));28 System.out.println("调试模式: " + config.get("app.debug"));29 30 // 类型转换31 int port = Integer.parseInt(config.get("db.port"));32 boolean debug = Boolean.parseBoolean(config.get("app.debug"));33 long timeout = Long.parseLong(config.get("app.timeout"));34 35 System.out.println("\n类型转换结果:");36 System.out.println(" 端口号: " + port + " (int)");37 System.out.println(" 调试模式: " + debug + " (boolean)");38 System.out.println(" 超时时间: " + timeout + " (long)");39 }40 41 // 解析配置文件42 public static java.util.Map<String, String> parseConfig(String content) {43 java.util.Map<String, String> config = new java.util.HashMap<>();44 45 if (content == null || content.isEmpty()) {46 return config;47 }48 49 String[] lines = content.split("\n");50 51 for (String line : lines) {52 line = line.trim();53 54 // 跳过空行和注释行55 if (line.isEmpty() || line.startsWith("#")) {56 continue;57 }58 59 // 解析键值对60 int equalIndex = line.indexOf('=');61 if (equalIndex > 0) {62 String key = line.substring(0, equalIndex).trim();63 String value = line.substring(equalIndex + 1).trim();64 config.put(key, value);65 }66 }67 68 return config;69 }70}7. 面试题精选
7.1 基础概念题
Q: String、StringBuilder、StringBuffer的区别是什么?
A: 三者的主要区别:
- String: 不可变类,线程安全,适合少量字符串操作
- StringBuilder: 可变类,非线程安全,性能最好,适合单线程环境
- StringBuffer: 可变类,线程安全,性能略低于StringBuilder,适合多线程环境
Q: 什么是字符串常量池?它有什么作用?
A: 字符串常量池是JVM中专门存储字符串字面量的内存区域,主要作用:
- 节省内存空间,避免重复创建相同内容的字符串对象
- 提高字符串比较效率,可以直接使用==比较
- 支持字符串的intern()操作
7.2 性能优化题
Q: 如何优化字符串操作的性能?
A: 字符串性能优化策略:
- 使用StringBuilder进行大量字符串连接
- 合理使用字符串常量池
- 避免在循环中创建字符串对象
- 使用String.join替代循环连接
- 合理设置StringBuilder的初始容量
Q: 什么情况下使用intern()方法?
A: intern()方法适用于:
- 需要频繁比较的字符串
- 内存敏感的应用
- 字符串内容相对固定的场景
- 需要节省内存的场景
7.3 实践题
Q: 实现一个字符串反转方法,要求不使用额外空间
A:
1public class StringReversal {2 public static String reverse(String str) {3 if (str == null || str.length() <= 1) {4 return str;5 }6 7 char[] chars = str.toCharArray();8 int left = 0, right = chars.length - 1;9 10 while (left < right) {11 char temp = chars[left];12 chars[left] = chars[right];13 chars[right] = temp;14 left++;15 right--;16 }17 18 return new String(chars);19 }20 21 public static void main(String[] args) {22 String test = "Hello World";23 System.out.println("原字符串: " + test);24 System.out.println("反转后: " + reverse(test));25 }26}Q: 实现一个方法判断字符串是否为回文
A:
1public class PalindromeChecker {2 public static boolean isPalindrome(String str) {3 if (str == null) return false;4 5 // 移除所有非字母数字字符并转换为小写6 String clean = str.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();7 8 if (clean.isEmpty()) return true;9 10 int left = 0, right = clean.length() - 1;11 12 while (left < right) {13 if (clean.charAt(left) != clean.charAt(right)) {14 return false;15 }16 left++;17 right--;18 }19 20 return true;21 }22 23 public static void main(String[] args) {24 String[] tests = {25 "A man, a plan, a canal: Panama",26 "race a car",27 "Was it a car or a cat I saw?",28 "Hello World"29 };30 31 for (String test : tests) {32 System.out.println("'" + test + "' 是回文: " + isPalindrome(test));33 }34 }35}8. 总结
8.1 核心要点回顾
- 不可变性: String对象一旦创建就不能修改,所有修改操作都返回新对象
- 字符串常量池: JVM优化机制,避免重复创建相同内容的字符串
- 性能考虑: 大量字符串操作时使用StringBuilder,少量操作使用String
- 编码处理: 正确处理字符串编码,避免乱码问题
- 最佳实践: 合理使用字符串方法,注意性能优化
8.2 学习建议
- 深入理解内存机制: 掌握字符串在内存中的存储方式
- 实践性能测试: 通过实际测试了解不同方法的性能差异
- 关注新特性: 了解Java新版本中String类的改进
- 实际项目应用: 在实际项目中应用字符串处理技巧
8.3 进阶方向
- 文本分析算法: 学习字符串匹配、搜索等算法
- 自然语言处理: 了解文本处理的更高级应用
- 性能调优: 深入学习JVM调优和性能分析
- 框架应用: 学习Spring等框架中的字符串处理
通过本章的学习,你应该已经掌握了Java String类的核心特性、使用方法和性能优化技巧。String类是Java编程中最基础也是最重要的类之一,良好的字符串处理能力是Java开发者的必备技能。
记住:选择合适的字符串处理方式,既能提高代码可读性,也能优化程序性能。
参与讨论