📢📢📢📣📣📣
哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝
一位上进心十足的【Java ToB端大厂领域博主】!😜😜😜
喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决😜😜😜✨ 如果有对【java】感兴趣的【小可爱】,欢迎关注我
❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
————————————————如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。
我想所有 Java 程序员都曾被这个 new String 的问题困扰过,这是一道高频的 Java 面试题,但可惜的是网上众说纷纭,竟然找不到标准的答案。
有人说创建了 1 个对象,也有人说创建了 2 个对象,还有人说可能创建了 1 个或 2 个对象,但谁都没有拿出干掉对方的证据,这就让我们这帮吃瓜群众们陷入了两难之中,不知道到底该信谁得。今天我就用事实来说话,用最有力的证据来说明new String这个过程到底创建了几个对象。
以目前的情况来看,关于 new String("xxx")
创建对象个数的答案有 3 种:
- 有人说创建了 1 个对象;
- 有人说创建了 2 个对象;
- 有人说创建了 1 个或 2 个对象。
而出现多个答案的关键争议点在「字符串常量池」上,有的说 new 字符串的方式会在常量池创建一个字符串对象,有人说 new 字符串的时候并不会去字符串常量池创建对象,而是在调用 intern()
方法时,才会去字符串常量池检测并创建字符串。
那我们就先来说说这个「字符串常量池」。
字符串常量池
字符串的分配和其他的对象分配一样,需要耗费高昂的时间和空间为代价,如果需要大量频繁的创建字符串,会极大程度地影响程序的性能,因此 JVM 为了提高性能和减少内存开销引入了字符串常量池(Constant Pool Table)的概念。
字符串常量池相当于给字符串开辟一个常量池空间类似于缓存区,对于直接赋值的字符串(String s="xxx")来说,在每次创建字符串时优先使用已经存在字符串常量池的字符串,如果字符串常量池没有相关的字符串,会先在字符串常量池中创建该字符串,然后将引用地址返回变量,如下图所示
以上说法可以通过如下代码进行证明:
public class Interview {
public static void main(String[] args) {
String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2);
}
}
以上程序的执行结果为:true
,说明变量 s1 和变量 s2 指向的是同一个地址。
在这里我们顺便说一下字符串常量池的再不同 JDK 版本的变化。
常量池的内存布局
从JDK 1.7 之后把永生代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。
JDK 1.8 与 JDK 1.7 最大的区别是 JDK 1.8 将永久代取消,并设立了元空间。官方给的说明是由于永久代内存经常不够用或发生内存泄露,会爆出 java.lang.OutOfMemoryError: PermGen 的异常,所以把将永久区废弃而改用元空间了,改为了使用本地内存空间,官网解释详情:openjdk.java.net/jeps/122
答案解密
认为 new 方式创建了 1 个对象的人认为,new String 只是在堆上创建了一个对象,只有在使用 intern()
时才去常量池中查找并创建字符串。
认为 new 方式创建了 2 个对象的人认为,new String 会在堆上创建一个对象,并且在字符串常量池中也创建一个字符串。
认为 new 方式有可能创建 1 个或 2 个对象的人认为,
new String 会先去常量池中判断有没有此字符串,如果有则只在堆上创建一个字符串并且指向常量池中的字符串,
如果常量池中没有此字符串,则会创建 2 个对象,先在常量池中新建此字符串,然后把此引用返回给堆上的对象,如下图所示:
所以小编认为还是第三个答案更准确:
创建 1 个或者 2 个对象。
对于做技术的人,我们对一个问题还要学会深究,深挖其底层的原理,回到问题的那个争议点上,new String 到底会不会在常量池中创建字符呢?我们通过反编译下面这段代码就可以得出正确的结论,代码如下: