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

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

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

  1. public native String intern();

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

  1. * <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. Golang语言系列-13-常用内置包

    常用内置包 net/http包 http请求和响应 http服务端 main.go文件 package main import ( "fmt" "io/ioutil&qu ...

  2. 【笔记】主成分分析法PCA的原理及计算

    主成分分析法PCA的原理及计算 主成分分析法 主成分分析法(Principal Component Analysis),简称PCA,其是一种统计方法,是数据降维,简化数据集的一种常用的方法 它本身是一 ...

  3. 【原创】利用“进程注入”实现无文件不死webshell

    引子 上周末,一个好兄弟找我说一个很重要的目标shell丢了,这个shell之前是通过一个S2代码执行的漏洞拿到的,现在漏洞还在,不过web目录全部不可写,问我有没有办法搞个webshell继续做内网 ...

  4. [ES6深度解析]13:let const

    当Brendan Eich在1995年设计了JavaScript的第一个版本时,他犯了很多错误,包括从那时起就成为该语言一部分的一些错误,比如Date对象和当你不小心将它们相乘时对象会自动转换为NaN ...

  5. final、finally与finalize的区别?

    一.final.finally与finalize的区别 final:final是一个修饰符,可以修饰类,方法和变量.final修饰类表示类不能被其它类继承,并且该类中的所有方法都会隐式的被final修 ...

  6. 老鼠走迷宫II

    转自:http://blog.csdn.net/holymaple/article/details/8636234 由于迷宫的设计,老鼠走迷宫的入口至出口路径可能不止一条,如何求出所有的路径呢? 解法 ...

  7. NVidia Jetson Ubuntu 18.04 安装ROS过程中运行sudo rosdep init指令出错

    参考:https://www.cnblogs.com/xuhaoforwards/p/9399744.html 安装ROS过程中运行sudo rosdep init后,出现如下错误LOG: ERROR ...

  8. Jetpack Compose之隐藏Scaffold的BottomNavigation

    做主页导航时会用到底部导航栏,Jetpack Compose提供了基础槽位的布局Scaffold,使用Scaffold可以构建底部导航栏,例如: @Composable fun Greeting(vm ...

  9. Hadoop day1

    Hadoop就是存储海量数据和分析海量数据的工具 1.概念 Hadoop是由java语言编写的,在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架,其核心部件是HDFS与MapReduce ...

  10. 遇到Web页面禁用鼠标右键操作时,该如何解禁?

    在使用Selenium做Web UI自动化测试过程中,经常需要鼠标右击Web页面检查DOM节点,用于获取Web元素的定位信息.一般情况下,绝大多数页面都是能够响应鼠标右击操作的.但出于某些目的,有些W ...