Java String的存储

String的存储

原理

① Java 将字符串常量实现为 String 对象,编译时期能够确定的字符串常量存储在常量区中:

String a = "12345";	// 编译时期在常量池创建 “123456”,运行时将引用赋值给a
String b = a;		// 运行时将a的引用赋值给b

java string 内存存放 java储存字符串_java

② 运行时才能确定的字符串常量是在堆中创建的:

// 编译时将在常量池创建两个 String 对象 "12345" 和 "67"
String a = "12345";
String a = a.concat("67");		  // 运行时在堆中创建 ”1234567“,并将引用赋值给a
String b = new String("1234567"); // 运行时在堆中创建 ”1234567“,并将引用赋值给b

java string 内存存放 java储存字符串_jvm_02

③ 编译时能够合并的字符串常量将会合并、并在常量区中创建,带变量的将无法合并,将在堆中创建:

String a = "12" + "34";	// 编译时在常量区创建 "1234",运行时将引用赋值给a
String b = "";
String c = "67" + b + "89"; // 编译时将在常量区创建 "67" 和 "89",运行时在堆中创建 "6789",并将引用赋值给 c

java string 内存存放 java储存字符串_System_03

举例

String a = "a";
String b = "b";
String c = "a";
String ab1 = a + b;
String ab2 = "ab";
String ab3 = a.concat(b);
String ab4 = a + b;
String ab5 = "a" + "b";
System.out.println(a == c);			// true
System.out.println(ab1 == ab2);		// false;
System.out.println(ab1 == ab3);		// false;
System.out.println(ab1 == ab4);		// false;
System.out.println(ab1 == ab5);		// false;
System.out.println(ab2 == ab3);		// false;
System.out.println(ab2 == ab4);		// false;
System.out.println(ab2 == ab5);		// true;
System.out.println(ab3 == ab4);		// false;
System.out.println(ab3 == ab5);		// false;
System.out.println(ab4 == ab5);		// false;

java string 内存存放 java储存字符串_jvm_04

Intern() 方法

原理

① 对某个 String 变量调用 .intern() 方法:

  • 如果常量池中不存在这个字符串,则:并返回常量池中该子串引用
  • JDK1.6:将字符串构建的对象加入常量池
  • JDK1.7+:将这个对象的引用加入常量池

② 当某个字符串需要被重复使用时,可以考虑使用 inter() ,节省大量重复字符串对象消耗的堆内存(像上一节的举例一样,ab1ab3ab4 的内容一样,但是却占用了不同的堆内存)

注意:后边的举例都以 JDK1.7+ 作为默认环境

举例

例一

(1)String str = new String("abc"); 创建了几个对象?

答:常量池有 “abc” 的话就创建了一个对象,常量池没有 “abc” 的话就创建了两个对象

(2)String str = "abc"; 创建了几个对象?

答:常量池有 “abc” 的话就创建了零个对象,常量池没有 “abc” 的话就创建了一个对象

(3)new String("abc").intern(); 创建了几个对象?

答:常量池有 “abc” 的话就创建了一个对象,常量池没有 “abc” 的话就创建了两个对象

例二

String b = new String("ab" + "cd");
String c = b.intern();
System.out.println(c == b);	// false

由于 "ab" + "cd" 可以合并,因此常量区中刚开始就有 "abcd"b.intern() 没有做什么动作;

java string 内存存放 java储存字符串_java_05

String a == "";
String b = new String("ab" + a + "cd");
String c = b.intern();
System.out.println(c == b);	// true

由于 "ab" + a + "cd" 无法合并,因此常量区中刚开始没有 "abcd" ,而是在调用了 b.intern() 之后才有,所以 c 指向的也是堆中的 "abcd"

java string 内存存放 java储存字符串_System_06

例三

String d = "";
String b = new String("ab" + "cd");
String a = "a" + d + "b";
System.out.println(a == a.intern());	// true

因为 "ab" + "cd" 可以合并,因此常量区中刚开始没有 "ab" ,是在 a.intern() 之后才有的,所以 aa.intern() 指向的是同一个对象

java string 内存存放 java储存字符串_jvm_07

String d = "";
String b = new String("ab" + d + "cd");
String a = "a" + d + "b";
System.out.println(a == a.intern());	// false

因为 "ab" + "cd" 不能合并,因此常量区中刚开始就有 "ab"a.intern() 没有做什么动作;所以 a.intern() 指的是原来常量区中的 "ab"

java string 内存存放 java储存字符串_数据结构_08

总结

没啥好总结的hhhh,有点绕,但是把上面的举的例子都理解了就问题不大