目录
一、字符串常量池
二、String的不可变性
三、String的内存分配
四、intern() 方法与 new String()
一、字符串常量池
JVM的字符串常量池(String Constant Pool)是一块位于方法区(Method Area)的内存区域,用于存储字符串常量。字符串常量池是为了提高性能和节省内存而设计的。在Java编程中,字符串是不可变的(immutable),即一旦创建就不能被修改。为了避免重复创建相同的字符串,JVM将字符串常量放入常量池中,并且多个引用可以指向同一个字符串常量。
当代码中出现字符串常量时,JVM首先检查常量池中是否已经存在该字符串。如果存在,则返回常量池中的引用,而不是创建新的字符串对象。这样可以节省内存空间,同时提高字符串的比较效率。
字符串常量池有以下特点:
- 字符串常量池中的字符串是唯一的,不会存在重复的字符串。
- 字符串常量池是在编译时确定的,即字符串常量在编译阶段被确定并放入常量池中。
- 字符串常量池中的字符串一般存储在方法区(Method Area)中。
可以使用String类的intern()方法将一个字符串对象添加到字符串常量池中。如果字符串常量池中已经存在该字符串,则返回常量池中的引用;如果不存在,则将该字符串对象添加到常量池中并返回引用。
使用字符串常量池可以提高字符串的比较效率,并且在一些特定场景下可以节省内存空间。但是需要注意,过度使用字符串常量池可能会导致内存泄漏或者占用过多的内存,因为字符串常量池中的字符串对象是不可回收的。因此,在使用字符串常量池时需要慎重考虑,并根据具体情况进行合理的字符串管理。
二、String的不可变性
String类在Java中是不可变的(immutable)。这意味着一旦创建了String对象,就无法修改其内容。下面是String的不可变性的几个关键点:
-
字符串内容不可变:String对象存储了一个字符序列,一旦创建,其中的字符内容不能被修改。如果尝试修改String对象的字符内容,实际上是创建了一个新的String对象。
-
String对象是线程安全的:由于String的不可变性,多个线程可以同时访问和共享String对象,而无需担心数据的不一致性。这使得String在多线程环境下具有线程安全性。
-
字符串常量池:Java中有一个字符串常量池,用于存储字符串常量。当使用字符串字面量创建String对象时,JVM会首先检查字符串常量池中是否已经存在相同内容的字符串。如果存在,则返回常量池中的引用;如果不存在,则创建一个新的String对象并将其添加到常量池中。这样可以避免创建重复的字符串对象,节省内存。
-
安全性:由于String的不可变性,可以将String对象用作方法参数,而无需担心在方法内部被修改。
-
缓存哈希值:String类的哈希值是在对象创建时计算出来的,并缓存在对象内部。这样可以避免每次调用哈希方法时都重新计算哈希值。
String的不可变性带来了一些优势,例如线程安全、安全性、哈希值缓存等。但也需要注意,频繁的字符串操作可能会导致大量的中间字符串对象的创建,占用较多的内存。在这种情况下,可以使用StringBuilder或StringBuffer类来进行可变字符串的操作,以提高性能。
三、String的内存分配
在JVM中,String对象的内存分配主要涉及两个部分:字符串常量池和堆内存。
-
字符串常量池(String Pool):
- 字符串常量池是在运行时常量池(Runtime Constant Pool)中的一个特殊部分,用于存储字符串常量。
- 当使用字面量创建String对象时,JVM首先会在字符串常量池中查找是否存在相同内容的字符串。
- 如果找到了相同内容的字符串,则返回常量池中的引用。
- 如果没有找到,则会在常量池中新建一个字符串对象并返回引用。
- 字符串常量池是在方法区(Method Area)中的一部分,它是一个共享的区域,所有线程都可以访问。
-
堆内存(Heap Memory):
- 当使用new关键字创建String对象时,会在堆内存中分配内存空间,并为字符串对象的内容分配存储空间。
- 这种方式创建的字符串对象不会被放入字符串常量池中,而是独立于常量池。
- 堆内存中的字符串对象是可变的,可以通过String的一些方法来修改内容。
需要注意的是:
- 在Java 7及之前的版本中,字符串常量池位于永久代(Permanent Generation),而在Java 8及之后的版本中,字符串常量池被移至堆内存。
- 在字符串拼接等操作时,如果使用的是字面量(如"abc" + "def"),编译器会在编译阶段就将其优化为一个新的字面量,而不是在运行时进行拼接操作。
总结:字符串常量池是一个特殊的常量池用于存储字符串常量,而堆内存用于存储通过new关键字创建的字符串对象。
四、intern() 方法与 new String()
intern()
方法与 new String()
在Java中都用于创建字符串对象,但它们的行为和作用略有不同。
intern()
方法:
intern()
方法是String类提供的一个实例方法,用于在字符串常量池中查找或添加一个与调用对象内容相同的字符串,并返回常量池中的引用。- 如果调用
intern()
方法的String对象在常量池中已经存在,则返回常量池中的引用;如果不存在,则将该字符串添加到常量池中并返回引用。 - 通常用于节省内存,避免创建重复的字符串对象,并且可以方便地进行字符串内容的比较。
new String()
:
new String()
是使用关键字new
显式地创建一个新的String对象。- 不同于直接使用字符串字面量或调用
intern()
方法,使用new String()
会在堆内存中创建一个新的字符串对象,即使字符串内容在常量池中已经存在。 - 这种方式创建的字符串对象是独立于字符串常量池的,即使内容相同,也会在堆内存中创建一个新的对象。
总结:
intern()
方法是用于将字符串对象添加到常量池中或获取常量池中已存在的对象的引用。new String()
是显式地创建一个新的字符串对象,不受字符串常量池的影响。