Java字符串String类详解
JavaString为引用类型,内部存储指向字符串数据的引用。字符串常量池在堆中,复用字面量。new关键字在堆创建独立对象。==比较引用地址,equals按字典序比较字符,compareTo返回差值。字符串不可变,替换等操作不修改原串。
1,String原理介绍
1.1 String 构造
//使用字符串常量进行赋值
String s1 = "hello world";
System.out.println(s1);
//直接new String对象
String s2 = new String("hello world");
System.out.println(s2);
//使用字符组进行构造
char[] array = {'h','e','l','l','o'};
String s4 = new String(array);
System.out.println(s4);
除了这些,String还有不少其他的构造方法。但归根结底,关键得看透一点:String是个引用类型,它内部并不直接存储字符串本体,存的是指向实际字符串数据的引用。

1.2 字符串常量池(StringTable)
字符串常量池,在JVM层面对应的是StringTable类。本质上,它就是一个固定的HashTable(哈希表),而且它现在是在堆上分配的。很多刚接触的同学容易把这个位置记混,这里值得特别留意。
1.3 字符串内存存储
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
}
//true

- str1赋值的时候,“abc”这个字面量会直接存入字符串常量池。
- 轮到str2时,JVM会先去常量池里瞅一眼,看看“abc”是不是已经在了。在,那就直接复用,不再重复创建。
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);
}
//false

- 第一次new,常量池里肯定还没有“abc”对吧?所以JVM会先在常量池里放一份“abc”,同时在堆上new出一个新对象。
- 每次使用
new关键字,都会在堆中实实在在实例化一个新的对象。所以str1和str2指向的是堆里两个不同的地址。 - 到了str2,“abc”在常量池中已经存在了,它也会拿这个常量去堆里new对象,但这和str1指向的堆对象是两个独立的东西。
2,常用方法介绍
2.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
}
2.2 equals 方法:按字典序比较
字典序,简单来说就是字符按特定大小顺序排列。String类重写了父类Object的equals方法——Object默认用的是==比较,而String则改成了按字符逐个比较。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
看个例子就明白了:
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
}
2.3 compareTo 方法:按字典序进行比较
和equals不同,equals只告诉你“是或否”(boolean),而compareTo会给出一个具体的差值(int),告诉你到底差在哪里、差了多少。它的规则是:
- 先按字典序逐个字符比较,一旦遇到不相等的字符,直接返回这两个字符的差值(Unicode码位的差值)。
- 如果前面k个字符都相等(k取两个字符串长度的较小值),那么返回的就是两个字符串长度的差值。
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.4 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.compareToIgnoreCase(s2)); // 不同输出字符差值-1
System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0
System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出⻓度差值 -3
}
2.5 字符串查找
| 方法 | 功能 |
|---|---|
| char charAt(int index) | 返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常 |
| int indexOf(int ch) | 返回ch第一次出现的位置,没有返回-1 |
| int indexOf(int ch,int fromIndex) | 从fromIndex位置出现找ch第一次出现的位置,没有返回-1 |
| int indexOf(String str) | 返回str第一次出现的位置,没有返回-1 |
| int indexOf(String str,int fromIndex) | 从fromIndex位置开始找str第一次出现的位置,没有返回-1 |
| int lastIndexOf(int ch) | 从后往前找,返回ch第一次出现的位置,没有返回-1 |
| int lastIndexOf(int ch,int fromIndex) | 从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1 |
| int lastIndexOf(String str) | 从后往前找,返回str第一次出现的位置,没有返回-1 |
| int lastIndesOf(String str,int fromIndex) | 从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1 |
public static void main(String[] args) {
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); // 'b'
System.out.println(s.indexOf('c')); // 6
System.out.println(s.indexOf('c', 10)); // 15
System.out.println(s.indexOf("bbb")); // 3
System.out.println(s.indexOf("bbb", 10)); // 12
System.out.println(s.lastIndexOf('c')); // 17
System.out.println(s.lastIndexOf('c', 10)); // 8
System.out.println(s.lastIndexOf("bbb")); // 12
System.out.println(s.lastIndexOf("bbb", 10)); // 3
}
2.6 转换
1. 数值和字符串转化
public static void main(String[] args) {
// 数字转字符串
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
String s4 = String.valueOf(new Student("Hanmeimei", 18));
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
System.out.println("=================================");
// 字符串转数字
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
}
2. 大小写转换
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO";
// ⼩写转⼤写
System.out.println(s1.toUpperCase());
// ⼤写转⼩写
System.out.println(s2.toLowerCase());
}
3. 字符串转数组
public static void main(String[] args) {
String s = "hello";
// 字符串转数组
char[] ch = s.toCharArray();
for (char x :ch) {
System.out.print(x + " ");
}
System.out.println();
// 数组转字符串
String s2 = new String(ch);
System.out.println(s2);
}
4. 格式化
public static void main(String[] args) {
String s = String.format("%d-%d-%d", 2026, 6,1);
System.out.println(s);
}
2.7 字符串替换
String replaceAll(String regex,String replacement) —— 替换所有匹配的指定内容。
String replaceFirst(String regex,String replacement) —— 只替换第一个匹配的内容。
String str = "helloworld" ;
System.out.println(str.replaceAll("l", "_"));
System.out.println(str.replaceFirst("l", "_"));
这里要记住一个特性:字符串是不可变对象,替换操作并不会修改原始字符串本身,而是返回一个新生成的字符串。
2.8 字符串拆分
| 方法 | 功能 |
| String[] split(String regex) | 将字符串全部拆分 |
| String[] split(String regex,int limit) | 将字符串以指定的格式,拆分为limit组 |
public static void main(String[] args) {
String st1 = "hello world yes no";
String[] result = st1.split(" ");
for (String x : result) {
System.out.println(x);
}
//hello
//world
//yes
//no
}
也可以根据需要做部分拆分:
public static void main(String[] args) {
String st1 = "hello world yes no";
String[] result = st1.split(" ",3);
for (String x : result) {
System.out.println(x);
}
//hello
//world
//yes no
}
split的参数涉及正则表达式,所以遇到特殊字符时需要留意转义问题。
拆分IP地址
String str = "192.168.1.1" ;
String[] result = str.split("\.") ;
for(String s: result) {
System.out.println(s);
}
几个注意事项:
- 字符“|”、“*”、“+”都需要加上转义字符,前面加上“\”。
- 如果是“\”,就需要写成“\\”。
- 多个分隔符可以用“|”连接起来。
多次拆分
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 = "name=zhangsan&age=18" ;
String[] result = str.split("=|&") ;
for (String s : result) {
System.out.println(s);
}
}
2.9 字符串的截取
| 方法 | 功能 |
| String substring(int beginIndex) | 从指定索引截取到结尾 |
| String substring(int beginIndex,int endIndex) | 截取部分内容 |
String str = "helloworld" ; System.out.println(str.substring(5)); System.out.println(str.substring(0, 5));
下标从0开始,取值范围是左闭右开的 [beginIndex, endIndex)。
去除左右两边的空格
String trim() 方法去掉字符串左右两边的空格,但会保留中间的空格。
String str = " hello world " ;
System.out.println("["+str+"]");
System.out.println("["+str.trim()+"]");
2.10 intern 方法
- 调用 intern() 方法时,如果字符串常量池里已经有一个与当前String对象内容相等的字符串(通过equals方法确定),就会直接返回常量池中的那个字符串引用。
- 如果常量池中没有,则会将当前String对象的引用添加到常量池中,并返回该引用。
public static void main(String[] args) {
char[] ch = new char[]{'a', 'b', 'c'};
String s1 = new String(ch); // s1对象并不在常量池中
//s1.intern(); // s1.intern();调⽤之后,会将s1对象的引⽤放⼊到常量池中
String s2 = "abc"; // "abc" 在常量池中存在了,s2创建时直接⽤常量池中"abc"的引⽤
System.out.println(s1 == s2);
}
// 输出false
// 将上述⽅法打开之后,就会输出true


