前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >正确理解和使用JAVA中的字符串常量池

正确理解和使用JAVA中的字符串常量池

作者头像
程序猿川子
发布2022-12-26 16:37:35
7950
发布2022-12-26 16:37:35
举报
文章被收录于专栏:用户9379187的专栏

前言

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

理解字符串常量池

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

比如下面的代码:

代码语言:javascript
复制
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关键字。他们创建的字符串对象是都在常量池中吗?

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

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

代码语言:javascript
复制
s1==s3 //真
s1==s4 //假
s2==s5 //假
复制代码

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

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

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

字符串拼接方式

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

例子1

代码语言:javascript
复制
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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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()方法的作用可以理解为主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

代码语言:javascript
复制
String s6 = new String("The Lord of the Rings").intern();
复制代码
代码语言:javascript
复制
s2==s6 //真
s2==s5 //假

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 理解字符串常量池
  • 两种创建字符串方式
  • 字符串拼接方式
  • 妙用String.intern() 方法
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档