一,前言

​ 昨天简单整理了JVM内存分配和String类常用方法,遇到了String中的intern()方法。本来想一并总结起来,但是intern方法还涉及到JDK版本的问题,内容也相对较多,所以今天就弥补昨天缺失的知识点。

二,String.intern()

​ 先来看下网上流行的关于intern()方法的示例代码:

public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2); String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
}

打印结果是:

​ JDK6:false,false

​ JDK7:false,true

首先我们先来回顾一个概念:

  • jdk6及以前中常量池是存放在方法区中。
  • jdk7常量池移动到堆中

​ 为什么要移动?常量池主要是存储加载类的信息,编译器生成的字面量和符号引用等。它的大小大约只有4M,如果大量使用intern()方法,便会造成常量池内除溢出的情况,同时GC回收也有一定的困难度。而且在jdk8以后,连Perm区域都没有了,被替换成了元区域。

​ 接着我们再来说说上面两种情况不同的结果。

JDK6:

String s = new String("1");

​ 1,这句代码实际上是生成两个实例对象,先在常量池中声明为1的字符对象,再通过关键字new生成1的字符对象并被s引用。

​ 2,接着 s.intern();先在常量池中查找是否有相应的字符串存在,如果有,直接返回引用,否则,在常量池中生成相应的字符串并返回引用

​ 3,而String s2 = "1";是属于符号引用,直接在常量池中生成。

​ 4,最后判断s 是否与s2相等,通过上面的分析,s指向的是通过关键字new出来的在堆中的字符对象,而s2是符号引用的在常量池中的字符对象,所以返回的结果为false。如下图所示:

String s3 = new String("1") + new String("1");同理这句代码实际上也是产生2个字符对象,其原理和上面分析是一样的,而最后判断s3与s4的值,结果还是一个堆中的s3与常量池中的s4比较,那返回的结果必然是false。

JDK7:

	String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s == s2);

​ 1,这一部分代码与jdk6中的分析是类似的,但唯一不同的是s1.intern();返回的是在常量池中的引用,并不会在常量池中复制一份再返回引用

String s2 = "1";s1.intern();指向的是同一个引用。

​ 2,最后比较s与s2的值,s是堆中,s2是常量池中,因而返回结果为false。

​ 3,接着我们再来看下面一段代码:

		String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

​ 我们先来分析第一句代码,同样是生成两个字符对象,先在常量池中生成为1的字符对象,再在堆中生成s3为11的字符对象。接着s3.intern();执行便去常量池中查询是否存在为11的字符对象,但是发现没有。这个时候在jdk7中便不会再复制一份为11到常量池中,而是直接返回堆中11的字符对象的引用。所以它与s3的引用地址是相同的。

String s4 = "11";这句代码是符号引用,直接去常量池中创建,但是发现池中已经存在为11的字符对象的引用,因此直接返回该引用。最后判断s3与s4是否相等,因为它们之间的引用的相同的,所以返回的结果为true。如下图所示:

三,补充内容

​ 本以为写到这里就结束了,没想到一位评论者告诉我在jdk8中,对于intern方法的处理与jdk7是不一样的。在这里非常感谢,是我的疏忽。

​ 下面请看jdk8中关于intern方法的源码解释:

/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java&trade; Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/

​ 大致解释一下就是说,当调用intern()方法时,如果池已经包含string这个对象时,由equals(Object)方法去比较,则池中的字符串为返回。否则,将此String对象添加到池和对这个String对象的引用被返回。

四,总结

​ 1,对于常量池的概念要注意jdk6与jdk7之间的变化。

​ 2,intern()方法在不同jdk版本之间的变化,jdk7中如果常量池中不存在,不会再复制一份到常量池中,而是返回堆中的存在的引用地址。

​ 3,jdk6常量池在方法区中,jdk7常量池在堆中,jdk8取消方法区,替换成元区域。

​ 最后关于String类中intern方法就介绍这里,其实从实际开发工作中,我们只需要了解jdk7及以后版本的原理就可以了,毕竟现在市面上很少再用jdk6版本。但是这里总结出来也是为了更方便的参照理解,同时也涉及到一些关于JVM内存的相关知识,本篇博客是接着上一篇内容的缺失的知识点。

​ 以上内容均是自主学习总结的,如有不适之处还请留言(邮箱)指教。

感谢阅读!

