深入理解字符串的底层存储方式


引言

以下讨论的,包括图示,都是基于JDK1.8以上。因为JDK1.7的常量池在方法区,而不是在Java堆中

先了解字符串常量在内存的表示方式,接着了解字符串对象在内存的表示方式。在了解两种字符串表现方式后,String.intern()就将会很容易理解。
关于Java堆栈内容可以阅读我的《深入理解Java虚拟机(二) — JVM内存管理》进行了解。



Case1:字符串常量

在这里插入图片描述

这是一张字符串常量在内存中表示的图片。由图可知,字符串常量都会被放进常量池中。‘

字符串常量创建

在常量池中查找是否有已经有存在的相同字符串

  • 若有,则将变量引用指向该字符串(eg:图中的str2)

  • 若没有,则在常量池中创建创建字符串,并将引用指向它(eg:图中的str3)


代码模拟

void Example(){
    String str1 = "abc";
    String str2 = "abc";
    String str3 = "sss";
    System.out.println(str1 == str2);
    System.out.println(str1 == str3);
}

结果: true false

因为str1与str2的引用是指向同一个地方,而str1与str3却指向不同的地址

Java中的字符串比较

通过String.equals(str),比较的是字符串中的值是否相等

通过 == ,比较的是字符变量所引用的地址是否相同

tips:这也是比较广义的说法,并非是精确的描述



Case2:字符串对象

在这里插入图片描述

这是一张字符串对象在内存中表示的图片。由图可知,字符串对象都是作为实例放在Java堆中,而不是常量池。

字符串对象创建

在Java堆中实例化一个String对象,返回变量一个相应的地址。

不同的对象,即使字符串值相同,引用也是不相同的。


代码模拟

void Example(){
    String str1 = new String(ab);
    String str2 = new String(ab);
    
    System.out.println(str1.equals(str2)); //值相同
    System.out.println(str1 == str2); //引用地址不同
}

结果: true false



String.intern()

方法目的: 该方法实现从常量池中取回字符串对象的值。

看完这个,可能有疑问:为什么字符串对象能从常量池中返回值?!

在这里插入图片描述

其实,在调用intern的时候,它进行了以下的步骤:

检查常量池中,有没有已存在的相同字符串

  • 若有,返回常量池的该地址
  • 若没有,在常量池创建一个索引指向原实例地址,并返回该地址指向的字符串(eg: str1)

代码模拟

void Example(){
    String str1 = new String(ab);
    String str2 = new String(ab);
    
    System.out.println(str1.equals(str2)); //值相同
    System.out.println(str1 == str2); //引用地址不同
    System.out.println(str1.intern() == str2.intern()); //地址相同,都是0xf0f010
}

结果 true true false

解析

  • 左边的str1.intern()在常量池创建了一个str1的索引,返回str1的字符串

  • 右边的str2.intern()在常量池中找到str1的索引,发现值相同,所以也返回了str1的字符串


Tips: 在JDK1.7以前,调用intern时,若常量池没有已存在字符串时,是创建一个原字符串的副本,而不是索引。

猜你喜欢

转载自blog.csdn.net/u011291916/article/details/83215878