在讲intern方法前,我们先简单回顾下Java中常量池的分类。

常量池的分类

Java中常量池可以分为Class常量池、运行时常量池和字符串常量池

1. Class文件常量池

在Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。

所谓字面量类似与我们平常说的常量,主要包括以下两种

  • 文本字符串,例如String a = "aa"。其中"aa"就是字面量。
  • 被final修饰的变量。

符号引用包括以下形式:

  • 类和接口和全限定名:例如对于String这个类,它的全限定名就是java/lang/String。
  • 字段的名称和描述符:所谓字段就是类或者接口中声明的变量,包括类级别变量和实例级的变量。
  • 方法的名称和描述符:所谓描述符就相当于方法的参数类型+返回值类型。

2. 运行时常量池

我们知道类加载器会加载对应的Class文件,上面介绍的Class文件常量池中的数据,会在类加载后进入方法区中的运行时常量池。运行时常量池是全局共享的,多个类共用一个运行时常量池。运行时常量池存在于方法区中。

3. 字符串常量池

看名字我们就可以知道字符串常量池是用来存放字符串的,也就是说Class文件常量池中的文本字符串会在类加载时进入字符串常量池。

字符串常量池运行时常量池是什么关系呢?上面我们说Class文件常量池中的字面量会在类加载后进入运行时常量池,其中字面量中也包括文本字符串,从这段文字我们可以知道字符串常量池存在于运行时常量池中,也就存在于方法区中。

但是到了JDK1.7时,字符串常量池被移出了方法区,转移到了堆里了。另外需要我们重点注意的是:字符串常量池中存放的并不是字符串本身,而是字符串对象的引用。

程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。

String 的 intern 方法

String 方法的作用是:判断字符串常量池中是否存在一个引用,这个引用指向的字符串对象和当前对象相等(使用 equals 方法判断相等),如果存在直接返回这个引用,如果不存在则创建一个字符串对象并将其引用存入字符串常量池。

下面举个列子帮助加深理解。

//代码基于JDK 8

//s1指向字符串常量池中的"自由之路"
String s1 = "自由之路";
//s2也指向字符串常量池中的"自由之路"
String s2 = "自由之路";
//s3指向堆中的某个对象
String s3 = new String("自由之路");
//因为字符串常量池中已经存在"自由之路"的引用,直接返回这个引用
String s4 = s3.intern(); //创建一个字符串对象
String s5 = new String("ddd");
//常量池中不存在指向"ddd"的引用,创建一个"ddd"对象,并将其引用存入常量池
String s6 = s5.intern();
//创建一个字符串对象
String s7 = new String("ddd");
//常量池中存在指向"ddd"的引用,直接返回
String s8 = s7.intern(); System.out.println("s1==s2:"+(s1==s2));
System.out.println("s1==s3:"+(s1==s3));
System.out.println("s1==s4:"+(s1==s4)); System.out.println("s5==s6:"+(s5==s6));
System.out.println("s6==s8:"+(s6==s8));
System.out.println("s7==s8:"+(s7==s8));

返回的结果如下:

s1==s2:true
s1==s2:false
s1==s2:true
s5==s6:false
s6==s8:true
s7==s8:false

intern 方法使用场景

我们来看下面这个方法。

public class Person{
String name;
public void setName(String name)
{
this.name = name
}
}

假如现在的Person对象都叫小明,那么这些Person对象都会引用一个不同的字符串对象。

如果我们改进下这个方法:

public class Person{
String name;
public void setName(String name)
{
this.name = name.intern();
}
}

那么对象的引用结构如下图所示:

这样明显可以节省多个字符串对象的空间。我写了一个测试程序:

public class JavaTest {

    public static void main(String[] args) throws Exception {
//一个很大的字符串
String s = "c...c";
List<Person> personList = new ArrayList<>(); int count = 100000;
for (int i = 0; i < count; i++) {
Person p = new Person();
p.setName(new String(s));
//防止垃圾回收
personList.add(p);
System.out.println(i);
}
System.out.println("success...");
} public static class Person{
private String name;
public void setName(String name) {
this.name = name;
}
} }

为了让程序快速将内存耗尽,我这边将内存设置成5M。

-Xms5m -Xmx5m

结果如下:

...
93889
93890
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.csx.demo.spring.boot.util.JavaTest.main(JavaTest.java:15)

创建9w多个对象时已经报OutOfMemoryError错误了。

