返回顶部
首页 > 资讯 > 精选 >JVM字符串常量池及String的intern方法实例分析
  • 559
分享到

JVM字符串常量池及String的intern方法实例分析

2023-06-27 11:06:39 559人浏览 安东尼
摘要

本文小编为大家详细介绍“JVM字符串常量池及String的intern方法实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“JVM字符串常量池及String的intern方法实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入

本文小编为大家详细介绍“JVM字符串常量池及String的intern方法实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“JVM字符串常量池及String的intern方法实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

面试题

先通过一个面试题形象的了解一下我们本篇文章要讲的内容的呈现形式:

String s1 = new String("he") + new String("llo");String s2 = new String("h") + new String("ello");String s3 = s1.intern();String s4 = s2.intern();System.out.println(s1 == s3);System.out.println(s1 == s4);

执行上面的代码,会发现打印的结果都是 true 。那么,为什么本来不相等的字符串,调用了intern方法之后便相等了呢?下面我们就来逐步分析这其中的底层实现。

intern方法的作用

intern()方法的功能定义:

(1)如果当前字符串内容存在于字符串常量池(即equals()方法为true,也就是内容一样),那直接返回此字符串在常量池的引用;

(2)如果当前字符串不在字符串常量池中,那么在常量池创建一个引用并指向堆中已存在的字符串,然后返回常量池中的引用。

简单说intern方法就是判断并将字符串是否存在于字符串常量池,如果不存在则创建,存在则返回。

字符串常量池

HotSpot中实现字符串常量池功能的是一个StringTable类,它是一个Hash表,默认值大小长度是1009。在每个HotSpot虚拟机的实例中只有一份,被所有的类共享。字符串常量由一个个字符组成,放在了StringTable上。

jdk6及之前版本,字符串常量池是放在Perm Gen区(方法区)中。StringTable的长度是固定的,长度是1009,当String字符串过多时会造成hash冲突,导致链表过长,性能大幅度下降。此时字符串常量池里面放的全部是字符串常量(字面值)。

由于永久代的空间有限且固定,JDK6的存储模式很容易造成OutOfMemoryError

JDK7时正在着手去永久代的工作,因此字符串常量池被放在了堆中。此时,即使堆的大小也是固定的,但对于应用调优工作,只需要调整堆大小就行了。

JDK7中字符串常量池不仅仅可以存放字符串常量,还可以存放字符串的引用。也就是说,堆中的字符串的引用可以作为常量池的值而存在。

字符串池化流程分析

在了解了上面的基础理论,我们下面以图文相结合的形式来逐步演示字符串池化的流程和分类。以下实例以JDK8版本为基础来进行分析讲解。

当我们通过双引号声明一个字符串:

String wechat = "程序新视界";

此时,双引号内的字符串会被直接存储在字符串常量池中。

关于上面的存储结构,我们已经在之前文章中提到,不再过多解释。下面如果我们再声明同样的字符串看看会有什么样的变化。

String wechat = "程序新视界";String wechat1 = "程序新视界";

上述代码中声明wechat1时,会发现常量池中已经存在了对应的字符串,则不会再重新创建,只是把对应的引用返回给wechat1

此时,如果直接用双等号比较wechatwechat1肯定是相等的,因为它们的引用和字面值都是相同的。

上面是直接双引号赋值的情况,那么如果通过 new 的形式创建字符串对应的流程又是如何呢?前面文章已经讲到这分两种情况:常量池存在对应的值和不存在对应的值。

String wechat2 = new String("程序新视界");

如果存在对应的值,此时会先在堆中创建一个针对wechat2变量的对象引用,然后将这个对象引用指向字符串常量池中已经存在的常量。

此时直接使用双等号比较wechatwechat2变量肯定是不相等的,而通过equals方法进行对比字面值则是相等的。

另外一种情况就是通过 new 创建时,字符串常量池中并不存在对应的常量。这种情况会现在字符串常量池中创建一个字符串常量,然后再在堆中创建一个字符串,持有常量池中对应字符串的引用。并把堆中对象的地址返回给wechat2。最终效果图依旧如上图。

在此时,如果不是直接new字符串赋值,而是通过+号操作,情况就有所不同。

String s1 = "程序";String wechat3 = new String(s1 + "新视界");

上述代码 s1 会存入常量池,而wechat3的值则由于JVM编译时采用了StringBuilder进行加号的拼接,只会在堆中创建一个String对象,并不会在常量池中存储对应的字符串。

此时的情况已经涉及到我们面试题中创建字符串的情况了。那么,下面我们就通过intern方法进行池化操作,看看字符串常量池的具体变化。

还以上面的代码为例,此时wechatwechat1wechat2三个变量和wechat3直接用双等号比较肯定是不相等的。下面对wechat3进行intern池化处理。

String s1 = "程序";String wechat3 = new String(s1 + "新视界");wechat3 = wechat3.intern();

此时会发现wechatwechat1两个变量与wechat3的值相等了。由于wechatwechat1其实是一个,这里只以wechatwechat3的比较为例来分析一下这个流程。

在没有调用intern方法之前内存的状态是下图(忽略掉s1部分)这样的

看上图它们的值不相等也就不奇怪了。下面对wechat3进行池化处理,并把池化的结果赋值给wechat3,就是上面的代码。内存结构会发生如下变化

此时,再判断对应的两个值,因为引用和字面值全部相同,因此便相等了。具体intern的判断规则我们上面已经知道,如果常量池中存在对应的值,则直接返回引用。

那还有另外一种情况,就是常量池中不存在对应的值会是如何处理的呢?先看如下代码:

String s2 = "关注";String wechat4 = new String(s2 + "公众号");wechat4 = wechat4.intern();

在调用intern之前的操作我们前面已经说过,会在堆中创建一个String对象,而常量池中并不会存储一份,与wechat3的图一样。

此时常量池中并未存在对应的字符串,此时调用intern方法之后

intern方法之后,常量池中存了堆中对应字符串的引用。对照上面说的,JDK7及之后字符串常量池中可以存储引用了。

需要注意的是,当字符串常量池中并不存在对应字符串时,调用intern方法返回的地址为堆中的地址,对应图中的0x99。而wechat4本来地址指向的就是堆中的地址,因此不会发生变化。

此时如果再定义一个双引号赋值的wechat5,如下代码:

String s2 = "关注";String wechat4 = new String(s2 + "公众号");wechat4 = wechat4.intern();String wechat5 = "关注公众号";System.out.println(wechat4 == wechat5);

变量wechat5初始化时发现字符串常量池中已经存在了一个引用,那么wechat5会直接指向这个引用,也就是wechat5wechat4一样,都指向内存中的String对象。

小结

上面这个演示实例时需要注意的重点是intern方法返回的引用地址。如果字符串常量池中已经存在对应的字符串时,此时返回的是字符串常量的地址【常量池中存储的是字符串】,如果字符串常量池中不存在对应的字符串,此时会把堆中的引用放在常量池对应的位置【常量池中存储的是堆中字符串的引用】,此时intern返回的是堆中字符串对应的引用。

搞清楚了上面的返回逻辑再看最初的代码:

String s1 = new String("he") + new String("llo");String s2 = new String("h") + new String("ello");String s3 = s1.intern();String s4 = s2.intern();System.out.println(s1 == s3);System.out.println(s1 == s4);

其中 s1 为堆中字符串“hello”的地址;s2 为堆中另外一个“hello”字符串的地址。当s1.intern(),常量池中存储了 s1 的地址,此时s1.intern()返回的也是 s1 的地址,因此s1=s3,都是同一个地址嘛。

然后执行s2.intern(),此时常量池中已经有 hello 字符串,类型为引用且指向 s1 的地址,执行之后返回的便是 s1 的地址,赋值给 s4 ,因此 s1 和 s4 也指向同一个地址,因此相等。

通过上面的更深层次的分析,想必大家对字符串常量、字符串常量池以及intern方法有了更加深刻的理解。相关的面试题如果按照这个思路分析,基本上都可以进行准确解答了。

读到这里,这篇“JVM字符串常量池及String的intern方法实例分析”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

--结束END--

本文标题: JVM字符串常量池及String的intern方法实例分析

