String
文章目录
Java 中用
java.lang.String
类代表字符串:
- Java 9 以前,底层使用
char[]
存储字符数据
从 Java 9 开始,底层使用byte[]
存储字符数据 - 所有字符串字面量(比如
"abc"
)都是 String 类的实例 - String 对象一旦创建完毕,它的字符内容不可以修改
首先来看一段代码:并思考一下这段代码的输出是否与你想的不一样。
public class Main {
public static void main(String[] args) {
String s = "555";
s += "555";
s = "666";
test(s);
System.out.println(s); // 666
}
static void test(String str) {
str += "555";
}
}
String s = "555";
、s += "555";
、s = "666"
等操作都会在堆空间开辟一块新的内存空间,因为 String 是不可变字符串,每次要修改栈空间的变量(s
、str
)的值,只能改变它的指针指向,令指针指向堆空间新的内存。
我们知道,成员变量、函数形参等都是开辟在栈空间的(因为随时可能销毁),而上面代码中的 s
与 str
实际上在栈空间不是同一块布局,test
方法修改的是 str
的指向的值,所以 s
的值不会变。
字符串常量池(String Constant Pool)
Java 中有个字符串常量池(String Constant Pool,简称 SCP)
- 从 Java 7 开始属于堆空间的一部分(以前放在方法区)
当遇到字符串字面量时,会去查看 SCP
- 如果 SCP 中存在与字面量内容一样的字符串对象 A 时,就返回 A
- 否则,创建一个新的字符串对象 D,并加入到 SCP 中,返回 D
String s1 = "mj"; // SCP中不存在字符串对象"mj"
String s2 = "mj"; // SCP中存在字符串对象"mj", 直接返回SCP中的"mj"
// 因此上面两个字符串对象是同一个对象
System.out.println(s1 == s2); // true
字符串的初始化
在 C语言里,字符数组就是字符串;在 Java 中,String
底层是由 char[]
组成的,但是他们不完全是一个东西。
利用 Java 的断点调试功能来验证上图:右半部分中的表示不是真实的内存地址,但是可以理解为标识不同则内存地址不同,相同则内存地址相同。可以看到 s1
、s2
、s3
、s4
、cs
、s5
、s6
的标识都是不同的;但是 s1
、s2
、s3
、s4
指向的value 的标识都是30,与上图展示的一致;s5
、s6
指向的value 的标识都是31。
intern 方法
A.intern
方法的作用:
- 如果 SCP 中存在与 A 内容一样的字符串对象 C 时,就返回 C
- 否则,将 A 加入到 SCP 中,返回 A
int a = 1, b = 2, c = 3;
String s1 = String.format("%d%d%d", a, b, c);
String s2 = String.format("%d%d%d", a, b, c);
// 去常量池中寻找"123", 发现没有, 就将s1的值"123"放入常量池
String s3 = s1.intern(); // s3、s1都指向了常量池中的"123"
String s4 = s2.intern(); // 返回常量池的"123"
String s5 = "123"; // s5指向常量池中的"123"
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true
System.out.println(s1 == s5); // true
字符串的常用方法(截取)
// 去除左右的空格: "123 456"
" 123 456 ".trim();
// 转为大写字母: "ABC"
"abc".toUpperCase();
// 转为小写字母: "abc"
"ABC".toLowerCase();
// 是否包含某个字符串: true
"123456".contains("34");
// 是否以某个字符串开头: true
"123456".startsWith("123");
// 是否以某个字符串结尾: true
"123456".endsWith("456");
// 将字符串分隔为数组: [1, 2, 3, 4]
"1_2_3_4".split("_");
// 比较字符串的大小: <
"abc".compareTo("adc");
// 忽略大小写比较字符串的大小: <
"abc".compareTo("ADC");
String s1 = "abc";
String s2 = new String("abc");
// 查看字符串的内容是否相等: true
s1.equals(s2);
// 忽略大小写查看字符串的内容是否相等: true
"abc".equalsIgnoreCase("ABC");
// 字符串的替换
String str = "hello123";
// 由于String是不可变字符串, 必须用变量接收
str = str.replace("123", "666"); // hello666
穿插个知识点,Eclipse 中 抽取代码块的快捷键:先选中代码块,ALT + SHIFT + M
字符串截取内存分析图:
String str = "abcdea";
// 字符串的截取区间: [2, 4)
str.substring(2, 4); // cd
str.substring(2); // cdea
str.indexOf("a"); // 0
str.lastIndexOf("a"); // 5
str.indexOf("z"); // -1