Java字符串对象池的作用是什么?

2024-01-07 19:12:38

Java字符串对象池的作用是什么?

在这里插入图片描述

在这里插入图片描述

在 Java 中,字符串池(String Pool)是字符串常量的存储区域,它位于堆区域中。字符串池的作用是提高字符串的重用性,减少内存消耗。

字符串池的位置:

  1. 在堆中: 字符串池是堆区的一部分。当创建字符串时,Java 首先检查字符串池中是否已经存在相同内容的字符串,如果存在,则直接返回池中的引用,不会重复创建。如果字符串池中没有相同内容的字符串,则将新字符串添加到池中。

  2. String 类中的 intern() 方法: String 类中有一个 intern() 方法,该方法可以手动将字符串对象添加到字符串池中。如果字符串池中已经存在相同内容的字符串,intern() 方法返回池中的引用;否则,将当前字符串对象添加到池中并返回。

与堆中 new 出来的字符串的区别:

  1. 重用性: 字符串池中的字符串具有重用性。如果在字符串池中已经存在相同内容的字符串,不会创建新的对象,而是直接返回现有对象的引用。而使用 new 关键字创建的字符串对象,无论内容是否相同,都会在堆中创建一个新的对象。

  2. 比较: 字符串池中的字符串可以使用 == 进行比较,因为它们是相同对象的引用。而在堆中通过 new 创建的字符串,应该使用 equals() 方法进行内容比较,因为它们可能是不同的对象。

示例代码:

String str1 = "Hello";         // 字符串池中创建
String str2 = "Hello";         // 直接使用字符串池中的引用
String str3 = new String("Hello"); // 在堆中创建新的字符串对象

System.out.println(str1 == str2);  // true,因为它们引用相同的对象
System.out.println(str1 == str3);  // false,因为它们引用不同的对象
System.out.println(str1.equals(str3)); // true,因为它们内容相同

总体来说,使用字符串池可以减少内存占用,提高字符串的比较效率。在大部分情况下,推荐使用字符串池来创建字符串对象。

在 Java 中,可以使用 System.identityHashCode() 方法来获取对象的哈希码,从而间接获取对象的地址。以下是一个例子,演示了使用 new 关键字创建字符串和使用字符串池创建字符串的地址输出:

public static void main(String[] args) {
        String str1 = "Hello";         // 字符串池中创建
        String str2 = "Hello";         // 直接使用字符串池中的引用
        String str3 = new String("Hello"); // 在堆中创建新的字符串对象
        String str4 = "Hello";         // 直接使用字符串池中的引用

        System.out.println(str1 == str2);  // true,因为它们引用相同的对象
        System.out.println(str1 == str3);  // false,因为它们引用不同的对象
        System.out.println(str1.equals(str3)); // true,因为它们内容相同
        System.out.println("Address of str1 (str1): " + System.identityHashCode(str1));// Address of str1 (str1): 2065951873
        System.out.println("Address of str2 (str2): " + System.identityHashCode(str2));// Address of str2 (str2): 2065951873
        System.out.println("Address of str3 (str3): " + System.identityHashCode(str3));// Address of str3 (str3): 1922154895
        System.out.println("Address of str4 (str4): " + System.identityHashCode(str4));// Address of str4 (str4): 2065951873
    }

通过 System.identityHashCode() 输出它们的地址信息。请注意,具体的地址值可能会因为不同的 JVM 实现而有所不同。

true
false
true
Address of str1 (str1): 2065951873
Address of str2 (str2): 2065951873
Address of str3 (str3): 1922154895
Address of str4 (str4): 2065951873

在这里插入图片描述

Java中的字符串

Java中没有原生的字符串类型,但是提供了StringStringBufferStringBuilder来表示字符串,在它们的代码实现中都是通过char[]来存储字符串中的字符的。下图是它们的继承关系:
在这里插入图片描述

StringStringBufferStringBuilder 是 Java 中用于处理字符串的三个主要类,它们在设计和使用上有一些关键的区别。

String 类:

  1. 不可变性: String 对象是不可变的,一旦创建,其内容不能被修改。任何对 String 类的操作都会创建一个新的字符串对象。

  2. 线程安全性: 由于不可变性,String 对象是线程安全的,可以在多个线程中共享。

  3. 使用场景: 适用于字符串内容不经常变化的场景,例如字符串常量、配置信息等。