本文链接: https://lsjlt.com/news/309811.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • JVM字符串常量池及String的intern方法实例分析
    本文小编为大家详细介绍“JVM字符串常量池及String的intern方法实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“JVM字符串常量池及String的intern方法实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入...
    99+
    2023-06-27
  • 如何解析Java常量池与字符串intern
    这期内容当中小编将会给大家带来有关如何解析Java常量池与字符串intern,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于cl...
    99+
    2023-06-17
  • JVM常量池的示例分析
    小编给大家分享一下JVM常量池的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、Class常量池与运行时常量池Class常量池可以理解为是Class文件...
    99+
    2023-06-14
  • Java String类的理解及字符串常量池介绍
    目录一. String类简介1. 介绍2. 字符串构造二. 字符串常量池(StringTable)1. 思考?2. 介绍和分析3. intern方法三. 面试题:String类中两种...
    99+
    2024-04-02
  • Python字符串的常用方法实例分析
    这篇文章主要介绍“Python字符串的常用方法实例分析”,在日常操作中,相信很多人在Python字符串的常用方法实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python字符串的常用方法实例分析”的疑...
    99+
    2023-06-29
  • JavaScript字符串string实例分析
    这篇“JavaScript字符串string实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看...
    99+
    2024-04-02
  • java中String源码和String常量池的示例分析
    小编给大家分享一下java中String源码和String常量池的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!String 常量池分析常用方法equal...
    99+
    2023-05-30
    java string
  • Java中string类型的xml字符串实例分析
    这篇“Java中string类型的xml字符串实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中string类...
    99+
    2023-06-29
  • C#字符串String及字符Char的相关方法
    一、字符串: 1、访问String中的字符: string本身可看作一个Char数组。 string s = "hello world"; for (int i = 0; i &l...
    99+
    2024-04-02
  • Java中String的split切割字符串方法实例及扩展
    目录一、public String[] split(String regex)二、public String[] split(String regex, int limit)三、扩展...
    99+
    2024-04-02
  • C#字符串String及字符Char的方法怎么用
    这篇文章主要介绍了C#字符串String及字符Char的方法怎么用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#字符串String及字符Char的方法怎么用文章都会有所收获,下面我们一起来看看吧。一、字符串...
    99+
    2023-06-30
  • Java字符串逆序方法的示例分析
    小编给大家分享一下Java字符串逆序方法的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.简述记录下实现字符串逆序的两种方法:第一种方法比较暴力,通过字...
    99+
    2023-06-29
  • python字符串乘法的示例分析
    这篇文章将为大家详细讲解有关python字符串乘法的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。字符串乘法在Python中,不仅可以乘以数字,还可以乘以字符串。例如:关于“python字符串乘法...
    99+
    2023-06-27
  • String字符串分割的3种方法 Java
    方法1:split(string) 方法 使用了 split(string) 方法通过指定分隔符将字符串分割为数组 public class Test { public static void ...
    99+
    2023-09-08
    java jvm 开发语言
  • Java中实现String字符串分割的3种方法
    目录Java中实现String字符串分割的3种方法方法1:split(string) 方法方法2:substring方法方法3:StringTokenizer方法扩展:JAVA 截取...
    99+
    2023-05-20
    java string字符串分割 java 字符串分割
  • Pythonre.split方法分割字符串的实现示例
    目录re.split方法问题解决方案讨论re.split方法 注:使用前需要引入包(import re) 功能:split能够按照所能匹配的字串将字符串进行切分,返回切分后的字符串列...
    99+
    2024-04-02
  • java切分字符串的2种方法实例
    目录方法一:通过substring()截取字符串方法二: 通过split()切割字符串,返回结果为字符串数组补充:根据某个字符截取字符串总结java中包含两种切分字符串方式...
    99+
    2024-04-02
  • Python字符串特性及常用字符串方法的简单笔记
    单引号和双引号都能表示字符串。区别在于转义的时候。 如果懒得加转义字符,可以通过在字符串前面加上r。例如: print r'C:somename' 通过在字符串里面添加反斜杠来不换行。 prin...
    99+
    2022-06-04
    字符串 性及 常用
  • python字符串切片及常用方法示例详解
    目录一、切片二、常用方法2.1 查找2.2 修改2.3 判断一、切片 切片:指对操作的对象截取其中一部分的操作,字符串、列表、元组都支持切片操作 语法:序列[开始位置下标:结束位置下...
    99+
    2023-05-15
    python字符串切片 python切片
  • Dart String字符串的常用方法总结概述
    目录Flutter中字符串的一些常用方法1 length :返回字符串的长度。2 isEmpty :判断字符串是否为空。3 toUpperCase() :将字符串中所有的小写字母转换...
    99+
    2023-05-18
    Dart String字符串方法总结 Dart String
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作