Java 字符串常量池


了解Java中的String类,创建它的不可变性背后的动机,以及String常量池的概念。我们将看到当我们通过String文字或String构造函数创建String实例时,内存是如何被操作的。最后,我们将讨论String类的不可变性引起的主要优点和缺点。

1.Java中的不可变字符串

字符串是字符序列。在Java中,与其他编程语言类似,字符串是预定义类型的一部分。Java有java.lang.String类,其实例表示字符串。

String类是一个不可变类。不可变意味着一旦创建了String实例,就无法更改它。

通常,许多敏感信息(用户名、密码、URL、端口、数据库、套接字连接)被表示并传递为字符串。通过使这些信息不可变,代码就能够免受各种安全威胁的影响。

字符串的不可变性还允许缓存字符串文字,这允许应用程序在堆内存和垃圾收集器上产生最小的影响的情况下使用大量的字符串文字。

在可变的上下文中,修改字符串文字可能导致变量损坏。

2.Java中的String常量池是什么?

在Java中,内存被分为三个部分,即堆、栈和字符串池。字符串常量池是用于存储字符串文字的特殊区域。

请注意,在Java 7之前,字符串池是永久代内存区的一部分。 从Java 7开始,字符串与应用程序创建的其他对象一起分配在Java堆区域中。 随后,在Java 8中,永久代已完全删除。 因此,在最新的JVM中,字符串池是堆内存中的一个特殊区域,用于存储字符串文字。

尽管字符串池已从永久代空间移动到堆内存区域,但围绕字符串的创建、文字、对象和内部化的所有概念都没有改变。

 

3.字符串文字和字符串对象的区别

在Java中,字符串文字是使用双引号创建的字符串,而字符串对象是使用new()运算符创建的字符串。 需要注意的是,字符串文字是在字符串池区域创建的,而字符串对象是在堆内存区域创建的。

String strLiteral = "Hello World";
String strObj = new String("Hello World");

假设我们想创建两个具有相同内容“panziye.com”的字符串。如果已经存在一个内容为“panziye.com”的字符串,那么新的文字将指向已经存在的文字。而对于字符串对象,每次都会在堆内存中创建一个新的字符串对象。

让我们看一个示例:

String a = "panziye.com";
String b = "panziye.com";
System.out.println(a == b);     //true

在上面的程序中,我们创建了两个具有相同内容的字符串文字。在字符串池中创建了’a’之后,下一个字符串文字’b’指向了内存中的同一个对象,所以’a == b’是true。这是因为Java中的字符串文字会被重用,以节省内存空间。

String a = "panziye.com";
String b = "panziye.com";
System.out.println(a == b);
String c = new String("panziye.com");
System.out.println(a == b);     //true
System.out.println(b == c);     //false

在上面的程序中,我们创建了一个具有类似内容的新字符串对象。当我们检查对象引用是否相等时,我们看到’b’和’c’指向不同的对象。这意味着当我们创建字符串对象’c’时,在内存中创建了一个新的对象。这是因为每次使用new关键字创建字符串对象时,都会在堆内存中分配一个新的对象。这与字符串文字不同,字符串文字在字符串池中被重用。

4.String.intern() API

我们知道字符串文字是在字符串池中创建的,而字符串对象是在堆内存区域中创建的。

我们可以使用String.intern()方法为字符串对象创建字符串文字。当在字符串对象上调用intern()方法时,它会在堆内存中创建一个String对象的精确副本,并将其存储在字符串常量池中。

String a = "panziye";
String b = "panziye";
String c = new String("panziye");
String d = c.intern();

在上面的例子中,字符串a、b和d将引用SCP中相同的字符串文字。而字符串c将继续指向堆内的对象。

5.优点

5.1. 增强的安全性

正如前面所述,字符串池允许字符串不可变。不可变对象有助于使应用程序更安全,因为它们可能存储敏感信息。

由于我们无法更改不可变对象,这有助于提高安全性。

5.2. 线程安全性

字符串可能是Java应用程序中最常用的对象。想象一下,如果字符串是可变的。在这种情况下,在应用程序中管理线程安全将会成为一场噩梦。

任何不可变对象都具有天生的线程安全性。这意味着多个线程可以共享和操作字符串,而无需担心损坏和不一致性。

6.缺点

6.1. 无法扩展String类

如果我们想扩展String类以添加更多功能,那是不可能的。不可变类被声明为final以防止扩展。

但幸运的是,我们有许多第三方库(如Apache Commons Lang、Spring Framework、Guava),它们为几乎所有种类的用途提供了出色的实用程序类。

6.2. 长时间内存中的敏感数据

字符串中的敏感数据(例如密码)可能会在内存中(在字符串常量池中)驻留更长时间,因为字符串常量池受到垃圾收集器的特殊处理。垃圾收集器不会像访问其他内存区域那样频繁(周期性)访问字符串常量池。

由于这种特殊处理,敏感数据会在字符串常量池中保存很长时间,可能会容易受到不希望的使用。

为了避免这种潜在的缺点,建议将敏感数据(例如密码)存储在char[]中,而不是String中。

6.3. 可能的OutOfMemoryError

字符串常量池与其他内存区域相比是一个小内存区域,可能会很快填满。将太多字符串文字存储在字符串常量池中会导致OutOfMemoryError。



为什么Java中字符串是不可变的

Java String面试题汇总

获取更多资讯请加入交流群


    协助本站SEO优化一下,谢谢!
    关键词不能为空
评 论
更换验证码