本文主要解释java的intern方法的作用和原理,同时会解释一下经常问的String面试题。

首先先说一下结论,后面会实际操作,验证一下结论。intern方法在不同的Java版本中的实现是不一样的。Java6之前是一种实现,Java6之后也就是Java7和Java8是另外一种实现。

先说一下intern方法的定义 在Java的String类中是这样定义的,是一个本地方法,其中源码由C实现

public native String intern();

再来看一下源码的注释描述:

* <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>

翻译过来的意思就是:如果常量池中已经有了此字符串,那么将常量池中该字符串的引用返回,如果没有,那么将该字符串对象添加到常量池中,并且将引用返回。

首先要明白,这里注释的该字符串是调用此方法的字符串,返回的是引用。

下面介绍一个new String的知识点,下文再讨论intern的实现的会用到。

有个经典的Java基础面试题,new String("xyz")会创建几个对象?

动手实践后,发现再new String("xyz")有可能会创建一个(不好验证,但是可以通过下文的分析得出结论),也有可能会创建两个(可验证)。

结论:如果常量池中没有 xyz,那么就会创建两个,现在堆中创建一个,然后将对象copy到常量池中,也就是第二次创建,堆中和常量池中是两个对象。

上图的版本是Java8版本。

Java6版本:

intern方法作用:确实如上述注释上所描述,如果常量池中没有字符串,则将该字符串对象加入常量池,并返回引用。

** 这里需要注意:Java6中常量池是在方法区中,而Java1.6版本hotspot采用永久带实现了方法区,永久代是和Java堆区分的,即就是常量池中没有字符串,那么将该字符串对象放入永久带的常量池中,并返回其引用。

Java7和Java8版本:

intern方法作用:和注释描述的并不同,

如果常量池有,那么返回该字符串的引用。

        如果常量池没有,那么如果是”a“.intern调用,那么就会把”a“放入常量池,并返回”a“在常量池中的引用。

                如果是new String("a").internal ,其中在 new String的时候上文已经说到过,会在堆和常量池各创建一个对象,那么这里返回的就是常量池的字符串a的引用。

                如果是new StringBuilder("a").internal,其中new StringBuilder会在堆中创建一个对象,常量池没有,这里调用intern方法后,**会将堆中字串a的引用放到常量池,注意这里始终只是创建了一个对象,

                返回的引用虽然是常量池的,但是常量池的引用是指向堆中字串a的引用的

上述粗体字描述部分也就是不同之处,也是intern方法在Java6 之后的使用变化如何体现。

再简单总结一下Java7和Java8的intern方法作用:如果常量池没有,那么会将堆中的字符串的引用放到常量池,注意是引用,然后返回该引用。为什么Java7和Java8会不一样呢,原因就是 Java7之后(部分虚拟机,Hotspot,JRockit)已经将永久代的

常量池、静态变量移出,放入了Java堆中,而永久代也在Java8中完全废弃,方法区改名为元空间。既然常量池已经在Java6之后放入了堆中,那么如果堆中已经创建过此字符串的对象了,那么就没有必要在常量池中再创建一个一毛一样的对象了,直接将其引用拷贝

返回就好了,因为都是处于同一个区域Java堆中。

下面看几个实践:来验证一下上述的结论:

1、下图 在new的时候已经创建了两个对象,第二行,只是获取的第一行创建的常量池的对象的引用,实际的对象已经创建过了。这里是两个不同的对象,返回false。

2、和上述一样,只不过这一次第一行,现在常量池创建了对象,第二行发现常量池已经有了,只在堆上创建了一次对象,但仍然是两个对象,引用不同,返回false。

3、第一行,Strignbuilder只会在堆中创建一个对象,第二行调用intern方法后,会将堆中的引用放到到常量池中。第三行发现常量池中已经有这个字符串的引用了,直接返回。因此是同一个引用,返回的都是第一次创建的堆中字串的引用

4、和上述3的不同之处在于没有调用intern方法,因此结果输出不一样。

5、new String之后使用 + 在Java中会进行编译优化,编译成字节码指令后,会将+ 优化成 先new Stringbuilder对象,然后调用append方法进行拼接。

如下图:

反编译生成字节码:

因此这里s1最终创建的时候,xyzz字符串并没有在常量池创建,只是在堆中创建了,因为就如同上面的3一样,是new Stringbuilder操作。

