在进一步处理 不变性 ,在得出任何结论之前,让我们先看看String类及其功能。
这就是 一串 作品:
String str = "knowledge";
和往常一样,这会创建一个包含“knowledge”的字符串,并将其分配给reference str。足够简单吗?让我们执行更多功能:
// assigns a new reference to the // same string "knowledge"String s = str;
让我们看看下面的语句是如何工作的:
str = str.concat(" base");
这会在str后面附加一个字符串“base”。但是等等,既然字符串对象是不可变的,这怎么可能呢?让你惊讶的是,的确如此。
当执行上述语句时,VM接受String str的值,即“knowledge”,并附加“base”,给我们一个值“knowledge base”。现在,因为字符串是不可变的,VM不能将这个值分配给str,所以它创建一个新的字符串对象,给它一个值“知识库”,并给它一个引用str。
这里需要注意的一点是,虽然String对象是不可变的, 它的参考变量不是。 这就是为什么在上面的例子中,引用是指一个新形成的字符串对象。
在上面的例子中,我们有两个字符串对象:第一个是我们用s指向的值“knowledge”创建的,第二个是str指向的“knowledge base”。但从技术上讲,我们有三个字符串对象,第三个是concat语句中的文字“base”。
为什么字符串本质上是不变的?
以下是使字符串在Java中不可变的更多原因。这些是:
- 如果字符串在Java中不是不可变的,则无法使用字符串池。JRE节省了大量堆空间。池中的多个字符串变量可以引用同一个字符串变量。如果字符串不是不可变的,那么字符串实习也不可能。
- 如果我们不使字符串不可变,它将对应用程序构成严重的安全威胁。例如,数据库用户名、密码作为字符串传递以接收数据库连接。套接字编程主机和端口描述也作为字符串传递。字符串是不可变的,因此其值不能更改。如果字符串没有保持不变,任何黑客都可以通过更改引用值在应用程序中引起安全问题。
- 由于字符串是不可变的,因此它对于多线程是安全的。不同的线程可以访问单个“字符串实例”。它删除了线程安全的同步,因为我们隐式地使字符串线程安全。
- 不变性提供了通过类加载器加载正确类的安全性。例如,假设我们有一个尝试加载java的实例。sql。连接类,但myhacked的引用值中的更改。Connection类对我们的数据库做了不必要的事情。
关于字符串和内存使用的重要事实
如果我们没有另外提到“知识”呢?我们会失去那根绳子的。然而,它仍然存在,但由于没有参考资料,将被视为丢失。 下面再看一个例子
JAVA
// Java Program to demonstrate why // Java Strings are immutable import java.io.*; class GFG { public static void main(String[] args) { String s1 = "java" ; s1.concat( " rules" ); // Yes, s1 still refers to "java" System.out.println( "s1 refers to " + s1); } } |
s1 refers to java
说明:
- 第一行非常简单:创建一个新字符串“java”,并引用它。
- 接下来,VM创建另一个新字符串“java规则”,但没有任何内容引用它。因此,第二根弦立即丢失。我们够不着。
引用变量s1仍然引用原始字符串“java”。
几乎所有应用于字符串对象以进行修改的方法都会创建一个新的字符串对象。那么,这些字符串对象去了哪里?这些都存在于内存中,任何编程语言的关键目标之一就是有效利用内存。
随着应用程序的增长,字符串文字占用大量内存的情况非常普遍,这甚至会导致冗余。所以,为了让Java更高效, JVM留出一个特殊的内存区域,称为“字符串常量池”。
当编译器看到字符串文字时,它会在池中查找该字符串。如果找到匹配项,对新文本的引用将指向现有字符串,并且不会创建新的字符串对象。现有的字符串只是多了一个引用。下面是使字符串对象不可变的要点:
在字符串常量池中,字符串对象可能有一个或多个引用。如果多个引用指向同一个字符串,而不知道它,那么如果其中一个引用修改了该字符串的值,那就不好了。这就是为什么字符串对象是不可变的。
好吧,现在你可以说,如果有人重写String类的功能呢?这就是为什么 字符串类被标记为final 这样就没有人可以推翻其方法的行为。