
目录
[1 String 类介绍](#1 String 类介绍)
[2 字符串对象的构造](#2 字符串对象的构造)
[3 字符串常量池 StringTable](#3 字符串常量池 StringTable)
[intern () 方法](#intern () 方法)
[4 字符串比较 API](#4 字符串比较 API)
[4.1 equals () 比较内容是否相等](#4.1 equals () 比较内容是否相等)
[4.2 compareTo () 字典序比较](#4.2 compareTo () 字典序比较)
[4.3 compareToIgnoreCase () 忽略大小写字典序比较](#4.3 compareToIgnoreCase () 忽略大小写字典序比较)
[5 String 常用工具方法](#5 String 常用工具方法)
[5.1 字符串和基本类型互相转换](#5.1 字符串和基本类型互相转换)
[5.2 大小写转换](#5.2 大小写转换)
[5.3 字符串与字符数组互相转换](#5.3 字符串与字符数组互相转换)
[5.4 split 字符串拆分(高频笔试考点)](#5.4 split 字符串拆分(高频笔试考点))
[5.5 substring 字符串截取(前闭后开!)](#5.5 substring 字符串截取(前闭后开!))
[5.6 trim () 去除字符串左右两边空格,中间空格保留](#5.6 trim () 去除字符串左右两边空格,中间空格保留)
[6 字符串不可变性(核心重点面试必考)](#6 字符串不可变性(核心重点面试必考))
[7 StringBuilder 和 StringBuffer](#7 StringBuilder 和 StringBuffer)
[StringBuilder 常用 API](#StringBuilder 常用 API)
[String 和 StringBuilder 互相转换](#String 和 StringBuilder 互相转换)
[StringBuilder vs StringBuffer 对比](#StringBuilder vs StringBuffer 对比)
[8 经典 OJ 练习题](#8 经典 OJ 练习题)
[习题 1:字符串中第一个只出现一次的字符(LeetCode387)](#习题 1:字符串中第一个只出现一次的字符(LeetCode387))
[习题 2:最后一个单词的长度(牛客)](#习题 2:最后一个单词的长度(牛客))
[习题 3:验证回文串 LeetCode125](#习题 3:验证回文串 LeetCode125)
本节学习目标
- 认识
String字符串类,掌握多种字符串构造方式 - 理解字符串常量池 StringTable ,分清
==与equals() - 熟练掌握 String 常用 API:比较、转换、拆分、截取、去空格、
intern() - 深刻理解字符串不可变性底层原理
- 分清
StringBuilder与StringBuffer,适用场景、线程安全区别 - 完成字符串经典 OJ 习题
1 String 类介绍
C 语言使用char[]字符数组处理字符串,数据和处理函数分开,不符合面向对象思想 。 Java 专门提供String引用类型,把字符串数据和操作方法封装到一起,开发中字符串使用极其频繁。
String是final修饰的类,不能被继承。
Java String源码

2 字符串对象的构造
常用 4 种构造方式:
java
public static void main(String[] args) {
//方式1:字符串字面量直接赋值(最常用)
String s1 = "hello bit";
System.out.println(s1);
//方式2:new String,传入字符串字面量
String s2 = new String("hello bit");
System.out.println(s2);
//方式3:字符数组构造字符串
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);
System.out.println(s3);
//方式4:字节数组构造字符串
byte[] bytes = {97,98,99,100}; //对应a b c d ASCII
String s4 = new String(bytes);
System.out.println(s4);
}
其他构造方法查阅官方文档:
java.lang.String
3 字符串常量池 StringTable
字符串常量池底层本质是HashTable 哈希表 ,存字符串实例引用,目的:复用字符串对象,减少内存重复开销。
规则:
- 使用双引号字面量
"abc"创建字符串,JVM 优先去字符串常量池查找;- 如果池中已经存在
"abc",直接复用池中的对象引用; - 如果池中不存在,则把字符串放入常量池。
- 如果池中已经存在
new String("abc"):一定会在堆上创建全新 String 对象,不直接复用池引用。
代码对比理解==
⚠️
==对于引用类型比较的是对象地址,不是字符串内容。
java
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2); // true,两个引用都指向常量池同一个对象
}

java
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); // false,new出来是堆上两个不同对象
}

内存图解:
"abc"字面量存放在字符串常量池;str1、str2保存常量池对象地址;new String("abc")在堆开辟新对象,内部 value 字符数组复用池里面内容,但对象本身是新堆实例。
intern () 方法
intern()作用:主动把当前字符串对象,放到 / 查找字符串常量池。
执行逻辑:
- 如果常量池已经存在 equals 相等的字符串,返回常量池中的引用;
- 如果常量池没有,就把当前对象引用加入常量池,返回池内引用。
java
public static void main(String[] args) {
char[] ch = {'a','b','c'};
String s1 = new String(ch); //堆上对象,不在常量池
// s1.intern(); //打开这行,s1引用进入常量池
String s2 = "abc"; //字面量,取自常量池
System.out.println(s1 == s2); //false
//打开 s1.intern();之后
// System.out.println(s1.intern() == s2); //true
}

s1.intern(); //打开这行,s1引用进入常量池

面试高频:
intern()一般用来手动把堆上字符串放入常量池,实现对象复用。
4 字符串比较 API
4.1 equals () 比较内容是否相等
String重写了 Object 的equals(),按字符内容逐个比较。
注意:
==比较地址;业务判断字符串内容相等,必须用 equals,不要用 ==。
java
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("Hello");
System.out.println(s1 == s2); //false 比较地址
System.out.println(s1.equals(s2)); //true 比较内容
System.out.println(s1.equals(s3)); //false 大小写不同
}
4.2 compareTo () 字典序比较
字典序:按照字符的 Unicode 编码大小比较。 返回值规则:
- 遇到第一个不相同字符,返回两个字符的差值;
- 如果前面字符全部一样,返回两个字符串长度的差值。
java
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("ac");
String s3 = new String("abc");
String s4 = new String("abcdef");
System.out.println(s1.compareTo(s2)); //-1,b和c字符差
System.out.println(s1.compareTo(s3)); //0,完全相同
System.out.println(s1.compareTo(s4)); //-3,长度差
}
4.3 compareToIgnoreCase () 忽略大小写字典序比较
java
public static void main(String[] args) {
String s1 = new String("abc");
String s3 = new String("ABc");
System.out.println(s1.compareToIgnoreCase(s3)); //0 忽略大小写相等
}
5 String 常用工具方法
5.1 字符串和基本类型互相转换
- 其他类型 → String:
String.valueOf(数据) - String → 基本类型:包装类的
parseXXX(),例如Integer.parseInt()、Double.parseDouble()
java
public static void main(String[] args) {
//基本类型转字符串
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
//字符串转基本类型
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
}
5.2 大小写转换
⚠️注意:返回新字符串!原字符串不会被修改,String 不可变。
java
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO";
System.out.println(s1.toUpperCase()); //转大写
System.out.println(s2.toLowerCase()); //转小写
}
5.3 字符串与字符数组互相转换
java
public static void main(String[] args) {
String s = "hello";
//字符串 → char数组
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();
//字符数组转回字符串
String s2 = new String(ch);
System.out.println(s2);
}
5.4 split 字符串拆分(高频笔试考点)
String[] split(String regex):按照分隔符把字符串切割,返回字符串数组。 重载:split(regex, limit),limit 代表分割最多生成几份。
⚠️坑:
.*+|属于正则特殊字符,不能直接写,需要转义\\。
示例 1:普通空格分割
java
String str = "hello world hello bit" ;
String[] result = str.split(" ") ;
for(String s: result) {
System.out.println(s);
}
示例 2:限定分割份数
java
String str = "hello world hello bit" ;
String[] result = str.split(" ",2) ;
for(String s: result) {
System.out.println(s);
}
示例 3:分割 IP 地址,.要写\\.
java
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}
转义规则记忆:
. * + |正则特殊符号,Java 字符串写\\符号;- 如果要表示反斜杠
\,Java 字符串需要写\\\\。
示例 4:多个分隔符,用|代表或
java
String str = "name=zhangsan&age=18" ;
String[] result = str.split("&") ;
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=") ;
System.out.println(temp[0]+" = "+temp[1]);
}
5.5 substring 字符串截取(前闭后开!)
substring(beginIndex):从下标截取到末尾substring(beginIndex, endIndex):[begin, end),包含起始下标,不包含结束下标
java
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5)); //取0~4下标字符
5.6 trim () 去除字符串左右两边空格,中间空格保留
java
String str = " hello world " ;
System.out.println("["+str+"]");
System.out.println("["+str.trim()+"]");
6 字符串不可变性(核心重点面试必考)

String对象内容一旦创建,不能修改,属于不可变对象。


底层原理:
- String 内部存储字符的
value字节数组被private final修饰; private:外部拿不到这个数组引用;final:数组引用不能指向别的数组。
❗所有看起来 "修改字符串" 的方法:
toUpperCase、substring、split、+拼接,都不会改动原来字符串,而是返回全新 String 对象。
字符串直接+拼接,会产生大量中间临时对象,性能很差。
java
//不推荐,循环拼接String,产生大量垃圾对象
String s = "";
for(int i = 0; i < 10000; i++){
s += i;
}
如果业务需要频繁修改字符串内容,不要直接用 String,使用
StringBuilder/StringBuffer。
7 StringBuilder 和 StringBuffer
String不可变;StringBuilder、StringBuffer是可变字符串,内部数组内容可以直接修改,不会大量生成新对象。
StringBuilder 常用 API
java
public static void main (String[] args) {
StringBuilder sb1 = new StringBuilder ("hello");
StringBuilder sb2 = sb1;
sb1.append(' '); //追加字符
sb1.append("world"); //追加字符串
sb1.append(123); //追加数字
System.out.println(sb1); // hello world123
System.out.println(sb1 == sb2); //true,同一个对象引用
System.out.println(sb1.charAt(0)); //获取指定下标字符 h
System.out.println(sb1.length()); //有效字符长度
System.out.println(sb1.capacity()); //底层数组总容量
sb1.deleteCharAt(0); //删除指定下标字符
sb1.delete(0,5); //删除区间 [0,5)
sb1.reverse(); //字符串反转
// StringBuilder转回String
String str = sb1.toString();
System.out.println(str);
}
String 和 StringBuilder 互相转换

- String → StringBuilder:
new StringBuilder(字符串)或者append() - StringBuilder → String:调用
.toString()
StringBuilder vs StringBuffer 对比
表格
| 类 | 线程安全 | 性能 | 适用场景 |
|---|---|---|---|
| StringBuilder | ❌非线程安全 | 高 | 单线程,绝大多数业务场景 |
| StringBuffer | ✅synchronized 同步,线程安全 | 较低 | 多线程并发场景下使用 |
开发绝大多数单线程业务优先
StringBuilder。
8 经典 OJ 练习题
习题 1:字符串中第一个只出现一次的字符(LeetCode387)
java
class Solution {
public int firstUniqChar(String s) {
int[] count = new int[256];
//统计每个字符出现次数
for(int i = 0; i < s.length(); ++i){
count[s.charAt(i)]++;
}
//遍历找第一个次数等于1
for(int i = 0; i < s.length(); ++i){
if(1 == count[s.charAt(i)]){
return i;
}
}
return -1;
}
}
习题 2:最后一个单词的长度(牛客)
java
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String s = sc.nextLine();
//找到最后一个空格,截取后面部分求长度
int len = s.substring(s.lastIndexOf(' ')+1, s.length()).length();
System.out.println(len);
}
sc.close();
}
}
习题 3:验证回文串 LeetCode125
java
class Solution {
public static boolean isValidChar(char ch){
if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')){
return true;
}
return false;
}
public boolean isPalindrome(String s) {
s = s.toLowerCase();
int left = 0, right = s.length()-1;
while(left < right){
//左指针找有效字符
while(left < right && !isValidChar(s.charAt(left))){
left++;
}
//右指针找有效字符
while(left < right && !isValidChar(s.charAt(right))){
right--;
}
if(s.charAt(left) != s.charAt(right)){
return false;
}else{
left++;
right--;
}
}
return true;
}
}
📔本篇核心考点总结
- String 是引用类型,
==比较地址;判断字符串内容相等必须使用equals()。 - 字符串常量池
StringTable:字面量会复用池对象;new String()堆上新对象;intern()主动入池。 - String不可变:所有修改操作返回全新字符串;循环拼接不要直接用 String,会产生大量临时对象。
- split 拆分注意正则特殊符号
.*+|,需要转义\\;substring 是前闭后开区间。 StringBuilder可变字符串,单线程高性能;StringBuffer加同步锁,线程安全,性能差。- 转换:
valueOf()其他类型转字符串;parseXXX()字符串转基础类型;toCharArray()字符串转字符数组。
面试简答 Q:为什么 String 设计成不可变?
A:①字符串常量池复用;②哈希缓存安全;③线程安全;④作为 HashMap 的 key 安全。
Q:StringBuilder 和 StringBuffer 区别?
A:StringBuilder 非线程安全,速度快,单线程推荐;StringBuffer 加 synchronized 同步,线程安全,性能低,适合多线程。
Q:String s = new String("abc");创建几个对象?A:如果常量池没有
"abc",2 个对象:常量池字符串对象 + 堆上 new 出来的 String 对象;如果池中已有,则只创建堆上 1 个对象。