public class Main{
    public static void main(String[] args) {
        String str = "Hello";
        System.out.println("Address of str (str): " + System.identityHashCode(str));//
        str = str + " World";  // 创建了一个新的字符串对象
        System.out.println("Address of str (str): " + System.identityHashCode(str));//
        System.out.println("Address of str (str): " + System.identityHashCode("Hello"));//
        System.out.println("Address of str (str): " + System.identityHashCode("World"));//

    }
}

结果

Address of str (str): 2065951873
Address of str (str): 883049899
Address of str (str): 2065951873
Address of str (str): 2093176254

StringBuffer 类:

  1. 可变性: StringBuffer 是可变的,可以通过其方法对字符串进行修改,而不创建新的对象。

  2. 线程安全性: StringBuffer 是线程安全的,所有的方法都是同步的,适合在多线程环境下使用。

  3. 使用场景: 适用于频繁修改字符串的场景,例如拼接大量字符串。

public class Main{
    public static void main(String[] args) {
        StringBuffer buffer = new StringBuffer("Hello");
        System.out.println("Address of str (buffer): " + System.identityHashCode(buffer));//
        buffer.append(" World");  // 在原有对象上进行修改
        System.out.println("Address of str (buffer): " + System.identityHashCode(buffer));//
        System.out.println("Address of str (Hello): " + System.identityHashCode("Hello"));//
        System.out.println("Address of str ( World): " + System.identityHashCode(" World"));//

    }
}

结果

Address of str (buffer): 2065951873
Address of str (buffer): 2065951873
Address of str (Hello): 1922154895
Address of str ( World): 883049899

StringBuilder 类:

  1. 可变性: StringBuilder 也是可变的,类似于 StringBuffer,但不同的是它的方法不是同步的,因此在单线程环境下使用更高效。

  2. 线程安全性: StringBuilder 不是线程安全的,不适合在多线程环境下使用。

  3. 使用场景: 适用于单线程环境下频繁修改字符串的场景,性能比 StringBuffer 更好。

public class EventOverlapChecker {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder("Hello");
        System.out.println("Address of str (builder): " + System.identityHashCode(builder));//
        builder.append(" World");  // 在原有对象上进行修改
        System.out.println("Address of str (builder): " + System.identityHashCode(builder));//
        System.out.println("Address of str (Hello): " + System.identityHashCode("Hello"));//
        System.out.println("Address of str ( World): " + System.identityHashCode(" World"));//
    }
}

结果

Address of str (builder): 2065951873
Address of str (builder): 2065951873
Address of str (Hello): 1922154895
Address of str ( World): 883049899

总结比较:

  • 如果字符串内容基本不变,且需要频繁操作,使用 StringBufferStringBuilder
  • 如果字符串内容基本不变,不需要频繁操作,使用 String
  • 如果字符串内容可能会改变,且在多线程环境下使用,使用 StringBuffer
  • 如果字符串内容可能会改变,且在单线程环境下使用,使用 StringBuilder
    在这里插入图片描述

字符串常量池

由于字符串的不可变性以及字符串使用的频繁性,JVM在堆中通过哈希表实现了一个字符串常量池,用于避免字符串的重复创建。在使用字符串字面量实例化字符串对象时,如果字符串常量池中没有该字符串,就会将该字符串实例化并将该字符串的引用加入字符串常量池;如果字符串常量池中有该字符串引用,那么就会直接返回该引用。

public static void main(String[] args) {
    String str1 = "helloWorld";
    String str2 = "helloWorld";
    System.out.println(str1 == str2);//true
}

在这里插入图片描述

String str1=new String("helloWorld");
String str2=new String(new char[]{'h','e','l','l','o','W','o','r','l','d'});

在这里插入图片描述
再看str2,就只有一个new出来的对象:

在这里插入图片描述

String str=new String("helloWorld");
System.out.println(str.intern()==str);//false

在这里插入图片描述

String str=new String("hello")+new String("World");
System.out.println(str.intern()==str);//true

在这里插入图片描述

在 Java 中,字符串池中实际上保存的是字符串对象的引用,而不是在堆中存储字符串对象的地址。每个字符串池中的引用指向在堆中实际存储的字符串对象。

字符串是不可变的,当我们创建一个字符串时,如果字符串池中已经存在相同内容的字符串,则直接返回池中的引用。如果字符串池中没有相同内容的字符串,会创建一个新的字符串对象,并将其引用加入到字符串池中。

这种机制有助于提高字符串的重用性,减少内存占用。因为相同的字符串在池中只保存一份,多个字符串变量可以共享相同的引用,而不需要重复创建相同的字符串对象。

文章来源:https://blog.csdn.net/weixin_46225503/article/details/135424251
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。