欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 深入解析Java字符串:常量池、内存管理与StringBuilder、StringBuffer操作类指南

深入解析Java字符串:常量池、内存管理与StringBuilder、StringBuffer操作类指南

2025/7/20 21:54:30 来源:https://blog.csdn.net/weixin_55599565/article/details/145912838  浏览:    关键词:深入解析Java字符串:常量池、内存管理与StringBuilder、StringBuffer操作类指南

文章目录

  • 前言
    • 本次话题:String类。
  • 一、String内部表示
    • 1、字符数组存储
    • 2、如何减少内存使用呢?
    • 结论
  • 二、String的内存管理
    • 1、 堆内存与方法区(元空间):
  • 三、StringBuilder和StringBuffer的区别
    • 1、如何选择?
    • 2、StringBuffer线程安全的实现
  • 总结


前言

在时光的长河中,技术如同一只悄然进化的鳄鱼,沉稳而强大,在看似平静的水面下孕育着无限可能。

鳄鱼杆 ,一名悄悄进化 No 变异的博主。开启了本次话题文章:

本次话题:String类。

在Java中,字符串的存储是一个非常有趣且复杂的话题。它涉及到字符串常量池、内存管理等多个方面。

本篇文章主要通过两个方面来分析Java中字符串:String

  1. String内部表示
  2. String的内存管理

同时,提供一些实践代码来展示String类的构成。相信我们一定可以在其中找到一些问题的答案。

Let’s GO!

在这里插入图片描述


一、String内部表示

在Java中,String 类的内部表示方式对于理解其性能特性和行为至关重要。

1、字符数组存储

  • 字段定义:
    • 从Java 9开始,String 类使用名为 value 的私有 byte[] 数组来存储字符数据,而不是之前的 char[]。
  • 这是引入了一种称为“压缩字符串”的优化技术,旨在减少内存使用。
  • 这就引出了一个问题,那它是如何减少内存使用呢?

2、如何减少内存使用呢?

  • 在Java 8及之前,String类使用一个char[]数组来存储字符串内容。这就导致每个字符占用2个字节(即UTF-16编码)。
  • 自Java 9起,String类改用了byte[]数组来存储字符数据,增加了字段:coder;
    • 用于指示当前字符串使用的编码类型。
    /*** The identifier of the encoding used to encode the bytes in* value. The supported values in this implementation are LATIN1 UTF16* * 使用的编码标识符,用于编码 value 中的字节。* 在此实现中支持的值是 LATIN1 和 UTF16。* * This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.*/private final byte coder;
                             JDK 17中代码片段

其中,支持的值是 LATIN1 和 UTF16的解释:

  1. 对于LATIN1编码,coder值为0;
    • 通常指的是ISO-8859-1编码,足够日常使用。(1字节)
  2. 对于UTF16编码,coder值为1。
    • 通常指可表示几乎所有书写系统的字符编码方式。(2字节)
    /** The value is used for character storage. */private final char value[];-----------------JDK8JDK17String类部分代码对比----------------  /*** The value is used for character storage.* 该值用于字符存储。* *  This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.* * 此字段被虚拟机信任,并且如果字符串实例是常量,则此字段可能会进行常量折叠。* 构造后覆写此字段将会导致问题。** Additionally, it is marked with {@link Stable} to trust the contents* of the array. No other facility in JDK provides this functionality (yet).* @Stable is safe here, because value is never null.* * 此外,它还被标记为 Stable 以信任数组的内容。* JDK 中目前没有其他设施提供这种功能。* 在这里 @Stable 是安全的,因为值永远不会是 null。* */@Stableprivate final byte[] value;
  • 以上是JDK17版本中,String类的部分代码,有一点值得关注:
  • constant folding
    • 指的是编译器优化技术,它会在编译期计算常量表达式的值,而不是在运行时计算.
    • 用于提高程序的性能。
  1. 不可变性
    • 上面代码提到的**@Stable**:
    • @ Stable 注解表明了被注解的字段在使用过程中不会发生改变。(即其内容是稳定的)
    • 同时也因为value数组被 final 修饰

String 对象是不可变的,这意味着一旦创建了一个 String 对象,就不能更改它的值。

  • 这里说的是值。有的时候,我们可以更改变量的引用,相当于间接更改值。
  • 可以通过一段代码来演示String的不可变性。

