Java基础-String

String不可变的原因

在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。

  public final class String
      implements java.io.Serializable, Comparable<String>, CharSequence {
      /** The value is used for character storage. */
      private final byte[] value;
  
      /** The identifier of the encoding used to encode the bytes in {@code value}. */
      private final byte coder;
  }

value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

String不可变的好处

  1. 因为String在Java中是不可变的。Java运行时环境可以节省大量的堆空间,因为不同的String变量可以引用池中的相同String变量。如果String不是不可变的,那么String缓冲池失去作用,因为任何变量已经改变了值,它也会反映在其他变量中。

  2. 如果String是可变的,那么它将对应用程序造成严重的安全威胁。例如,数据库用户名,密码作为String传递以获取数据库连接,并在套接字编程主机和端口详细信息中作为String传递。由于String是不可变的,因此无法更改其值,否则任何黑客都可能更改引用的值以导致应用程序中出现安全问题。

  3. 由于String是不可变的,因此对于多线程是安全的。可以跨不同的线程共享单个String实例。这避免了使用同步来保证线程安全。字符串是隐式线程安全的。

  4. 字符串在java类加载器中使用,不可变性提供了安全性,类由Classloader加载。例如,假设尝试加载java.sql.Connection类的实例,但引用的值更改为myhacked.Connection类,可以对数据库执行不需要的操作。

  5. 由于String是不可变的,因此在创建时缓存它的哈希码,不需要再次计算。这使得它成为Map中键的一个很好的候选者,它的处理速度比其他HashMap键对象快。这就是String是最广泛用作HashMap键的原因

String StringBuffer StringBuilder区别

运行速度快慢为:StringBuilder > StringBuffer > String

  String str="abc";
  System.out.println(str);
  str=str+"de";
  System.out.println(str);

运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。

StringBuilder线程不安全

StringBuffer所有public方法都是synchronized的

缓冲区

StringBuffer 代码片段:

  private transient char[] toStringCache;
  
  @Override
  public synchronized String toString() {
      if (toStringCache == null) {
          toStringCache = Arrays.copyOfRange(value, 0, count);
      }
      return new String(toStringCache, true);
  }

StringBuilder 代码片段:

  @Override
  public String toString() {
      // Create a copy, don't share the array
      return new String(value, 0, count);
  }

可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。

而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。

posted @ 2020-08-28 17:56  rikarika  阅读(85)  评论(0)    收藏  举报