关于String的赋值创建和new String的过程的解读

您所在的位置:网站首页 string类型赋值过程 关于String的赋值创建和new String的过程的解读

关于String的赋值创建和new String的过程的解读

2023-12-28 14:55| 来源: 网络整理| 查看: 265

关于String的创建过程和intern()解释 1.字面值和new创建

​ String:不可改变的Unicode字符序列,它的创建是一种池化思想,把需要共享的数据放在池中,用一个存储区域来存放一些公用资源以减少存储空间的开销。String类的数据都会存放在字符串常量池中,JDK1.7后该常量池被移到了堆中!

String s1 = "HelloWorld"; String s2=new String("HelloWorld"); String s3 = "Hello"; String s4 = "World"; String s5 = "Hello" + "World"; String s6 = s3 + s4; System.out.println(s1 == s5); System.out.println(s1 == s2); System.out.println(s1 == s6); System.out.println(s2 == s6); 结果: true false false false

​ 首先解释一下字面值创建的过程:当运行到String s1 ="helloworld"时。s1是存放在栈中的,先查看字符串常量池中是否有"helloworld"这个字符串,如果有就将这个字符串的地址赋给s1,如果没有就在常量池中创建然后在赋给s1。

​ 对于s2:首先在堆中创建一个helloworld字符串对象,同时会在常量池中创建字符串对象,但是发现已经有了,就不会再去创建(到这个创建过程结束,不会再有其他操作)。此时的s2指向的是堆空间中的字符串对象。所以s1!=s2。

​ 对于s5:这个会创建3个字符串对象,hello,world,和helloworld,但是常量池中已经有helloworld的所以并不会创建,s5指向的就是字符串常量池中的对象。

​ 对于s6:这里其实就相当于 s6 = new String(s3 + s4); s6 是存放于堆中的,不是字面量。所以 s1 不等于 s6 。

很显然s2肯定是不等于s6的,每次new都会创建一个新对象!

2.intern()

先看API文档的解释:

public String intern() 返回字符串对象的规范化表示形式。一个初始为空的字符串池,它由类 String 私有地维护。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。 它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。

​ 文档中说的很详细:当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。

​ JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。

​ 直接通过几个例子来说明:

String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("2"); s3.intern(); String s4 = "12"; System.out.println(s3 == s4); 结果: false true

​ 解释:对于**第一段不仅在堆中创建了字符串对象,而且也在字符串常量池中也创建了字符串对象。**接着s调用了intern(),此时背后隐含的操作是:先去字符串常量池中寻找是否有equals相等的String对象,发现已经存在,就返回这个对象的引用地址,但是这里并没有变量来接受 ,所以此操作没有带来任何影响。在栈中的引用s指向的是堆中的字符串对象,栈中的s2指向的是字符串常量池中的对象,故不相等,如果是sysout(s.intern==s2)结果为true。

​ 对于这里有些博主认为new String(“1”),只会在堆上创建一个字符串对象,但是这样写无法解释上面的例子为false。s.intern() 发现常量池中没有字符串对象,那么就会将堆上的引用复制到常量池中,这样s2引用指向的就是堆中对象的地址,结果应为true。故new String()在堆和常量池中均会创建对象。

​ 对于第二段:new String(“1”) + new String(“1”)注意:这里会在堆中创建3个字符串对象"1"对象和"2"对象还有"12"对象,在字符串常量池中只有2个对象"1"对象和"2"对象,因为我没有明确的new String(“12”),所以字符串常量池中并不会存在"12"这个对象。

​ 那么接下来就好解释了,调用intern()时,发现字符串常量池中并不存在"12"对象而堆中有,就将堆中对象的引用存到字符串常量池中(注意这个是JDK1.7开始改变的),s4存的实际就是堆内存中的字符串对象地址,因此s3==s4为true。

3.有关运行时常量池和字符串常量池JDK1.7之前和之后的变化

​ 在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代

​ 在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代

​ 在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)

参考:intern():https://blog.csdn.net/soonfly/article/details/70147205

​ 字符串常量池:https://blog.csdn.net/q5706503/article/details/84640762



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3