例如:

 public static void main(String[] args) {String temp = "change after";System.out.println("temp对象的hashCode:" + temp.hashCode());//      原始对象String str = "Not change";System.out.println("str对象的hashCode:" + str.hashCode());//        对象引用修改str = temp;System.out.println("引用修改后str的hashCode:" + str.hashCode());//      拼接修改str = temp + "second change"; System.out.println("方法修改后str的hashCode:" + str.hashCode());}

执行以上代码会得到如下结果:

  • 无论通过哪种方式,都无法修改原有对象的内容。(反射除外,说反射的纯粹是来捣乱的)

在这里插入图片描述

其中字符串拼接代码值得我们关注,如下代码:(JDK8版本)

    public static void main(String[] args) {String s1 = "a";String s2 = "b";//编译器优化:编译期拼接abString s4 = "a" + "b";
// 相当于new StringBuilder().append(s1).append(s2).toString();String s4 = s1 + s2;}

如果我们使用JDK9以及以后的版本呢?就会得到下面的情况:

    public static void main(String[] args) {String s1 = "a";String s2 = "b";
//编译器会使用 StringConcatFactory.makeConcatWithConstants 方法来处理此字符串拼接String s4 = s1 + s2;}

这一点可以通过:javap className.class,反编译后的字节码文件得出:

在这里插入图片描述
其中,makeConcatWithConstants()

  • 这个方法特别适合于那些包含常量字符串与一个或多个变量的拼接情况。
    • 允许编译器在编译期确定不变的部分,并在运行时仅对需要计算的部分进行处理,从而减少了运行时的开销,提高了程序的执行效率。

结论

  • String 类的设计考虑了多方面的因素,包括性能、内存使用和安全性。
    • 通过使用 字节数组 进行内部表示,并结合不可变性、压缩字符串等特性。
    • 了解这些细节有助于我们更好地利用 String 类的功能,并编写出更加高效的代码。

二、String的内存管理

  • 内存方面绕不开的便是字符串常量池。下面介绍字符串常量池的位置,以及内存空间术语介绍:

1、 堆内存与方法区(元空间):

  • 虽然字符串常量池(Java 7之前),是在永久代(PermGen)中实现的,但在JDK 17中,字符串常量池(String Pool)位于堆(Heap)内存区域中的“元空间”(Metaspace)之外。,以更好地进行垃圾回收。
  • 元空间(方法区的实现):
    • 自Java 8开始引入的一个内存区域,用于替代永久代(Permanent Generation, PermGen)。
  • 垃圾回收:
    • 由于字符串常量池位于堆内存中,因此其中的对象也可以被垃圾回收器回收,但这通常只发生在极端情况下,例如系统面临严重的内存压力时。

在这里插入图片描述


三、StringBuilder和StringBuffer的区别

特点:

  • 两者都不像 String 类是不可变的,可以改变它们所包含的内容而不必创建新的对象。
  • 在频繁修改字符串内容的情况下,推荐使用!
    public static void main(String[] args) {StringBuffer stringBuffer = new StringBuffer();System.out.println("修改前stringBuffer的hashCode:" + stringBuffer.hashCode());
、stringBuffer.append("b");System.out.println("修改后stringBuffer的hashCode:" + stringBuffer.hashCode());System.out.println("------------------------------------------------------");StringBuilder stringBuilder = new StringBuilder();System.out.println("修改前stringBuilder的hashCode:" + stringBuilder.hashCode());stringBuilder.append("b");System.out.println("修改后stringBuilder的hashCode:" + stringBuilder.hashCode());}

这样我们就会到结果,如下图:

在这里插入图片描述

  • 由这个测试结果,可知无论是StringBuilder还是StringBuffer都是操作同一个对象,不会创建新的对象。

当然我们还可以这样做:

    public static void main(String[] args) {StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("change after");StringBuilder stringBuilder = new StringBuilder();System.out.println("修改前stringBuilder的hashCode:" + stringBuilder.hashCode());stringBuilder.append(stringBuffer);System.out.println("修改后stringBuilder的hashCode:" + stringBuilder.hashCode());}

这样也是行得通的!我们可以从二者的父类:AbstractStringBuilder 中得到答案。

在这里插入图片描述

1、如何选择?

  • 单线程下操作,即确定对字符串的操作是在一个线程内完成的,那么推荐使用 StringBuilder,因为它提供了更好的性能
  • 涉及到多线程环境,即多个线程同时访问或修改同一个 StringBuffer 实例,则应选择 StringBuffer 来确保 线程安全。

2、StringBuffer线程安全的实现

  • 通过对它的公开方法使用 synchronized 关键字来确保任意时刻只有一个线程能够执行这些方法。如图所示:

在这里插入图片描述

总结

  • 综上所述,理解Java中字符串的存储机制有助于编写更加高效和资源友好的代码。
    • 尤其是在处理大量字符串操作的应用程序中,合理利用字符串常量池和选择合适的字符串操作方式显得尤为重要。

各位再见!这里是 鳄鱼杆,钓……鳄鱼的杆儿!

期待下次再会,愿每一次垂钓之旅都能满载而归。

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词