目录写在前面一、String类的重要性二、字符串的三种创建方式2.1 直接赋值2.2 通过new创建2.3 通过字符/字节数组创建三、字符串常量池一个容易被忽视的内存区域3.1 什么是字符串常量池3.2 常量池的位置演变3.3 两种创建方式的内存差异四、字符串的比较4.1 比较的是地址4.2 equals 比较的是内容4.3 compareTo 返回差值五、字符串的常见操作5.1 字符串查找5.2 字符串转换5.3 字符串替换5.4 字符串拆分5.5 字符串截取5.6 去除空格六、字符串的不可变性6.1 什么是不可变性6.2 不可变性的好处6.3 看似修改实则新建七、StringBuilder和StringBuffer7.1 为什么需要它们7.2 StringBuilder的使用7.3 StringBuffer vs StringBuilder7.4 三者之间的转换八、实战练习8.1 找第一个只出现一次的字符8.2 字符串最后一个单词的长度8.3 验证回文串总结写在前面在Java编程中String类可能是我们打交道最多的类之一。无论是接收用户输入、拼接日志信息还是处理网络请求中的数据字符串的身影无处不在。但你真的了解String吗它为什么是不可变的字符串比较到底该用还是equals频繁拼接字符串时为什么性能会急剧下降StringBuilder和StringBuffer又有什么区别带着这些问题我们来一场String类的深度探索之旅。一、String类的重要性在C语言中字符串只能用字符数组或字符指针来表示操作字符串需要借助标准库中的一系列函数。这种方式有一个明显的缺陷数据和操作数据的方法是分离的不符合面向对象的思想。Java语言专门设计了String类来解决这个问题。它将字符串数据和操作方法封装在一起提供了一套完整、便捷的API。再加上字符串在编程中的广泛应用String类成为了Java中最核心的类之一。二、字符串的三种创建方式2.1 直接赋值String s1 hello bit;这是最常用、也最推荐的方式。字符串字面量会存储在字符串常量池中如果后续再有相同的字符串会直接复用常量池中的对象。2.2 通过new创建String s2 new String(hello bit);这种方式会在堆内存中创建一个新的String对象无论常量池中是否已经存在相同的字符串。2.3 通过字符/字节数组创建char[] array {h, e, l, l, o, , b, i, t}; String s3 new String(array); byte[] bytes {97, 98, 99, 100}; String s4 new String(bytes); // 输出: abcd三、字符串常量池一个容易被忽视的内存区域3.1 什么是字符串常量池字符串常量池StringTable是JVM中一块专门用于存储字符串字面量的内存区域。它的核心作用是复用字符串对象节省内存开销。打个比方你家每个月的生活费如果父母按月转账每次都要走银行流程耗时较长。但如果一次性存入银行卡随用随取效率就高了很多。字符串常量池就相当于这张银行卡——先把字符串存好需要用的时候直接取不用重复创建。3.2 常量池的位置演变JDK版本常量池位置特点Java 6方法区永久代固定大小1009容易OOMJava 7堆中可设置默认大小60013Java 8堆中可设置有范围限制最小10093.3 两种创建方式的内存差异示例1直接赋值String str1 abc; String str2 abc; System.out.println(str1 str2); // true执行过程str1创建时abc被存入字符串常量池str2创建时发现常量池中已有abc直接复用两个引用指向常量池中的同一个对象示例2通过new创建String str1 new String(abc); String str2 new String(abc); System.out.println(str1 str2); // false执行过程第一次new时abc存入常量池同时在堆中创建一个新的String对象第二次new时常量池中的abc被复用但堆中又创建了一个新的String对象两个引用指向堆中的不同对象四、字符串的比较4.1 比较的是地址对于基本类型比较的是值对于引用类型比较的是引用地址。String s1 new String(hello); String s2 new String(hello); System.out.println(s1 s2); // false两个不同的对象4.2 equals 比较的是内容String类重写了Object的equals方法按照字典序逐个比较字符。String s1 new String(hello); String s2 new String(hello); System.out.println(s1.equals(s2)); // true内容相同4.3 compareTo 返回差值与equals返回boolean不同compareTo返回int类型String s1 abc; String s2 abd; System.out.println(s1.compareTo(s2)); // 负数因为c d比较规则先按字典序比较字符遇到不同字符直接返回差值如果前面都相同返回长度差值。五、字符串的常见操作5.1 字符串查找String s aaabbbcccaaabbbccc; s.charAt(3); // b s.indexOf(c); // 6第一次出现的位置 s.indexOf(c, 10); // 15从第10位开始找 s.indexOf(bbb); // 3 s.lastIndexOf(c); // 17最后一次出现的位置5.2 字符串转换// 数值转字符串 String s String.valueOf(123); // 大小写转换 Hello.toUpperCase(); // HELLO Hello.toLowerCase(); // hello // 字符串与字符数组互转 char[] ch hello.toCharArray(); String s2 new String(ch); // 格式化 String s String.format(%d-%d-%d, 2024, 3, 15);5.3 字符串替换String str helloworld; str.replaceAll(l, _); // he__owor_d str.replaceFirst(l, _); // he_loworld注意字符串是不可变的替换操作不会修改原字符串而是产生一个新字符串。5.4 字符串拆分String str hello world hello bit; String[] result str.split( ); // 按空格拆分 // 限制拆分次数 String[] result2 str.split( , 2); // 特殊字符需要转义 String ip 192.168.1.1; String[] parts ip.split(\\.); // 点号需要转义 // 多个分隔符 String data namezhangsanage18; String[] pairs data.split(); for (String pair : pairs) { String[] kv pair.split(); System.out.println(kv[0] kv[1]); }5.5 字符串截取String str helloworld; str.substring(5); // world从索引5到末尾 str.substring(0, 5); // hello前闭后开区间5.6 去除空格String str hello world ; str.trim(); // hello world只去除首尾空格六、字符串的不可变性6.1 什么是不可变性String类被设计为不可变对象——一旦创建它的内容就不能被改变。查看String源码可以发现存储字符串的value数组被声明为private final byte[]Stable private final byte[] value;final修饰保证该引用不能指向其他数组private修饰外部无法直接访问String类没有提供任何修改value数组内容的方法6.2 不可变性的好处线程安全不可变对象天然线程安全无需额外的同步措施字符串常量池正因为不可变才能放心地复用字符串对象哈希缓存String的哈希码可以缓存提高HashMap等容器的性能6.3 看似修改实则新建String s hello; s s world;这段代码看似修改了s的内容但实际上hello对象没有被修改而是在堆中创建了一个新的hello world对象s的引用指向了新对象。七、StringBuilder和StringBuffer7.1 为什么需要它们由于String的不可变性频繁拼接字符串会产生大量临时对象严重影响性能。long start System.currentTimeMillis(); String s ; for (int i 0; i 10000; i) { s i; } long end System.currentTimeMillis(); System.out.println(end - start); // 耗时很长每次循环都会创建一个新的String对象效率极低。7.2 StringBuilder的使用StringBuilder是可变的字符序列修改操作直接在原对象上进行不会产生新对象。StringBuilder sb new StringBuilder(hello); sb.append( ); // hello sb.append(world); // hello world sb.append(123); // hello world123 System.out.println(sb); // hello world123 // 支持链式调用 StringBuilder sb2 new StringBuilder(hello); sb2.append( ).append(world).append(123);7.3 StringBuffer vs StringBuilder对比维度StringBufferStringBuilder线程安全是方法使用synchronized否性能较慢较快适用场景多线程环境单线程环境选择建议在单线程环境下优先使用StringBuilder性能更好只有在多线程环境下才需要考虑StringBuffer。7.4 三者之间的转换// String - StringBuilder String str hello; StringBuilder sb new StringBuilder(str); // StringBuilder - String String result sb.toString();八、实战练习8.1 找第一个只出现一次的字符public int firstUniqChar(String s) { int[] count new int[256]; for (int i 0; i s.length(); i) { count[s.charAt(i)]; } for (int i 0; i s.length(); i) { if (count[s.charAt(i)] 1) { return i; } } return -1; }8.2 字符串最后一个单词的长度public int lengthOfLastWord(String s) { s s.trim(); return s.length() - s.lastIndexOf( ) - 1; }8.3 验证回文串public boolean isPalindrome(String s) { s s.toLowerCase(); int left 0, right s.length() - 1; while (left right) { while (left right !Character.isLetterOrDigit(s.charAt(left))) { left; } while (left right !Character.isLetterOrDigit(s.charAt(right))) { right--; } if (s.charAt(left) ! s.charAt(right)) { return false; } left; right--; } return true; }总结今天我们系统地学习了Java中的String类三种创建方式直接赋值推荐、new创建、数组转换字符串常量池复用字符串对象节省内存字符串比较比较地址equals比较内容compareTo返回差值常见操作查找、转换、替换、拆分、截取、去空格不可变性String内容不可变修改操作产生新对象StringBuilder/StringBuffer可变字符序列适合频繁修改的场景String类看似简单但背后涉及的内存管理和设计思想却很值得玩味。理解这些原理不仅能帮你写出更高效的代码也能让你在面对复杂问题时有更深层的思考角度。