3,字符串的不可变性
String被设计为不可变对象,这就意味着一旦创建,字符串中的内容就无法被更改。
所有那些看起来是在修改字符串内容的操作,本质上都是创建一个新的对象,原对象丝毫不动。String之所以能做到这一点,其底层原因在于存放字符的数组被private修饰且不提供公开的修改方法。
4,字符串修改
public static void main(String[] args) {
String s = "hello";
s = s + " world";
System.out.println(s); // 输出:hello world
}
像上面这样拼接字符串,每次操作都会产生临时的中间对象。拼一次就在堆里new一个对象,效率可想而知。所以,为了执行效率,Ja va专门提供了StringBuffer和StringBuilder这两个类。
4.1 StringBuilder
参考文档
| 方法 | 说明 |
|---|---|
| StringBuilder append(String str) | 在尾部追加,相当于String的+= |
| char charAt(int index) | 获取index位置的字符 |
| int length() | 获取字符串的长度 |
| int capacity() | 获取底层保存字符串空间总的大小 |
| void ensureCapacity(int mininmumCapacity) | 确保容量至少等于规定的最小值 |
| void setCharAt(int index,char ch) | 将index位置的字符设置为ch |
| int indexOf(String str) | 返回str第一次出现的位置 |
| int indexOf(String str,int fromIndex) | 返回指定子串的第一次出现的字符串中的索引,从指定的索引开始 |
| int lastIndexOf(String str) | 返回最后一次出现str的位置 |
| int lastIndexOf(String str,int fromIndex) | 从fromIndex位置开始找str最后一次出现的 |
| StringBuilder insert(int offset,String str) | 从offset位置插入 |
| StringBuilder deleteCharAt(int index) | 删除index位置字符 |
| StringBuilder delete(int start,int end) | 删除[start,end)区间内的字符 |
| StringBuilder replace(int start,int end,String str) | 将[start,end)位置的字符替换为str |
| String substring(int start) | 从start开始一直到末尾的字符以String的形式返回 |
| String substring(int start,int end) | 从start开始一直到end的字符以String的形式返回 |
| StringBuilder reverse() | 反转字符串 |
| String toString() | 将所有字符按照String的方式返回 |
StringBuilder的内容是可以修改的,这一点和String有本质区别。
两者之间可以互相转换:
- String变为StringBuilder:利用StringBuilder的构造方法或append()方法。
- StringBuilder变为String:调用toString()方法即可。
4.2 String Buffer
功能上,StringBuffer和StringBuilder几乎一模一样。
最大的区别在于:StringBuffer采用了同步处理,属于线程安全操作;而StringBuilder则未采用同步处理,属于线程不安全操作。所以,在多线程环境下,优先考虑StringBuffer;单线程下,StringBuilder的性能会更高。
介绍文档
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















