前言
在阅读文章之前,先来做一道题:下面代码会输出什么呢?
$ String a ="abd";
$ String b = "abc";
$ System.out.println(a==b);
介绍
在上一篇文章中晚饭简单讲解了JVM是如何加载class文件的,浅解JVM加载class文件 | Mrsssswan.club
内存的控制权在于JVM,JVM在执行java程序时会把它管理的内存划分为不同的数据区
静态常量池
加载class文件之后内存中会生成该类的对象,并把该对象实例放在堆中
堆中的对象是可以共享的,方法区也和堆一样,它用于存储已被JVM加载了的数据。这其中就有常量,静态变量。
运行时常量池是方法区的一部分。
在上面题目中,“abc”这个字符串是放在常量池中的,内存中只有一份,是“共享”的,所以a,b指向的是同一个,答案为true。
隐隐预约懂了一丢丢?那再做个题
下面一共创建了几个对象呢??
$ String a ="a"+"b"+"c";
赋值右边的“a”,“b”,“c”其实都是常量,编译时会进行优化,将它们的连接结果存储起来
当JVM执行到String a =“a”+“b”+"c"时,编译器会进行优化,相当于执行的是String a =“abc”,这时候就会在常量池中找,如果没有这个字符串,就会产生一个对象。
所以,答案是只创建了一个对象
常量池的动态性
前面晚饭讲的是静态常量池,而运行期间也是可以放入新的常量的,称之为运行时常量池。可以使用String类的intern()方法来实现
调用intern()方法中,如果常量池中已经包含String对象的字符串,返回该字符串,否则将String对象的字符串添加进池中,并返回其引用
$ String s1 = "abc";
$ String s2 = new String("abd");
$ s2 = s2.intern();
$ System.out.println(s1==s2);
输出为 true
一定得说清楚的一点就是,new出来的对象是在堆中分配的,所以下面这两条语句其实在不同内存区域中存放了不同的数据
$ String s1 = new String("abd"); //heap中创建一个String的实例
$ s1 = s1.intern(); //常量池中存放abc这个字符串
一张图可能更清晰的说明
补充编译时变量
在前面讲到
$ String a ="abd";
$ String b = "abc";
$ System.out.println(a==b);
会输出true,因为“abc”是常量,但如果改成这样结果还是会为true吗
$ String a ="a";
$ String b ="b";
$ String c ="c";
$ String s = a+b+c;
$ String s1 = "abc";
$ System.out.println(s==s1); //true还是false???
a,b,c三个变量,都是不可预料的,编译器是编译时不能确定它的值是多少,只会在执行时创建新的String对象存储到堆中
晚饭自己画的图嘻嘻嘻~~~
所以,答案为false
总结
1.常量池中存储了编译器编译后的数据,而运行时常量池的常量,基本来源于各个class文件中的常量池,可以理解为方法区的“共享”.
2.程序运行期间,可以手动想常量池添加常量,例如调用String的intern()方法。
3.要注意区分new出来的对象是在堆中创建的
4.编译器会对常量进行优化,但编译期间无法确定变量的值
浅谈jvm运行时常量池就到这里了啊,有什么疑问可以发晚饭邮箱!~
晚饭后来看到一篇美团技术的文章,讲解的更清楚,看完文章后更是自愧不如,对比之下文章很不严谨
大家可以看下美图的这篇文章深入解析String#intern
晚饭邮箱:1520010400@qq.com
公众号
欢迎关注,微信公众号,获取最新文章