Java String 类解析

本文详细探讨了Java中String对象不可变性的原因,包括其设计优势和线程安全性。同时,解释了字符串池的概念,强调其在节省内存和提升性能上的作用。还介绍了String.intern()方法的工作原理及其在字符串共享中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

String对象是不可变的

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

String 被声明为 final,因此它不可被继承。内部使用 char 数组存储数据,该数组被声明为 private final char value[];,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,所有需要修改char值的方法都是创建新的char数组进行返回,因此可以保证 String 不可变。String类中每一个看起来会修改String值的方法实际上都创建了一个全新的String对象。

为什么String对象不可变

因为value[] 和 hash变量是private类型,其他类不可访问,类内也没有提供修改value[] 引用、value数组值的、hash值的方法。外界也不能获得value[] 的指针,如果用value[]构造String类,String类会新建一个数组复制value数组。并且value引用是final类型。

但通过反射其实可以改变String对象的值,具体看Java 反射

String 不可变的优势

  • 因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
  • 如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。(多个引用指向一个对象)
  • String 经常作为参数,String 不可变性可以保证参数不可变
  • String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

String Pool (字符串池)

  • JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,这就是字符串池(String Pool),字符串池由String类私有的维护
  • 字符串池的优点就是避免了相同内容的字符串的创建,节省了内存,省去了创建相同字符串的时间,同时提升了性能;另一方面,字符串池的缺点就是牺牲了JVM在常量池中遍历对象所需要的时间,不过其时间成本相比而言比较低。
  • 每一个字符串常量都是指向一个字符串类实例的引用。字符串对象有一个固定值。字符串常量,或者常量表达式中的字符串都使用方法 String.intern()进行保留来共享唯一的实例。
  • 字符串池中维护了共享的字符串对象,这些字符串不会被垃圾收集器回收。
  • 在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

String.intern() 方法

  • 一个初始为空的字符串池,它由类String独自维护。当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用
  • 对于任意两个字符串s和t,当且仅当s.equals(t)为true时,s.intern() == t.intern()才为true。
  • 所有字符串常量和表达式中的字符串常量(编译时期已经确定)都被使用了intern()方法。即已被加入到了String Pool中。
  • intern()Native函数。
    在这里插入图片描述

字符串池的测试:

在这里插入图片描述

其中new String("abc");

  • 使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。
  • “abc” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “abc” 字符串字面量;
  • 而使用 new 的方式会在堆中创建一个字符串对象。
  • 虽然str与str1指向两个对象,但都由"111"这个字符串对象构造而来,两个对象的value[]指向同一个char数组(与字符串池中"111"的value[]指向也相同)。
public String(String str) 构造函数的源代码:

在这里插入图片描述

在这里插入图片描述
这里编译错误,因为 “1111” 是String类型,而非char[] 类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值