String类中intern方法的原理分析
一,前言
昨天简单整理了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™ 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方法的原理分析的更多相关文章
- JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap
一.intern()定义及使用 相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下: public native String inter ...
- Java String类的intern()方法
该方法的作用是把字符串加载到常量池中(jdk1.6常量池位于方法区,jdk1.7以后常量池位于堆) 在jdk1.6中,该方法把字符串的值复制到常量区,然后返回常量区里这个字符串的值: 在jdk1.7里 ...
- 2019.4.1今日一练String类中的方法
package com.pjc.objects; replaceAll()方法的理解引出正则表达式import java.util.regex.Patte ...
- JAVA中String类的intern()方法的作用
一般我们变成很少使用到 intern这个方法,今天我就来解释一下这个方法是干什么的,做什么用的 首先请大家看一个例子: public static void main(String[] args) t ...
- Java技术——你真的了解String类的intern()方法吗
0.引言 什么都先不说,先看下面这个引入的例子: String str1 = new String("SEU")+ new String("Calvin") ...
- String类的intern()方法
0.引言 什么都先不说,先看下面这个引入的例子: String str1 = new String("SEU")+ new String("Calvin"); ...
- String类的intern()方法,随常量池发生的变化
JVM的知识这里总结的很详细:https://github.com/doocs/jvm/blob/master/README.md,因此在本博客也不会再对其中的东西重复总结了. intern的作用 简 ...
- (Object String 类中的方法练习)
package com.zs.demo1; public class Demo1 { public static void main(String[] args) { fun1(); fun2(); ...
- 【Java面试题】17 如何把一个逗号分隔的字符串转换为数组? 关于String类中split方法的使用,超级详细!!!
split 方法:将一个字符串分割为子字符串,然后将结果作为字符串数组返回. stringObj.split([separator],[limit])参数:stringObj 必选项.要被分解的 ...
随机推荐
- Python基础总结之第二天从变量开始(新手可相互督促)
hhh 为年薪20万加油ヾ(◍°∇°◍)ノ゙ 一.变量:(变量的命名规则:一般使用字母开头,可以使用下划线连接,以及数字) 正确的变量命名示范: (尽量使用容易理解什么用途的词语) a1 na ...
- CSS和html如何结合起来——选择符及优先级
1.选择符 兼容性 统配选择符 * 元素选择符 body 类选择符 .class id选择符 #id 包含原则符 p strong (所有 ...
- Redis(六)--- Redis过期策略与内存淘汰机制
1.简述 关于Redis键的过期策略,首先要了解两种时间的区别,生存时间和过期时间: 生存时间:一段时长,如30秒.6000毫秒,设置键的生存时间就是设置这个键可以存在多长时间,命令有两个 expir ...
- EF 拉姆达 linq 帮助类
(这个类是很早以前在网上找的,忘记出处请原谅.) 一.基本用法 [Route("List")] public ApiResult GetList(int page, int lim ...
- Asp.Net MVC 高级特性(附带源码剖析)
1. 程序入口(MvcHandler,RouteHandler,HttpModule) 2.异步类包(静态类AsyncResultWrapper),开启整个MVC异步循环 3.Aggregate递归链 ...
- Transformations 方块转换 USACO 模拟 数组 数学 耐心
1006: 1.2.2 Transformations 方块转换 时间限制: 1 Sec 内存限制: 128 MB提交: 10 解决: 7[提交] [状态] [讨论版] [命题人:外部导入] 题目 ...
- Android--SharedPreferences数据存储方案
SharedPreferences是使用键值对的形式存储的,并且支持多种不同的数据类型,存的是String,取得值也是String. 使用SharedPreferenc ...
- Windows 下安装 Python + Django
Django是Python的一个Web开发框架,以下是介绍的是windows下的安装步骤, 作者的环境是Win10 ,Windows Server 也是一样的 以下是作者整理的步骤,也可以参考官方教程 ...
- 深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议
前言 消息队列在现今数据量超大,并发量超高的系统中是十分常用的.本文将会对现时最常用到的几款消息队列框架 ActiveMQ.RabbitMQ.Kafka 进行分析对比.详细介绍 RabbitMQ 在 ...
- 关于Oracle本地连接出现与监听有关的问题的解决方法探讨
关于Oracle本地连接出现与监听有关的问题的解决方法探讨 监听的作用: 用于应用桌面即用户与数据库服务器建立连接的媒介,客户端发送连接请求,监听识别请求并建立客户端与服务器的连接后,监听的使命并完成 ...