下面调整下 Person 的 set 方法,再执行下。

public static class Person{
private String name;
public void setName(String name) {
this.name = name.intern();
}
}
99997
99998
99999
success...

顺利执行完成。

String的intern方法的使用场景的更多相关文章

  1. java String 中 intern方法的概念

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

  2. String 的intern() 方法说明

    1.说明 Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 2.源码说明 public native ...

  3. String 的 intern() 方法解析

    一.概述 JDK7 之前和之后的版本,String 的 intern() 方法在实现上存在差异,本文的说明环境是 JDK8,会在文末说明 intern() 方法的版本差异性. intern() 方法是 ...

  4. String中intern方法的作用

    前言 读完这篇文章你可以了解,String对象在虚拟机内存中的存放,intern的作用,这么多String对象的创建到底有什么区别,String 创建的对象有几个!! 正题 先科普几个知识点1.常量池 ...

  5. 关于对String中intern方法的理解

    在java的String中有个一直被我们忽视了的方法intern方法:它的官方解释是:一个初始时为空的字符串池,它由类 String 私有地维护. 当调用 intern 方法时,如果池已经包含一个等于 ...

  6. String的intern方法的用处

    今天第一次翻看Effective java,在其第一个item中讲静态工厂方法的有点的时候说到“它们每次被调用 的时候,不要非得创建一个新的对象”并在结尾处提到---"String.inte ...

  7. String的Intern方法详解

    引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常量池就类似一个JAVA系统级别提供的缓存.8种 ...

  8. String的Intern方法

    jdk6 和 jdk7 下 intern 的区别 相信很多 JAVA 程序员都做做类似 String s = new String("abc")这个语句创建了几个对象的题目. 这种 ...

  9. java String的intern()方法

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

随机推荐

  1. 重回OI的第一篇博客

    太久没学OI了, 代码都不会写了, 先写一篇BFS练练手, 是我太菜了qwq #include<cstdio> #include<queue> #include<iost ...

  2. Salesforce学习笔记之Actions and Recommendations

    设置Actions and Recommendations(Salesforce提供的标准元素),Salesforce上的文档说有两种方法,即Deployment和Process Builder(通过 ...

  3. windows 服务端 狼人杀 发牌器 开发完成 待继续开发其他服务

    开发工具: python2.7 eric4 成果链接地址 https://wws.lanzous.com/iPCDTfnuoif

  4. 使用Kali中的Metasploit生成木马控制Windows系统

    一.概念:Kali基于debin的数字取证系统,上面集成了很多渗透测试工具,其前身是BT5 R3(BtackTrack). 其中Metasploit是一个综合利用工具,极大的提高了攻击者的渗透效率,使 ...

  5. yum安装软件时,提示No package netstat available.的解决方法

    1. 序言 如笔者在本机上运行netstat时,提示没有这个命令,向来简单粗暴,直接yum -y install netstat,显然是不能正常安装的. [root@hadoop-103 ~]# yu ...

  6. 计算机网络-传输层(3)TCP协议与拥堵控制

    TCP是因特网传输层的面向连接的可靠的运输协议.   TCP被称为是面向连接的:通信双方在发送数据之前必须建立连接,连接状态只在连接的两端中维护,在沿途节点中并不维护状态. TCP连接包括:两台主机上 ...

  7. 第二篇Scrum冲刺博客--Interesting-Corps

    第二篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 主页页面和探测空间设计及布局实现 主页页面跳转社区功能及社区设计及布局实现 叶学涛 设计页面 ...

  8. 教育行业CRM项目开发

    项目开发流程 需求分析 存储所有的客户咨询信息    避免重复数据    客户多次跟踪记录    客户来源分析.成单率分析    每个销售只能修改自己的客户信息    报名流程开发        班级 ...

  9. 怎么下载chrome的扩展程序

    很多时候我们是没办法访问谷歌扩展应用程序 chrome应用商店的,这时候我们最好能把对应扩展应用程序下载保存,以便提供给其他人员使用. 搜索得到知乎有很全的方法: 如何导出并打包第三方chrome扩展 ...

  10. 轻松应对并发,Newbe.Claptrap 框架入门,第四步 —— 利用 Minion,商品下单

    接上一篇 Newbe.Claptrap 框架入门,第三步 —— 定义 Claptrap,管理商品库存 ,我们继续要了解一下如何使用 Newbe.Claptrap 框架开发业务.通过本篇阅读,您便可以开 ...