前言

研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池。Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串。它的实现是为了提高字符串操作的性能并节省内存。它也被称为String Intern PoolString Constant Pool。那让我来看看究竟是怎么一回事吧。

欢迎关注微信公众号「JAVA旭阳」交流和学习

理解字符串常量池

当您从在类中写一个字符串字面量时,JVM将首先检查该字符串是否已存在于字符串常量池中,如果存在,JVM 将返回对现有字符串对象的引用,而不是创建新对象。我们通过一个例子更好的来理解。

比如下面的代码:

String s1 = "Harry Potter";
String s2 = "The Lord of the Rings";
String s3 = "Harry Potter";

在这段代码中,JVM 将创建一个值为“Harry Potter”的字符串对象,并将其存储在字符串常量池中。s1和s3都将是对该单个字符串对象的引用。

如果s2的字符串内容“The Lord of the Rings”不存在于池中,则在字符串池中生成一个新的字符串对象。

两种创建字符串方式

Java 编程语言中有两种创建 String 的方法。第一种方式是使用String Literal字符串字面量的方式,另一种方式是使用new关键字。他们创建的字符串对象是都在常量池中吗?

  • 字符串字面量的方式创建
String s1 = "Harry Potter";
String s2 = "The Lord of the Rings";
String s3 = "Harry Potter";
  • new关键字创建
String s4 = new String("Harry Potter");
String s5 = new String("The Lord of the Rings");

我们来比较下他们引用的是否是同一个对象:

s1==s3 //真
s1==s4 //假
s2==s5 //假

使用 == 运算符比较两个对象时,它会比较内存中的地址。

正如您在上面的图片和示例中看到的,每当我们使用new运算符创建字符串时,它都会在 Java 堆中创建一个新的字符串对象,并且不会检查该对象是否在字符串常量池中。

那么我现在有个问题,如果是字符串拼接的情况,又是怎么样的呢?

字符串拼接方式

前面讲清楚了通过直接用字面量的方式,也就是引号的方式和用new关键字创建字符串,他们创建出的字符串对象在堆中存储在不同的地方,那么我们现在来看看用+这个运算符拼接会怎么样。

例子1

public static void test1() {
// 都是常量,前端编译期会进行代码优化
// 通过idea直接看对应的反编译的class文件,会显示 String s1 = "abc"; 说明做了代码优化
String s1 = "a" + "b" + "c";
String s2 = "abc"; // true,有上述可知,s1和s2实际上指向字符串常量池中的同一个值
System.out.println(s1 == s2);
}
  • 常量与常量的拼接结果在常量池,原理是编译期优化。

例子2

public static void test5() {
String s1 = "javaEE";
String s2 = "hadoop"; String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2; System.out.println(s3 == s4); // true 编译期优化
System.out.println(s3 == s5); // false s1是变量,不能编译期优化
System.out.println(s3 == s6); // false s2是变量,不能编译期优化
System.out.println(s3 == s7); // false s1、s2都是变量
System.out.println(s5 == s6); // false s5、s6 不同的对象实例
System.out.println(s5 == s7); // false s5、s7 不同的对象实例
System.out.println(s6 == s7); // false s6、s7 不同的对象实例
}
  • 只要其中有一个是变量,结果就在堆中, 变量拼接的底层原理其实是StringBuilder

例子3:

public void test6(){
String s0 = "beijing";
String s1 = "bei";
String s2 = "jing";
String s3 = s1 + s2;
System.out.println(s0 == s3); // false s3指向对象实例,s0指向字符串常量池中的"beijing"
String s7 = "shanxi";
final String s4 = "shan";
final String s5 = "xi";
String s6 = s4 + s5;
System.out.println(s6 == s7); // true s4和s5是final修饰的,编译期就能确定s6的值了
}
  • 不使用final修饰,即为变量。如s3行的s1和s2,会通过new StringBuilder进行拼接
  • 使用final修饰,即为常量。会在编译器进行代码优化。

妙用String.intern() 方法

前面提到new关键字创建出来的字符串对象以及某些和变量进行拼接不会在字符串常量池中,而是直接在堆中新建了一个对象。这样不大好,做不到复用,节约不了空间。那有什么好办法呢?intern()就派上用场了,这个非常有用。

intern()方法的作用可以理解为主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

String s6 = new String("The Lord of the Rings").intern();

s2==s6 //真
s2==s5 //假

字符串常量池有多大?

关于字符串常量池究竟有多大,我也说不上来,但是讲清楚它底层的数据结构,也许你就明白了。

字符串常量池是一个固定大小的HashTable,哈希表,默认值大小长度是1009。如果放进String PoolString非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降。

使用-XX:StringTablesize可设置StringTable的长度

  • 在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTable Size设置没有要求
  • 在jdk7中,StringTable的长度默认值是60013StringTable Size设置没有要求

● 在jdk8中,设置StringTable长度的话,1009是可以设置的最小值

字符串常量池的优缺点

字符串池的优点

  • 提高性能。由于 JVM 可以返回对现有字符串对象的引用而不是创建新对象,因此使用字符串池时字符串操作更快。
  • 共享字符串,节省内存。字符串池允许您在不同的变量和对象之间共享字符串,通过避免创建不必要的字符串对象来帮助节省内存。

