# String Pool

JVM为了提升性能和减少内存开销，避免字符串的重复创建，其维护了一块特殊的内存空间，字符串池由String类私有的维护

在Java中有两种创建字符串对象的方式：1）采用字面值的方式赋值 2）采用new关键字新建一个字符串对象。这两种方式在性能和内存占用方面存在着差别。

方式一：采用字面值的方式赋值，例如：

![](https://images2015.cnblogs.com/blog/952935/201605/952935-20160516214058341-1275144858.png)

```
  采用字面值的方式创建一个字符串时，JVM首先会去字符串池中查找是否存在"aaa"这个对象，如果不存在，则在字符串池中创建"aaa"这个对象，然后将池中"aaa"这个对象的引用地址返回给字符串常量str，这样str会指向池中"aaa"这个字符串对象；如果存在，则不创建任何对象，直接将池中"aaa"这个对象的地址返回，赋给字符串常量。

  在本例中，执行：str == str2 ，会得到以下结果：
```

![](https://images2015.cnblogs.com/blog/952935/201605/952935-20160516215749794-1555137097.png)

```
  这是因为，创建字符串对象str2时，字符串池中已经存在"aaa"这个对象，直接把对象"aaa"的引用地址返回给str2，这样str2指向了池中"aaa"这个对象，也就是说str和str2指向了同一个对象，因此语句System.out.println\(str == str2\)输出：true。



 方式二：采用new关键字新建一个字符串对象，例如：
```

![](https://images2015.cnblogs.com/blog/952935/201605/952935-20160516221013513-255401440.png)

```
 采用new关键字新建一个字符串对象时，JVM首先在字符串池中查找有没有"aaa"这个字符串对象，如果有，则不在池中再去创建"aaa"这个对象了，直接在堆中创建一个"aaa"字符串对象，然后将堆中的这个"aaa"对象的地址返回赋给引用str3，这样，str3就指向了堆中创建的这个"aaa"字符串对象；如果没有，则首先在字符串池中创建一个"aaa"字符串对象，然后再在堆中创建一个"aaa"字符串对象，然后将堆中这个"aaa"字符串对象的地址返回赋给str3引用，这样，str3指向了堆中创建的这个"aaa"字符串对象。

 在这个例子中，执行：str3 == str4，得到以下结果：
```

![](https://images2015.cnblogs.com/blog/952935/201605/952935-20160516222313810-541667641.png)

```
 因为，采用new关键字创建对象时，每次new出来的都是一个新的对象，也即是说引用str3和str4指向的是两个不同的对象，因此语句System.out.println\(str3 == str4\)输出：false。

 字符串池的实现有一个前提条件：String对象是不可变的。因为这样可以保证多个引用可以同事指向字符串池中的同一个对象。如果字符串是可变的，那么一个引用操作改变了对象的值，对其他引用会有影响，这样显然是不合理的。
```

**字符串池的优缺点**：字符串池的优点就是避免了相同内容的字符串的创建，节省了内存，省去了创建相同字符串的时间，同时提升了性能；另一方面，字符串池的缺点就是牺牲了JVM在常量池中遍历对象所需要的时间，不过其时间成本相比而言比较低。

&#x20;**intern方法使用：**&#x4E00;个初始为空的字符串池，它由类String独自维护。当调用 intern方法时，如果池已经包含一个等于此String对象的字符串（用equals(oject)方法确定），则返回池中的字符串。否则，将此String对象添加到池中，并返回此String对象的引用。 对于任意两个字符串s和t，当且仅当s.equals(t)为true时，s.instan() == t.instan才为true。所有字面值字符串和字符串赋值常量表达式都使用 intern方法进行操作。

**GC回收：**&#x5B57;符串池中维护了共享的字符串对象，这些字符串不会被垃圾收集器回收。

**拼接**：字符串**字面量拼接操作**是在Java编译器编译期间就执行了，也就是说编译器编译时，直接把"java"、"language"和"specification"这三个字面量进行"+"操作得到一个"javalanguagespecification" 常量，并且直接将这个常量放入字符串池中，这样做实际上是一种优化，将3个字面量合成一个，避免了创建多余的字符串对象。而字符串**引用的"+"运算**是在Java运行期间执行的，即str + str2 + str3在程序执行期间才会进行计算，它会在堆内存中重新创建一个拼接后的字符串对象。

总结来说就是：字面量"+"拼接是在编译期间进行的，拼接后的字符串存放在字符串池中；而字符串引用的"+"拼接运算实在运行时进行的，新创建的字符串存放在堆中。

**总结**：字符串是常量，字符串池中的每个字符串对象只有唯一的一份，可以被多个引用所指向，避免了重复创建内容相同的字符串；通过字面值赋值创建的字符串对象存放在字符串池中，通过关键字new出来的字符串对象存放在堆中。
