【Java String】类深度解析:从原理到高效使用技巧
🎁个人主页:User_芊芊君子
🎉欢迎大家点赞👍评论📝收藏⭐文章
🔍系列专栏:【Java】内容概括
【前言】
在 Java 编程中,String 类是使用频率最高的类之一,也是初学者接触最早的引用类型之一。但正是因为其基础且常用,很多开发者往往忽略了它的底层原理和高级特性。本文将从 String 类的底层实现、核心方法到性能优化、常见误区,全方位解析 Java String 类,帮你彻底搞懂这一基础却关键的类。
文章目录:
- 一、String类本质特征
- 二、String 类核心方法
-
- 1.字符串方法比较
-
- 1.1“==”比较是否引用同一个对象
- 1.2 equals ⽅法:按照字典序⽐较
- 1.3 compareTo ⽅法:按照字典序进⾏⽐较
- 2.字符串查找
- 3.字符串转换
- 4.字符串修改
- 三、总结
一、String类本质特征
1.String类定义
String 是引用类型,内部不储存字符串本身
不可变性(Immutable)
: value 数组被 private final 修饰,且 String 类没有提供修改数组的方法,确保字符串创建后无法被修改final 修饰
:String 类被 final 修饰,无法被继承,保证了字符串操作的安全性和一致性字符数组存储
:底层通过字符数组 char[] 存储字符串内容(JDK 9+ 后改为 byte[] 配合编码标识,优化存储效率)
2.字符串创建及内存机制
String s = \"hello\"
String s = new String(\"hello\")
String类提供的常用的构造方法如下:
public class Test { public static void main(String[] args) { String s1 = \"hello\"; String s2 = new String(\"hello\"); char[] arr = {\'h\',\'e\',\'l\',\'l\',\'0\'}; String s3 = new String(arr); System.out.println(s1); System.out.println(s2); System.out.println(s3); }}
Java构造方法在线文档(可点击查看)
3.字符串常量池(StringTable)
字符串常量池是 JVM 为优化字符串存储引入的内存区域(属于方法区),其核心作用是复用相同内容的字符串对象。
常量池的工作流程:
- 当使用字面量创建字符串时,JVM 先检查常量池是否存在该字符串
- 若存在则直接返回引用,若不存在则创建新对象放入常量池并返回引用
- 通过 intern() 方法可将堆中的字符串对象移入/引用常量池对象(当调⽤intern() ⽅法时,如果字符串常量池中已经包含⼀个等于此String对象的字符串(由equals(Object)⽅法确定),则返回常量池中的字符串。否则,将此String对象添加到常量池中,并返回此String对象的引⽤。)
eg:
public static void main(String[] args) { String str1 = \"abc\"; String str2 = \"abc\"; System.out.println(str1 == str2); } //返回true
- str1 储存时,“abc”会先储存到字符串常量池中
- str2再次储存时,会先检查字符串常量池中是否存在“abc”,如果存在,就不会再次存储
eg:
public static void main(String[] args) { String str1 = new String(\"abc\"); String str2 = new String(\"abc\"); System.out.println(str1 == str2); } //返回false
- 第一次储存时,会将“abc”存储到常量池中;
- 每次new,都会再堆中实例化新的对象
- 储存str2时,会使用常量池的“abc”对象进行存储
二、String 类核心方法
1.字符串方法比较
equals()
equalsIgnoreCase()
compareTo()
==运算符
1.1“==”比较是否引用同一个对象
注意:对于内置类型,“ == ”⽐较的是变量中的值;对于引⽤类型“ == ”⽐较的是引⽤中的地址。
public static void main(String[] args) { int a = 10; int b = 20; int c = 10; //对于基本类型变量,==⽐较两个变量中存储的值是否相同 System.out.println(a == b);// false System.out.println(a == c);// true //对于引⽤类型变量==⽐较两个引⽤变量引⽤的是否为同⼀个对象 String s1 = new String(\"hello\"); String s2 = new String(\"hello\"); String s3 = new String(\"world\"); String s4 = s1; System.out.println(s1 == s2); // false System.out.println(s2 == s3); // false System.out.println(s1 == s4); // true }
1.2 equals ⽅法:按照字典序⽐较
- String类重写了⽗类Object中equals⽅法,Object equals默认按照“==”⽐较
- String重写equals⽅法后,按照如下规则进⾏⽐较,⽐如:s1.equals(s2)
public static void main(String[] args) { String s1 = new String(\"hello\"); String s2 = new String(\"hello\"); String s3 = new String(\"Hello\"); // s1 s2 s3引⽤的是三个不同对象,因此==⽐较结果全部为false System.out.println(s1 == s2);// false System.out.println(s1 == s3);// false // equals⽐较:String对象中的逐个字符 //虽然s1与 s2 引⽤的不是同⼀个对象,但是两个对象中放置的内容相同,因此输出true // s1与s3引⽤的不是同⼀个对象,⽽且两个对象中内容也不同,因此输出false System.out.println(s1.equals(s2)); // true System.out.println(s1.equals(s3)); // false }
1.3 compareTo ⽅法:按照字典序进⾏⽐较
- equals返回的是boolean类型,⽽compareTo返回的是int类型
- compareToIgnoreCase ⽅法:与compareTo⽅式相同,但是忽略⼤⼩写⽐较
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 System.out.println(s1.compareTo(s3)); //相同输出0 System.out.println(s1.compareTo(s4)); //前k 个字符完全相同,输出⻓度差值-3 }
2.字符串查找
char charAt(int index)
int indexOf(int ch)
int indexOf(int ch, int fromIndex)
int indexOf(String str)
int indexOf(String str, int fromIndex)
int lastIndexOf(int ch)
int lastIndexOf(int ch, int fromIndex)
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)
public static void main(String[] args) { String s = \"aaabbbcccaaabbbccc\"; System.out.println(s.charAt(5));//返回下标对应的字符 System.out.println(s.indexOf(\'a\'));//返回a第一次出现的下标 System.out.println(s.indexOf(\'a\',5));//从5开始找a,如果没有,返回-1 System.out.println(s.indexOf(\"aaa\"));//返回aaa第一次出现的位置,没有返回-1 System.out.println(s.indexOf(\"aaa\",5));//从5开始找aaa,如果没有,返回-1 System.out.println(s.lastIndexOf(\'b\'));//从后往前找,返回b第一次出现的位置 System.out.println(s.lastIndexOf(\'b\',10));//从后往前找,返回b第一次出现的位置 System.out.println(s.lastIndexOf(\"bbb\"));//从后往前找,返回bbb第一次出现的位置 System.out.println(s.lastIndexOf(\"bbb\",10));//从10开始,从后往前,bbb第一次出现的位置 }
3.字符串转换
- 数值和字符串转换
public static void main(String[] args) { //数字转字符串 String s1 = String.valueOf(123); String s2 = String.valueOf(12.3); String s3 = String.valueOf(true); String s4 = String.valueOf(new Person(\"Peter\",20)); System.out.println(s1); System.out.println(s2); System.out.println(s3); System.out.println(s4); //字符串转数字 int str1 = Integer.parseInt(\"123\"); Double str2 = Double.parseDouble(\"12.3\"); System.out.println(str1); System.out.println(str2); }
- 大小写转换
public static void main(String[] args) { String s1 = \"hello\"; String s2 = \"HELLO\"; System.out.println(s1.toUpperCase());//小转大 System.out.println(s2.toLowerCase());//大转小 }
- 字符串转数组
public static void main(String[] args) { String s = \"hello\"; //字符串转数组 char[] ch = s.toCharArray(); for (int i = 0; i < ch.length; i++) { System.out.println(ch[i]); } //数组转字符串 String s2 = new String(ch); System.out.println(s2); }
- 格式化
public static void main(String[] args) { String s = String.format(\"%d-%d-%d\",2025,8,5); System.out.println(s); }
- 字符串替换
所有
的指定内容首个
内容public static void main(String[] args) { String str = \"hello\"; System.out.println(str.replaceAll(\"o\",\"e\")); System.out.println(str.replaceFirst(\"l\",\"e\")); }
【注意】:由于字符串是不可变对象,替换不修改当前字符串,⽽是产⽣⼀个新的字符串.
- 字符串拆分
(1)按照空格拆分
public static void main(String[] args) { String str = \"hello world hello\"; String[] ret = str.split(\" \");//按照空格拆分 for (String s: ret){ System.out.println(s); } }
(2)按照空格拆分,分成两组
public static void main(String[] args) { String str = \"hello world hello\"; String[] ret = str.split(\" \",2);//按照空格拆分,分成两组 for (String s: ret){ System.out.println(s); } }
(3)拆分IP地址
public static void main(String[] args) { String str = \"145.169.1.1\"; String[] ret = str.split(\"\\\\.\"); for (String s: ret){ System.out.println(s); } }
(4)多次拆分
public static void main(String[] args) { 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]); } }
- 字符串截取
【注意】:区间左闭右开
public static void main(String[] args) { String str = \"hello world\"; System.out.println(str.substring(9)); System.out.println(str.substring(5,9)); }
- 去除左右两边空格
String str = \" hello world \" ; System.out.println(\"[\"+str+\"]\"); System.out.println(\"[\"+str.trim()+\"]\");
4.字符串修改
由于String的不可变性,为了⽅便字符串的修改,Java中⼜提供StringBuilder和StringBuffer类。
- StringBuffer 采用同步处理,属于线程安全操作;
- StringBuilder 未采用同步处理,属于线程不安全操作
public static void main(String[] args) { StringBuffer stringBuffer = new StringBuffer(\"hello\"); stringBuffer.append(\"world\"); System.out.println(stringBuffer); StringBuilder stringBuilder = new StringBuilder(\"hello\"); stringBuilder.append(\"world\"); System.out.println(stringBuilder); }}
三、总结
String 类作为 Java 中最基础也最常用的类,其底层原理和使用技巧值得每个开发者深入掌握。总结本文核心要点:
- 理解不可变性:字符串创建后无法修改,每次修改都会产生新对象
- 掌握创建方式:优先使用字面量创建,避免不必要的 new String()
- 优化拼接性能:动态拼接使用 StringBuilder ,并指定初始容量
- 合理使用常量池:通过 intern() 方法复用字符串,减少内存占用
- 注意空指针安全:始终先判断 null 再调用字符串方法
掌握这些知识,不仅能避免常见的字符串操作误区,还能写出更高效、更优雅的 Java 代码。在实际开发中,应根据具体场景选择合适的字符串操作方式,平衡可读性和性能需求。