String类中intern方法的原理分析的更多相关文章

  1. JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap

    一.intern()定义及使用 相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下: public native String inter ...

  2. Java String类的intern()方法

    该方法的作用是把字符串加载到常量池中(jdk1.6常量池位于方法区,jdk1.7以后常量池位于堆) 在jdk1.6中,该方法把字符串的值复制到常量区,然后返回常量区里这个字符串的值: 在jdk1.7里 ...

  3. 2019.4.1今日一练String类中的方法

    package com.pjc.objects;                        replaceAll()方法的理解引出正则表达式import java.util.regex.Patte ...

  4. JAVA中String类的intern()方法的作用

    一般我们变成很少使用到 intern这个方法,今天我就来解释一下这个方法是干什么的,做什么用的 首先请大家看一个例子: public static void main(String[] args) t ...

  5. Java技术——你真的了解String类的intern()方法吗

    0.引言 什么都先不说,先看下面这个引入的例子:   String str1 = new String("SEU")+ new String("Calvin") ...

  6. String类的intern()方法

    0.引言 什么都先不说,先看下面这个引入的例子: String str1 = new String("SEU")+ new String("Calvin"); ...

  7. String类的intern()方法,随常量池发生的变化

    JVM的知识这里总结的很详细:https://github.com/doocs/jvm/blob/master/README.md,因此在本博客也不会再对其中的东西重复总结了. intern的作用 简 ...

  8. (Object String 类中的方法练习)

    package com.zs.demo1; public class Demo1 { public static void main(String[] args) { fun1(); fun2(); ...

  9. 【Java面试题】17 如何把一个逗号分隔的字符串转换为数组? 关于String类中split方法的使用,超级详细!!!

    split 方法:将一个字符串分割为子字符串,然后将结果作为字符串数组返回. stringObj.split([separator],[limit])参数:stringObj   必选项.要被分解的 ...

随机推荐

  1. JAVA-注解(2)-自定义注解及反射注解

    自定义注解开发 1.开发一个注解类 开发一个注解类的过程,非常类似于开发一个接口,只不过需要通过@interface关键字来声明 2.使用元注解修饰注解的声明 所谓的原注解是用来修饰注解声明的注释,可 ...

  2. Spring Boot 面试的十个问题

    用下面这些常见的面试问题为下一次 Spring Boot 面试做准备. 在本文中,我们将讨论 Spring boot 中最常见的10个面试问题.现在,在就业市场上,这些问题有点棘手,而且趋势日益严重. ...

  3. MacBook Air多出一块磁盘?

    今天将MAC的系统升级到Mojave,启动之后发现系统挂载的磁盘变了,我记得升级之前文件系统是挂载在/dev/disk0上的,但是升级之后,文件系统挂载在/dev/disk1上了. 用diskutil ...

  4. flink入门实战总结

    随着大数据技术在各行各业的广泛应用,要求能对海量数据进行实时处理的需求越来越多,同时数据处理的业务逻辑也越来越复杂,传统的批处理方式和早期的流式处理框架也越来越难以在延迟性.吞吐量.容错能力以及使用便 ...

  5. Metrics类型

    Metrics类型 在上一小节中我们带领读者了解了Prometheus的底层数据模型,在Prometheus的存储实现上所有的监控样本都是以time-series的形式保存在Prometheus内存的 ...

  6. 熟悉软件的生命周期AND测试工程师的工作流程

    1.软件的生命周期 *软件生命周期(SDLC)是软件开始研制到最终被废弃不用所经历的各个阶段.在不同阶段里,由不同的组织.个人和资源进行着明确的任务. 2.生命周期的模型 *常见的生命周期模型有:瀑布 ...

  7. 脱壳系列_2_IAT加密壳_详细版解法1(含脚本)

    1 查看壳程序信息 使用ExeInfoPe 分析: 发现这个壳的类型没有被识别出来,Vc 6.0倒是识别出来了,Vc 6.0的特征是 入口函数先调用GetVersion() 2 用OD找OEP 拖进O ...

  8. 整理github总结

    之前记述过两篇关于git命令的随笔,那时候对git的理解还不太深(虽然现在理解的也不是很多...菜哭了),只会用那些命令,并没有形成自己的知识,今天下载完Git之后,规整了一下自己的github,在吃 ...

  9. HTML第六章 盒子模型

    什么是盒子模型: (1)边框: (2)内边距: (3)外边距: (4)元素内容·: (5)背景色·: 边框: 属性: 颜色(border-color),粗细(border-width),样式(bord ...

  10. SpringMVC项目案例之---数据的获取与显示

    数据的获取与显示 (一)功能 1.对用户输入的数据进行获取 2.将获取的数据显示到页面 3.使用了SpringMVC技术的注解方式 4.使用了过滤器,处理中文乱码问题 5.在web.xml中设置了访问 ...