javaSE:String专题知识
1.常量池与不可变
(1)几种定义方式
public static void main(String[] args) {
// 使用常量串构造
String s1 = "hello";
System.out.println(s1);
// 直接newString对象
String s2 = new String("hello");
System.out.println(s2);
// 使用字符数组进行构造
char[] array = {'h','e','l','l','o'};
String s3 = new String(array);
System.out.println(s3);
}
结果:

(2)字符串常量池:java内置机制,JVM运行时会自动构造,不用程序员关注
(3)不可变:String类被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中 的内容可以修改(String 不可变,常量池地址也不变;new 对象内容不变,地址可换)
// 1. 常量池:地址不变
String s1 = "Java";
String s2 = "Java"; // 同一常量池对象
System.out.println("常量池地址相同? " + (s1 == s2)); // true
// 2. new 对象:内容相同,地址不同
String s3 = new String("Java");
String s4 = new String("Java");
System.out.println("new 地址相同? " + (s3 == s4)); // false
System.out.println("内容相同? " + s3.equals(s4)); // true
(4)String常量自己会变是错觉——你拿到的已经是另一个全新的 String 对象,只是变量名 reuse 了。
代码看真相:
String s = "Java"; // 常量池对象 A
s = s + "Script"; // 新建对象 B("JavaScript")
System.out.println(s); // 输出 "JavaScript"
过程拆解:
Java→ 常量池对象 A(不可变)+ "Script"→ 新建对象 B("JavaScript")s = ...→ 变量s的引用从 A 改成 B,A 本身从未改变
结论: 变量可以指向新对象,但原 String 对象内容永不变。
2.三种比较
(1)"=="(双等号):两个引用指向的对象是否是同一个
String s1="abc";
String s2="abc";
System.out.println(s1==s2);
结果为:true
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1==s2);
结果为:false(因为这里写法是新创建了两个对象,两个对象地址空间不同,所以两个引用自然不同)
(2)equals:两个引用对应的对象包含的字符内容是否相等
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1.equals(s2));
System.out.println("abc".equals(s2));
结果均为true(equals比较的是内容)
(3)compareTo字典序:String已经实现Comparable,可直接使用compareTo方法,compareToIgnoreCase方法忽略大小写,注意汉字排序按照汉字每个字节比较大小,并不符合我们的使用习惯(按照笔画数量排序)
String s1 = "abc";
String s2 = "def";
int cmp = s1.compareTo(s2); // 字典序:a→d 差值
System.out.println(cmp);
结果:输出 -3 (a 比 d 小 3)
解释:若s1小于s2返回小于0的数,至于什么值就是首字母a的ascll码减去首字母d的ascll码的值
3.查找与截取
(一)查找
(1)charAt:给定下标,获取对应字符
String s1=new String("abc");
System.out.println(s1.charAt(0));
结果:a
(2)indexOf:查找改字符串的某个子字符串,从左往右找,如果是,是处于哪个位置(子串第一个字符下标)
String s = "hello world hello bit";
int idx1 = s.indexOf("world"); // 6
int idx2 = s.indexOf("hello"); // 0
int idx3 = s.indexOf("hello", 5); // 12(从第5位开始找)
System.out.println("indexOf结果:"+idx1+", "+idx2+", "+idx3);
indexOf结果:6, 0, 12
(3)lastIndexOf:也是查找字符串,从右往左找(返回的也是子串第一个字符下标)
String s = "hello world hello bit";
int last1 = s.lastIndexOf("hello"); // 12
int last2 = s.lastIndexOf("hello", 10); // 0(从右往左,但不超过第10位)
System.out.println("lastIndexOf结果:"+last1+", "+last2);
lastIndexOf结果:12, 0
(二)截取
substring方法:
String str="abcdefg";
String s1=str.substring(0,4);//下标范围左闭右开
System.out.println(s1);
结果:abcd
4.转换与格式化
(一)字符串转换
(1)String.valueOf:数字转字符串
String s1 = String.valueOf(123);
结果:“123”
(2)Integer.parseInt:字符串转整型
int n = Integer.parseInt("456");
结果:456
(3)若直接数字+字符串,则结果是字符串,数字的类型(如int型)隐式转换为String类型
String s3 = 789 + "abc";
结果:"789abc"
(4)小写转大写和大写转小写:toUpperCase和toLowerCase,注意这两个方法都是生成新的字符串,而不是在上一个toUpperCase或toLowerCase基础上进行转换,因为String本身是常量不可修改
String s4 = "Hello".toUpperCase();
String s5 = "Hello".toLowerCase();
结果:
"HELLO"
"hello"
(5)toCharArray:将字符串转成字符数组
char[] ch = "abc".toCharArray();
结果:['a','b','c']
(6)getBytes:转为字节数组,经常十六进制形式打印(方便看),原本一个汉字占2个字节,若以三个字节为标准打印,结果展现的是一个汉字,这个和编码方式有关,所以一个汉字可能2个或3个字节,utf-8是世界通用编码也是变长编码,长度可改变所以可支持各种国家语言,因为不同语言的字符的字节数可能不同
(二)格式化
格式化字符串:
int x=10;
System.out.printf("x=%d\n",x);
String s=String.format("x=%d\n",x);
System.out.println(s);
结果都是:x=10
String s=String.format("%d-%d-%d",2026,1,27);
System.out.println(s);
结果:2026-1-27
5.拆分与替换
(一)拆分
(1)split方法:
String s="java,python,c++";
String []str=s.split(",");
for(String s1:str){
System.out.println(s1);
}
结果:
java
python
c++
(2)拆分IP地址:有些特殊字符作为分割符可能无法正确切分, 需要加上转义
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}
结果:
192
168
1
1
注意:
- 字符"|","*","+"都得加上转义字符,前面加上 "\\"
- 而如果是 "\" ,那么就得写成 "\\\\"
- 如果一个字符串中有多个分隔符,可以用"|"作为连字符
(二)替换
(1)repalce方法,可替换所有的字符,替换的结果是新的字符串,不会改变原来的
String s="abcd efgh";
String str=s.replace("abcd","ABCD");
System.out.println(s);
System.out.println(str);
结果:
abcd efgh
ABCD efgh
6.StringBuilder vs StringBuffer
(1)StringBuilder定义的字符串可以修改,不会创建新的对象,都是对同一个对象操作的,StringBuilder效率高
(2)StringBuffer(旧)和StringBuilder(新)相似,也是可变的,优先StringBuilder
(3)String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:
- String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
- StringBuilder变为String: 调用toString()方法。
StringBuilder stringBuilder=new StringBuilder("abcd");
String str=stringBuilder.substring(0,2);
System.out.println("append前:"+stringBuilder);
stringBuilder.append('e');
System.out.println("append后:"+stringBuilder);
结果:
append前:abcd
append后:abcde
(4)注意:substring() 方法不修改原对象,虽然 StringBuilder 是可变的(mutable),但 substring() 方法并不会修改原对象,而是返回一个新的 String。
| 方法 | 是否修改原 StringBuilder | 返回值 |
|---|---|---|
append() ,insert() ,delete() ,reverse() |
修改自身 | StringBuilder(支持链式调用) |
substring() |
不修改,原对象保持不变 | String(新的字符串) |
7.其他操作方法
(1)trim方法:只是去左右两侧的,不会去中间的
String str=" sddaf afse ";
str=str.trim();
System.out.println(str);
运行结果:

(2)intern方法:手动把字符串添加到常量池
String s=new String("hello");
String s1=s.intern();
System.out.println(s=="hello");
System.out.println(s1=="hello");
结果:
false
true
解释:
| 步骤 | 动作 | 位置 |
|---|---|---|
| ① | 字面量 "hello" 被放入字符串常量池(如果不在的话) |
常量池 |
| ② | new String() 在堆内存中创建一个新的 String 对象 |
堆 |
| 表达式 | 结果 | 原因 |
|---|---|---|
s == "hello" |
false |
s 指向堆对象,"hello" 指向常量池 |
s1 == "hello" |
true |
两者都指向常量池的同一对象 |
而 “==” 比较的是引用地址,不是内容,内容用equals方法比较
更多推荐




所有评论(0)