字符串池的缺点

  • 它有可能导致性能下降。从池中检索字符串需要搜索池中的所有字符串,这可能比简单地创建一个新的字符串对象要慢。如果程序创建和丢弃大量字符串,则尤其如此,因为每次使用字符串时都需要搜索字符串池。

总结

其实在 Java 7 之前,JVM将 Java String Pool 放置在PermGen空间中,它具有固定大小——它不能在运行时扩展,也不符合垃圾回收的条件。在PermGen(而不是堆)中驻留字符串的风险是,如果我们驻留太多字符串,我们可能会从 JVM 得到一个OutOfMemory错误。从 Java 7 开始,Java String Pool存放在Heap空间,由 JVM进行垃圾回收。这种方法的优点是降低了OutOfMemory错误的风险,因为未引用的字符串将从池中删除,从而释放内存。

现在通过本文的学习,你该知道如何更好的创建字符串对象了吧。

欢迎关注微信公众号「JAVA旭阳」交流和学习

更多学习资料请移步:程序员成神之路

正确理解和使用JAVA中的字符串常量池的更多相关文章

  1. Java中的字符串常量池,栈和堆的概念

    问题:String str = new String(“abc”),“abc”在内存中是怎么分配的?    答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...

  2. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  3. Java中String字符串常量池

    首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...

  4. Java中String字符串常量池总结

    最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...

  5. Java进阶——Java中的字符串常量池

    转载. https://blog.csdn.net/qq_30379689/article/details/80518283 字符串常量池 JVM为了减少字符串对象的重复创建,其内部维护了一个特殊的内 ...

  6. Java中的字符串常量池

    ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  7. Java中的字符串常量池和JVM运行时数据区的相关概念

    什么是字符串常量池 JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池 工作原理 当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量 ...

  8. Java中的字符串常量池?

    参考:http://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/index.html

  9. Java String:字符串常量池(转)

    作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么? 字符串常量池的设计思想是什么? 字符串常量池在哪里? 如何操作字符串常量 ...

  10. Java SE之字符串常量池

    Reference Document: 什么是字符串常量池?   http://www.importnew.com/10756.html[Recommend] Java常量池理解与总结   http: ...

随机推荐

  1. Codeforces Round #823 (Div. 2) A-D

    比赛链接 A 题解 知识点:贪心. 对于一个轨道,要么一次性清理,要么一个一个清理.显然,如果行星个数大于直接清理的花费,那么选择直接清理,否则一个一个清理.即 \(\sum \min (c,cnt[ ...

  2. 在Tomcat中启用虚拟线程特性

    前提 趁着国庆前后阅读了虚拟线程相关的源码,写了一篇<虚拟线程 - VirtualThread源码透视>,里面介绍了虚拟线程的实现原理和使用示例.需要准备做一下前期准备: 安装OpenJD ...

  3. 巧用VBA实现:基于多个关键词模糊匹配Excel多行数据

    在用Excel处理实际业务中,我们会碰到如下场景: 1.从一堆人名中找到包含某些关键字的名字: 2.从银行流水文件中根据[备注]字段找到包含某些关键字的,统一识别为[手续费业务]等. 这本质说的都是一 ...

  4. JavaFx 使用字体图标记录

    原文:JavaFx 使用字体图标记录 - Stars-One的杂货小窝 之前其实也是研究过关于字体图标的使用,还整了个库Tornadofx学习笔记(4)--IconTextFx开源库,整合5000+个 ...

  5. JavaScript基础&实战(2)js中的强制类型转换、运算符、关系运算符、逻辑运算符、条件运算符

    文章目录 1.强制类型转换Number 1.1 代码 1.2 测试结果 2.进制表示 2.1 代码 2.2 测试结果 3.强制类型转换为Boolea 3.1 代码 3.2 测试结果 4.运算符 4.1 ...

  6. 手把手教你使用LabVIEW实现Mask R-CNN图像实例分割

    前言 前面给大家介绍了使用LabVIEW工具包实现图像分类,目标检测,今天我们来看一下如何使用LabVIEW实现Mask R-CNN图像实例分割. 一.什么是图像实例分割? 图像实例分割(Instan ...

  7. JS逆向实战6-- x轴 y轴 过点触验证码

    点触验证码是一种常见的反爬手段 解决方案有两种:一种是直接解决,这需要深度学习机器学习等图像处理技术,以大量的数据训练识别模型,最终达到模型足矣识别图片中的文字提示和应该点击的区域之间的对应关系. 这 ...

  8. SpringBoot 02: 初识SpringBoot

    1. SpringBoot 产生原因 spring, springmvc框架使用上的一些缺点: 需要使用的大量的配置文件 还需要配置各种对象 需要把使用的对象放入到spring容器中才能使用对象 需要 ...

  9. 思维分析逻辑 2 DAY

    目录 数据分析多元思维模型 微观能力 中观能力 宏观能力 电商平台分析 整体数据 漏斗模型 互联网金融分析 授信模型 了解芝麻信用分结构 数据源的数据变量 数据处理 游戏数据分析 常规指标 商业化指标 ...

  10. 【第6篇】AI语音测试简介

    1.4 AI语音测试简介 AI语音测试是针对人工智能语音交互的测试,人工智能语音交互一般包含语音识别技术(ASR).自然语言处理技术(NLP)和语音合成技术(TTS)等3大部分,通俗地来讲AI语音测试 ...