字符串常量池

池这个词在java里并不罕见,比如运行时常量池,字符串常量池,线程池,数据库连接池等,所谓“池”就是为了资源复用,减少空间的占用,提高性能。

1、什么是字符串常量池?

JVM为了提高性能,减少内存开销,维护的一个存放字符串常量的内存区域,里面的字符串不允许重复,有长度限制,最大为65535字节(有兴趣的可以参考我的这篇博客:String的长度限制)。

2、字符串常量池在哪?

在JDK1.7以前,字符串常量池在方法区中,JDK1.7及以后的版本字符串常量池在堆中。
如下图所示(简单示意,就不展开了):

3、什么样的字符串会放入池中?

我们平时使用String比较多的方式就是字面量赋值,String s = "abc",还有StringBuilder/StringBuffer,还有在类里定义的字符串变量,类里定义的字符串变量属于对象的一部分,不会存放在字符串常量池中。下面就分别讲一下这几种方式
为了对比,把这种不常用的方式String s = new String("abc")也讲一下吧。

3.1、字面量赋值String s = "xxx"方式

String s1 = "长得帅弹玻璃球都帅";
String s2 = "长得帅弹玻璃球都帅";
System.out.println(s1 == s2);

结果:

s1和s2其实是指向了同一块内存地址,并且该内存地址在字符串常量池中,可见字面量赋值会直接放入字符串常量池中。
内存分布:

3.2、String s = new String("xxx")方式

这种方式不常用,但为了和String s = "xxx"对比还是讲下

String s1 = "长得帅弹玻璃球都帅";
String s2 = "长得帅弹玻璃球都帅";
String s3 = new String("长得帅弹玻璃球都帅");
String s4 = new String("长得好看套麻布袋都好看");
String s5 = new String("长得好看套麻布袋都好看");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s4 == s5);

结果:

可见s3并没有在字符串常量池中,new出来的是对象,分配在堆上(注意并不是所有的对象都分配在堆上,这里我们不做讨论)
s4与s5是堆上的两个不同对象,自然不等,但他们堆上的对象都指向常量池里的"长得好看套麻布袋都好看"
对于new String("xxx")这种,如果常量池中没有xxx字符串,则会在常量池中建一个xxx字符串,比如这里的s3,s4
如果常量池中有了xxx字符串,则其堆上的对象会指向常量池里的xxx字符串,比如这里的s5
内存分布:

我们用javap命令看下常量池:

如果面试官问你,String s = new String("xxx")创建了几个对象,答曰:两个,字符串常量池里一个,堆上一个(如果结合xxx是否已经在常量池里存在分析,把内存分布图画出来,offer+1,哈哈)

3.3、StringBuilder/StringBuffer方式

StringBuilder/StringBuffer最后会调用toString()方法,而他们的toString()方法都是调的new String方法,所以和2.2一样
StringBuffer的toString()方法:

public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

StringBuilder的toString()方法:

public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

4、intern方法

当一个String对象调用intern()方法时,如果常量池中已经有同样的字符串了,则返回该对象的引用(在堆中就返回堆中的引用,在池中就返回池中的引用),如果没有,则将该对象添加到池中,并返回池中的引用。
我在JDK1.8-java.lang.String类源码阅读有写过intern方法

5、小实验

String s1 = "hello"+"world";
String s2 = new String("abc");
String s3 = s2 +"def";

内存分布:

我们用javap命令看下常量池:

posted @ 2020-06-28 15:42  liu_whut  阅读(2178)  评论(0编辑  收藏  举报