Java中的String为什么要设计成不可变的?

一、不可变类和不可变对象

Normally,you create an object and allow its contents to be changed later.However ,occasionally it is desirable to create an object whose contents cannot be changed once the object has been created.We call such an object as immutable object and its class as immutable class.

创建一个一旦其内容就不能在改变的对象,称其为一个不可变对象(immutable object),而它的类称为不可变类(immutable class)。

 JDK中String的源码

public final class String implements 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的本质是一个char数组,是对字符串数组的封装,并且是被final修饰的,创建后不可改变。

二、String类不可变性的好处

1、便于实现字符串池(String pool)

在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。

String a = "Hello world!";
String b = "Hello world!";

如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!

2、使多线程安全

 看下面这个场景,一个函数appendStr()在不可变的String参数后面加上一段“bbb”后返回。appendSb()负责在可变的StringBuilder后面加"bbb"。

public class test {
  // 不可变的String
  public static String appendStr(String s) {
      s += "bbb";
      return s;
  }

  // 可变的StringBuilder
  public static StringBuilder appendSb(StringBuilder sb) {
      return sb.append("bbb");
  }
  
  public static void main(String[] args) {
      String s = new String("aaa");
      String ns = test.appendStr(s);
      System.out.println("String aaa>>>" + s.toString());
      // StringBuilder做参数
      StringBuilder sb = new StringBuilder("aaa");
      StringBuilder nsb = test.appendSb(sb);
      System.out.println("StringBuilder aaa >>>" + sb.toString());
  }
}

如果程序员不小心像上面例子里,直接在传进来的参数上加上“bbb”.因为Java对象参数传的是引用,所有可变的StringBuffer参数就被改变了。可以看到变量sb在Test.appendSb(sb)操作之后,就变成了"aaabbb"。
有的时候这可能不是程序员的本意。所以String不可变的安全性就体现在这里。

在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。

The advantage of immutability comes with concurrency. It is difficult to maintain correctness in mutable objects, as multiple threads could be trying to change the state of the same object, leading to some threads seeing a different state of the same object, depending on the timing of the reads and writes to the said object.
By having an immutable object, one can ensure that all threads that are looking at the object will be seeing the same state, as the state of an immutable object will not change.

3、避免安全问题

在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。

因为String是不可变的,所以它的值是不可改变的。但由于String不可变,也就没有任何方式能修改字符串的值,每一次修改都将产生新的字符串,如果使用char[]来保存密码,仍然能够将其中所有的元素设置为空和清零,也不会被放入字符串缓存池中,用字符串数组来保存密码会更好。

4、加快字符串处理速度

由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。

在String类的定义中有如下代码:

private int hash;//用来缓存HashCode

总体来说,String不可变的原因要包括 设计考虑,效率优化,以及安全性这三大方面。

 

https://blog.csdn.net/jiahao1186/article/details/81150912

https://blog.csdn.net/u012546526/article/details/44458185

https://blog.csdn.net/hyddhy/article/details/87105691

https://www.cnblogs.com/wcyBlog/p/4073725.html、

posted @ 2019-10-17 17:08  王陸  阅读(7797)  评论(2编辑  收藏  举报