探究java的intern方法
本文主要解释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方法的更多相关文章
- java String 中 intern方法的概念
1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...
- java 创建string对象机制 字符串缓冲池 字符串拼接机制 字符串中intern()方法
字符串常量池:字符串常量池在方法区中 为了优化空间,为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池.如果字符串已经存在池中,就 ...
- Java技术——你真的了解String类的intern()方法吗
0.引言 什么都先不说,先看下面这个引入的例子: String str1 = new String("SEU")+ new String("Calvin") ...
- java String的intern()方法
intern()方法用于将字符串对象加入常量池中. public native String intern(); intern()方法返回的是一个常量池中的String对象(即常量池中某个String ...
- 理解Java字符串常量池与intern()方法
String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel" + "lo ...
- Java String类中的intern()方法
今天在看一本书的时候注意到一个String的intern()方法,平常没用过,只是见过这个方法,也没去仔细看过这个方法.所以今天看了一下.个人觉得给String类中加入这个方法可能是为了提升一点点性能 ...
- java基础知识回顾之---java String final类之intern方法
public class StringObjectDemo { /** * @param args */ public static void main(String[] args) { String ...
- Java intern()方法
intern()方法: public String intern() JDK源代码如下图: 返回字符串对象的规范化表示形式. 一个初始时为空的字符串池,它由类 String 私有地维护. 当调用 in ...
- Java String类的intern()方法
该方法的作用是把字符串加载到常量池中(jdk1.6常量池位于方法区,jdk1.7以后常量池位于堆) 在jdk1.6中,该方法把字符串的值复制到常量区,然后返回常量区里这个字符串的值: 在jdk1.7里 ...
随机推荐
- 【Python机器学习实战】决策树和集成学习(一)
摘要:本部分对决策树几种算法的原理及算法过程进行简要介绍,然后编写程序实现决策树算法,再根据Python自带机器学习包实现决策树算法,最后从决策树引申至集成学习相关内容. 1.决策树 决策树作为一种常 ...
- 安鸾CTF Writeup SSRF02
SSRF02 题目链接:http://www.whalwl.host:8090/ 看到题目,以为是SSRF 获取内网信息, SFTP FTP Dict gopher TFTP file ldap 协议 ...
- 轻松上手SpringBoot+SpringSecurity+JWT实RESTfulAPI权限控制实战
前言 我们知道在项目开发中,后台开发权限认证是非常重要的,springboot 中常用熟悉的权限认证框架有,shiro,还有就是springboot 全家桶的 security当然他们各有各的好处,但 ...
- redis的过期策略和淘汰策略
过期键删除策略 1.定时删除:在设置键的过期时间的同时,创建一个定时器timer,让定时器在键过期时间来临时,立即执行对键的删除操作. 2.惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查 ...
- JAVA集合类(代码手写实现,全面梳理)
参考网址:https://blog.csdn.net/weixin_41231928/article/details/103413167 目录 一.集合类关系图 二.Iterator 三.ListIt ...
- 【java虚拟机】分代垃圾回收策略的基础概念
作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6602166.html 一.为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一 ...
- jQuery中的样式(七):addClass()、removeClass()、toggleClass()、hasClass()、css()、width()、height()等
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...
- 高德地图——searchNearBy()
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script ty ...
- C#窗体间互相传值
Demo窗体图片,Form1 Demo窗体图片,Form2 公共委托 using System; namespace _DeleFrm{ public class Dele { public ...
- GUI容器之布局管理器
布局管理器 布局管理器:frame.setLayout(); 默认值为new flowLayout() 流式布局 frame.setLayout(new FlowLayout(FlowLayout.R ...