当前位置:网站首页>解读String的intern()
解读String的intern()
2022-08-11 05:19:00 【qi_ming88】
intern()在面试中也是经常遇到的,本文从案例以及debug来主要讲解intern()。这一块也是困扰本人很久的一个问题,通过debug,查看字节码分析intern()的作用,希望对大家有所帮助。
首先记住intern()的作用:
如果SCP(字符串常量池)中存在与A内容一样的字符串对象C时,就返回C 的地址;
否则将A放入SCP中,返回A的地址。
案例1:
public class StringInternDemo {
public static void main(String[] args) {
String s1 = new StringBuilder("re").append("dis").toString(); // @479 堆对象
String s4 = new StringBuilder("re").append("dis").toString(); // @481 堆对象
// String s5 = "redis"; // @433,放回常量中,首次出现在常量池
// System.out.println(s1 == s5);// 一个是常量一个是堆对象
System.out.println(s1.intern()==s4.intern()); // 都想去常量池找,发现已经有了,直接返回@433
// System.out.println(s1.intern() == s5);
System.out.println(s1);
System.out.println(s1.intern());
System.out.println(s1 == s1.intern()); // s1是堆地址,s1.intern()去常量池找,发现已经有了s5,所以s1.intern()指向s5了,返回的是s5的地址。
// 假如没有s5,常量池没有,就会吧s1放进常量池,返回s1的地址
}
}
debug查看对象地址:
结果:
true
redis
redis
true
分析:
首先只有用双引号(“”)创建的的String才是常量,其他的方式都是新增一个堆对象,所以s1与s4式不同对象,看到堆地址也是不一样的;
s1.intern(),s4.intern(),按照上面intern()的作用都想去常量池找,都没有,s1.intern()先执行,所以会把s1放进常量池,地址为s1的的堆地址,当s4.intern()访问时,发现常量有了(s1放进去的)
所以直接返回s1.intern()存的地址,两个时相等的。
同样s1.intern() = s1的时候,常量池存放的地址就是s1存进去的,必然相等。
以上验证了可能看起来不太直观,那么我们继续新建一个字符串,也就是把上面注释的代码放开,直接放入常量池
String s1 = new StringBuilder("re").append("dis").toString(); // @479 堆对象
String s4 = new StringBuilder("re").append("dis").toString(); // @481 堆对象
String s5 = "redis"; // @433,放回常量中,首次出现在常量池
System.out.println(s1 == s5);// 一个是常量一个是堆对象
System.out.println(s1.intern()==s4.intern()); // 都想去常量池找,发现已经有了,直接返回@433
System.out.println(s1.intern() == s5);
System.out.println(s1);
System.out.println(s1.intern());
System.out.println(s1 == s1.intern()); // s1是堆地址,s1.intern()去常量池找,发现已经有了s5,所以s1.intern()指向s5了,返回的是s5的地址。
// 假如没有s5,常量池没有,就会吧s1放进常量池,返回s1的地址
结果:
false
true
true
redis
redis
false
debug观察;
可以发现s1.intern()的地址是常量s5的,这就相当于,String s5 = "redis";这样创建时直接把s5放入常量池中。
总结:记住上面作用的定义。
小插曲:
其实jdk本身也是自己自带了常量池字符串,可以看Version这个类:
有的jdk8还有保留Java这个字符串
深入jvm虚拟机一书中也提出了:
以下代码也可以验证,发现我们自己没有像上面”redis“新建一个字符串常量,但发现s2 == s2.intern()为false,所以在此之前常量池肯定是有的
String s2 = new StringBuilder("open").append("jdk").toString();
String s22 = new StringBuilder("open").append("jdk").toString();
System.out.println(s2.intern()==s22.intern());
System.out.println(s2);
System.out.println(s2.intern());
System.out.println(s2 == s2.intern());
------------------
注意:大家应该有发现以上比较的是利用append拼接的,接下来看一下没有用append拼接直接new String或者new StringBuilder
使用new String或者new StringBuilder都是至多创建两个对象
String s3 = new StringBuilder("redis").toString();
System.out.println(s3 == s3.intern());
结果是false
debug:
很明显两个是不同的地址
看一下字节码:
发现创建了两个对象,会吧redis这个字符串放入常量池,显示intern的时候,常量池已经有了
比较append拼接:
String s33 = new StringBuilder("red").append("is").toString();
System.out.println(s33 == s33.intern());
debug:
显然是通过地址
查看字节码:
发现创建三个对象,但是并没有字符串“redis”,所以intern的时候,会吧redis放入常量池
边栏推荐
猜你喜欢
随机推荐
Qt 字符串截取 查找字符串
在项目中使用flex布局的justify-content:space-around;遇到的问题,(数量为单数)
07-JS事件:事件类型、事件对象、事件传播、事件委托
QT QLabel控件(使用详解)
sand和mana两大元宇宙游戏,哪个更有潜力?
PHP提高并发能力有哪些方案
使用Go语言开发的低代码应用引擎
最全总结Redis数据类型使用场景
Install different versions of MinGW (g++/gcc) and the configuration of the corresponding clion editor under Win
事件绑定触发
QT Mat转HObject和HObject转Mat 图像视觉处理
内存泄露与内存溢出
bootstarp作业一:制作分页器
Chapter 5 Loops and Relational Expressions
[C language from elementary to advanced] Part 1 Initial C language (1)
ES11新增数据类型BigInt大整型
C语言学习记录--变量基本类型和内存大小
LeetCode43. String multiplication (this method can be used to multiply large numbers)
C语言动态内存分配(1)三种函数讲解
分布式日志存储架构设计方案