String中intern方法的使用场景详解

在讲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方法的使用场景的文章就介绍到这了,更多相关String intern方法使用场景内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 通过String.intern()方法浅谈堆中常量池

    简介 String是我们最常用的一个类,和普通java类一样其对象会存在java堆中.但是String类有其特殊之处,可以通过new方法生成,也可以通过带引号的字符串常量直接赋值.在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍. String.intern()方法 简单的说,String.intern()方法的作用就是返回常量池中字符串对象,在对该方法进行详解之前,我们看几个创建字符串对象的例子.

  • java String的intern方法

    首先我们应该清楚的是JDK1.6和JDK1.7中String类的intern方法还是有差别的: JDK1.6中的intern:   调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用:如果不存在的话,则会将原先堆中的该字符串拷贝一份到常量池中. JDK1.7中的intern:   调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个Stri

  • String中intern方法的使用场景详解

    在讲intern方法前,我们先简单回顾下Java中常量池的分类. 常量池的分类# Java中常量池可以分为Class常量池.运行时常量池和字符串常量池. 1. Class文件常量池 在Class文件中除了有类的版本.字段.方法.接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用. 所谓字面量类似与我们平常说的常量,主要包括以下两种 文本字符串,例如String a = "aa".其中"aa"就是

  • 详解Java String中intern方法的原理与使用

    目录 简介 常量池简介 intern方法简介(JDK7) 原理(JDK6与JDK7) 例程测试 例程分析 jdk1.6 jdk1.7 应用实例 简介 本文介绍Java的String的intern方法的原理. 常量池简介 在 JAVA 语言中有8种基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池(在方法区)的概念.常量池就类似一个JAVA系统级别提供的缓存.8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊. Str

  • AndroidStudio中重载方法@Override的使用详解

    在我们使用Android Studio时,创建一个类是继承于Android中已有的类时,我们会对原有类中的方法进行重载,Android Studio为我们提供了快捷的插入重载的方法,便于开发.减轻了工作量. 当我们继承一个Android中已有的类时,需要对其函数进行重载只需要点击菜单栏 Code->Override Methods,会跳出可以重载的函数,寻找需要的函数即可. 其中重载函数前都会有@Override标志. public MyService extends Service{ @Ove

  • JAVA中static方法的用法实例详解

    本文实例讲述了JAVA中static方法的用法.分享给大家供大家参考,具体如下: static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何对象.也就是说,它不依赖类特定的实例,被类的所有实例共享.只要这个类被加载,Java虚拟机就能根据类名在运行时数据区或者方法区内找到他们.因此,static对象可以在它的任何对象创建之前访

  • JS面试中你不知道的call apply bind方法及使用场景详解

    目录 面试 三者区别 call apply. bind 总结 应用场景 简单实现 call apply bind 面试 面试官 :说一下 call apply bind 的三者区别吧? 我:啊.....这. 三者区别 call 我们先来看看 call call 函数接收两个参数 @params1 需要指向this @params2 需要携带的参数 就拿这段代码 来说 我调用 foo 函数 的时候 想去 执行obj 中的 eat函数 怎么办? 默认 我的 foo 里面什么页面有 怎么可能会打印 吃

  • jQuery中on方法使用注意事项详解

    on(eventType,[childSelector],[data],fn) 采用事件委托机制绑定事件,好处是子元素动态加入时无需再次绑定. on方法可以传入childSelector指定添加事件处理函数的具体子元素,还可以传入对象格式的数据data,fn函数内部通过event.data.XXX获取具体参数.传入事件处理函数的data需要在事件绑定时就确定好,不同于trigger('eventType',[data]),tirgger方法中传入的data(对象或数组形式)是事件发生时才计算的值

  • Python装饰器实现方法及应用场景详解

    应用场景: 1.授权(Authorization) 装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint).它们被大量使用于Flask和Django web框架中.这里是一个例子来使用基于装饰器的授权: from functools import wraps # 最新版python引用是 import functools def requires_auth(f): # f 就是我们需要装饰的函数,一看就是不带参数的装饰器 @wraps(f) # 新版python写法 @

  • Java String的intern方法使用场景示例

    在讲intern方法前,我们先简单回顾下Java中常量池的分类. 常量池的分类 Java中常量池可以分为Class常量池.运行时常量池和字符串常量池. 1. Class文件常量池 在Class文件中除了有类的版本.字段.方法.接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用. 所谓字面量类似与我们平常说的常量,主要包括以下两种 文本字符串,例如String a = "aa".其中"aa"就是字

  • Java中文件的读写方法之IO流详解

    目录 1.File类 1.1File类概述和构造方法 1.2File类创建功能 1.3File类判断和获取功能 1.4File类删除功能 2.递归 2.1递归 2.2递归求阶乘 2.3递归遍历目录 3.IO流 3.1 IO流概述和分类 3.2字节流写数据 3.3字节流写数据的三种方式 3.4字节流写数据的两个小问题 3.5字节流写数据加异常处理 3.6字节流读数据(一次读一个字节数据) 3.7字节流复制文本文件 3.8字节流读数据(一次读一个字节数组数据) 3.9字节流复制图片 总结 1.Fil

  • 浅谈java中unmodifiableList方法的应用场景

    java对象中primitive类型变量可以通过不提供set方法保证不被修改,但对象的List成员在提供get方法后,就可以随意add.remove改变其结构,这不是希望的结果.网上看了下,发现Collections的静态方法unmodifiableList可以达到目的.方法原型为:public static <T> List<T> unmodifiableList(List<? extends T> list);用法也很简单,传入一个List实例la,返回这个list

随机推荐