Java中String是一个非常特殊的数据类型,在学习JVM虚拟机的时候,才真正意识到为了让String类型在运行过程中速度更快、更节省内存,才提供了字符串常量池的概念。

概念

字符串常量池(String Pool)保存所欲字符串字面量(literal strings),这些字面量在编译时期就确定,不仅如此,还可以使用String的intern()方法在运行时添加到常量池中。

常量池的使用主要有两种方法: 直接使用双引号声明出来的String对象会直接存储到常量池中。 使用String提供的intern()方法时,会查询字符串常量池中是否存在当前字符,如果不存在则将当前字符串放入常量池中。

String、StringBuffer、StringBuilder
这是String的另一个知识点,这里只是提一下一笔带过。 1. 可变性 String 不可变 StringBuffer 和 StringBuilder 可变线程安全性
String 不可变,因此是线程安全的
StringBuilder 不是线程安全的
StringBuffer 是线程安全的,内部使用 synchronized 进行同步
String Pool示例(JDK1.8)
这里要特别注意,此处以JDK1.8为例,因为在JDK1.7之前常量池在Perm区,而在JDK1.7之后常量池在Java Heap区,所以所看到的运行结果会有所不同。
String s1 = "ss"; // 在常量池上创建常量s1String s2 = "ss"; // 直接放回已存在的常量System.out.println(s1 == s2); // trueString s1 = new String("ss"); // 在堆上创建对象String s2 = new String("ss"); // 在堆上创建对象System.out.println(s1 == s2); // falseString s1 = "ss1" + "ss2" // 在常量池上创建常量ss1、ss2、ss1ss2String s2 = "ss1ss2";
String s3 = "ss1" + "ss2";
System.out.println(s2 == s3); // trueString s1 = new String("ss1") + new String("ss2"); // 在堆上创建对象ss1、ss2和ss1ss2,在常量池上创建常量ss1和ss2s1.intern();
String s2 = "ss1" + "ss2";
System.out.println(s1 == s2); // trueString s1 = new String("ss") + new String("ss1"); // 常量ss不存在,所以第一步在常量池中创建了常量ssString s2 = new String("s") + new String("s"); // 创建对象ssSystem.out.println(s2 == s2.intern()); // falseSystem.out.println(s1 == s1.intern()); // trueString s1 = "ss1ss2";
String s2 = "ss1" + new String("ss2");
System.out.println(s1 == s2.intern()); // trueString s1 = "ss1ss2";
String s2 = "ss1" + new String("ss2");
System.out.println(s1 == s2.intern()); // trueSystem.out.println(s2 == s2.intern()); // false

规律:

1.new String在堆上创建字符串对象。

2.通过字面量赋值创建字符串(如:String str=”1”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。

3.常量字符串的+“”操作,编译阶段直接会合成为一个字符串。如String str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用。