从String和new String 看java常量池
常量池
静态常量池(class文件常量池)
即class文件中的常量池,类加载后被放到运行时常量池中。包含字面量,类、方法的信息。占用class文件大部分空间。
使用javap命令可查看。
运行时常量池
运行时常量池(Runtime Constant Pool),是方法区的一部分。Class文件中除了有类的版本、
字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),
用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
运行时常量池相对于class文件常量池的另外一个重要特性是具备动态性,
java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件常量池的内容才能进入方法区运行时常量池,
运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
—— 摘自《深入理解java虚拟机》 周志明
划重点:
- 存放什么: 编译期生成的东西。
- 什么时候存放:类加载后。
- 特殊情況:动态性,即系统运行时动态放入池中,例如String类的intern()方法。
字符串池
最初为空的字符串池由String类私有维护。
new String(“s”)
java doc:
初始化一个新的Sting对象,这个对象与参数相等。换句话说,创建的String是参数的一个copy。
解读:
参数”s” 会在编译期被确定将会放入常量池,在系统启动的时候放入常量池中。
当调用new String("s")
的时候,将常量池中的“s”字符串copy到堆中。
此时,内存中一共有两个“s”对象。
String.intern()
java doc:
调用此方法时,如果字符串池中已经有一个equal此String对象的字符串(由String的equals方法判定),则返回字符串池中的字符串。
否则,将此String对象的引用添加到池中,并返回对此String对象的引用。
因此,对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()== t.intern() 才为true。
换句话说,就是如果字符串池中有,就返回池中对象;如果没有,就放入池中,返回当前对象。
测试
1 |
|
输出
1 |
|
m0
- new String(“a”)是将常量池中的a复制了一份到字符串池。调用intern()方法的时候“a”已经在字符串池中了,
这个操作对s1和s2都没有影响,s1!=s2。 - s3指向堆内存,调用intern()之后这个引用被放入字符串池中。声明s4的时候也是用的此引用,s3==s4。
m1
- 由于调用intern()方法的时候“a”已经在字符串池中了,所以s1!=s2
- s是intern()返回的字符串池中的“a”,所以s==s2
- 同理调用s3.intern()的时候“bb”已经在m0的时候放入字符串池中了,所以s3!=s4
m2
- 调用s1.intern()的时候“b”已经在字符串池了,s1!=s2
- s是intern()返回的字符串池中的“b”,所以as==s2
- 同理调用s3.intern()的时候“bb”已经在m0的时候放入字符串池中了,所以s3!=s4
m3
- 都是用常量池c==d
- 大于127不使用常量池 e!=f
- 使用常量池
- equals相等
- 相等
- equals判断类型不相同
参考:
https://tech.meituan.com/in_depth_understanding_string_intern.html
https://blog.csdn.net/wangtaomtk/article/details/52267548