所以在调用intern操作后,将其堆中的引用放入常量池并返回。所以后面的结果都是true,因为至始至终都是堆中的一个对象。

6、和上述5是相反的,结果输出也不同。

总结:

在Java6之后,使用intern可以起到优化的作用,但也要看具体的情况,比如在使用加号做字符拼接的时候,如果不想在因为其他的操作在常量池中重新创建相同的对象,那么调用intern方法,在常量池中只会放入一个引用,这时候只创建了一个对象。

探究java的intern方法的更多相关文章

  1. java String 中 intern方法的概念

    1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...

  2. java 创建string对象机制 字符串缓冲池 字符串拼接机制 字符串中intern()方法

    字符串常量池:字符串常量池在方法区中 为了优化空间,为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池.如果字符串已经存在池中,就 ...

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

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

  4. java String的intern()方法

    intern()方法用于将字符串对象加入常量池中. public native String intern(); intern()方法返回的是一个常量池中的String对象(即常量池中某个String ...

  5. 理解Java字符串常量池与intern()方法

    String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel" + "lo ...

  6. Java String类中的intern()方法

    今天在看一本书的时候注意到一个String的intern()方法,平常没用过,只是见过这个方法,也没去仔细看过这个方法.所以今天看了一下.个人觉得给String类中加入这个方法可能是为了提升一点点性能 ...

  7. java基础知识回顾之---java String final类之intern方法

    public class StringObjectDemo { /** * @param args */ public static void main(String[] args) { String ...

  8. Java intern()方法

    intern()方法: public String intern() JDK源代码如下图: 返回字符串对象的规范化表示形式. 一个初始时为空的字符串池,它由类 String 私有地维护. 当调用 in ...

  9. Java String类的intern()方法

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

随机推荐

  1. wait_time参数

    1.未修改的 wait_timeout 的初始值是28800,单位为秒,等于8个小时 2.wait_timeout过大有弊端,其体现就是mysql里有大量的sleep进程无法及时释放,拖累系统性能 但 ...

  2. 关于knn算法的总结思考

    更多的关于k近邻算法的思考 KNN(K- Nearest Neighbor)法即K最邻近法,数据挖掘分类技术中最简单的方法之一 对k近邻算法的总结: 优点部分 其可以解决分类问题,同时可以天然的解决多 ...

  3. Kurento实战之三:知识点小导游

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. STM32—TIMx实现编码器四倍频

    文章目录 一.储备知识 二.TIMx的编码器模式介绍 1.计数边沿设置 2.选择极性和使能 3.使能 4.计数方向 三.代码部分 一.储备知识 通过STM32的定时器编码器接口模式对编码器进行四倍频, ...

  5. docker安装sonarqube

    目录 一.sonarqube简介 二.安装postgresql数据库 三.sonarqube安装 四.使用教程 五.参考 一.sonarqube简介 SonarQube是管理代码质量的一个开放平台,可 ...

  6. beeline: 新版连接Hive server的工具

    HiveServer2 支持一个新的命令行Shell,称为Beeline,它是基于SQLLine CLI的JDBC客户端.它是从 Hive 0.11版本引入的,是Hive新的命令行客户端工具.Hive ...

  7. servlet中servletContext的五大作用(五)

    1.    获取web的上下文路径 2.    获取全局的参数 3.    作为域对象使用 4.    请求转发 5.    读取web项目的资源文件 package day10.about_serv ...

  8. springboot系列总结(一)---初识springboot

    Spring Boot是一个简化Spring开发的框架.用来监护spring应用开发,约定大于配置,去繁就简,just run 就能创建一个独立的,产品级的应用. 一说springboot ,Java ...

  9. 使用 antd 的 form 组件来自定义提交的数据格式

    最近使用antd UI 的表单提交数据,数据里面有的是数组,有的是对象.提交的时候还要去校验参数,让人非常头疼.在我仔细看完文档之后,发现 antd 的 form 组件做的非常不错,这些需求通通不是问 ...

  10. BUUCTF-[CISCN2019 华东北赛区]Web2

    BUUCTF-[CISCN2019 华东北赛区]Web2 看题 一个论坛,内容不错:) 可以投稿,点击投稿发现要注册,那就先注册登录.随便账号密码就行. 常规操作,扫一下站点,发现有admin.php ...