一、可变和不可变目标

可变目标:当您拥有对目标实例的引证时,该实例的内容能够更改 不可变目标:当您拥有对目标实例的引证时,该实例的内容无法更改

运用java.lang.String作为不可变类,运用java.awt.Point作为可变类

public class StringTest {
    public static void main(String[] args) {
        Point myPoint = new Point( 0, 0 );
        System.out.println( myPoint );
        myPoint.setLocation( 1.0, 0.0 );
        System.out.println( myPoint );
        String myString = new String( "old String" );
        System.out.println( myString );
        myString.replaceAll( "old", "new" );
        System.out.println( myString );
    }
}

结果输出如下

java.awt.Point[x=0,y=0]
java.awt.Point[x=1,y=0]
old String
old String

咱们只查看每个目标的单个实例,可是能够看到myPoint的内容发生了改变,而myString的内容没有发生改变。 某些言语,例如 C++ 和Ruby,通常允许在创立字符串后更改其内容;这些被称为可变字符串。在其他言语中,例如Java和Python,该值是固定的,假如要进行任何更改,则有必要创立一个新字符串;这些被称为不可变字符串(其中一些言语还供给了另一种可变类型,例如 Java 和.NET StringBuilder、线程安全的 JavaStringBuffer和Cocoa NSMutableString)。

1、String

在 Java 编程中广泛运用的字符串是一个字符序列。在 Java 编程言语中,字符串是目标。

经过查看String.java的源码能够知道String是被final修饰的,因而它是不能被继承的。

public final class String {
}

下面咱们来看一组代码

public class StringTest {
    public static void main(String[] args) {
		String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1 == str2);
    }
}

下面是该代码的内存图

Java 字符串 String、StringBulider和StringBuffer
凡是带有双引号的字符串都会放在字符串常量池中,字符串放在字符串常量池是为了提高功率。假如运用构造函数或办法创立字符串,则这些字符串将存储在堆内存中的SringConstantPool,可是在保存到池中之前,它会调用intern()办法来运用 equals()办法查看池中具有相同内容的目标可用性。假如池中的字符串副本可用,则回来引证。不然,将 String 目标增加到池中并回来引证。

再来看一组代码

public class StringTest {
    public static void main(String[] args) {
		String str3 = new String("abcde");
        String str4 = new String("abcde");
        System.out.println(str3 == str4);
    }
}

此刻 str3 和 str4 都在堆中创立了目标,这两个目标时两个不同地址的空间,所以上述代码的结果为false。可是他们所指引的字符串仍是在字符串常量区中。

Java 字符串 String、StringBulider和StringBuffer
下面咱们再来看一组代码,以下代码会创立十一个目标。假如,咱们for循环执行上万条,此刻,会造成字符串常量区溢出,所以这个时候就不适合在循环里运用连接符操作字符串。这时就呈现了StringBuffStringBuilder

String str = "a";
for (int i = 0; i < 10; i++) {
    str += "b";
}
System.out.println(str);

Java 字符串 String、StringBulider和StringBuffer

2、StringBuffer和StringBuilder

String是一个不可变的类,无法改变。StringBuilder是一个可变类,能够附加到替换或删除的字符,终究转换为String

下面是常用的办法

public class StringTest {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        String str = "java";
        // 将字符串增加"xiao"字符串
        sb.append("xiao");
        // 将sb字符串中第 [0, 2) 位字符替换为str -- javaao
        sb.replace(0, 2, str);
        // 删除第 [3, 5) 为字符                -- javo
        sb.delete(3, 5);
        // 回来字符串的长度                     -- 4
        System.out.println(sb.length());
        // 回来指定下标的值                     -- j
        System.out.println(sb.charAt(0));
        // 回来下标为 [0, 3) 之间的子序列        -- jav
        System.out.println(sb.substring(0, 3));
        System.out.println(sb);
    }
}

StringBuffer 每逢咱们对运用此类创立的任何 String 进行一些更改时,都不会创立新目标,由于更改是在同一目标中进行的。这个类是线程安全的,即它能够在多线程环境中正常作业。此类的办法是同步的,因而每逢多个线程调用目标上的某些办法时,它们就会依照每个独自线程进行的办法调用的次序执行。 常用办法:

  • append – 在字符串的末尾追加数据。
  • insert – 在任何给定索引处刺进数据。

StringBuilder 这是 StringBuffer 类的一个新的但更小的版本,随着 JDK 5 的发布而引入。它与 StringBuffer 相似,但它不为线程安全或同步供给任何确保。 它用于只要一个线程应用程序的地方,由于它在大多数情况下要快得多。其余部分与 StringBuffer 相似,具有相同的办法和声明。咱们只需要在初始化期间运用不同的类,其余的代码不需要更改,由于它也有相同的办法。

下面是三者的区别

Java 字符串 String、StringBulider和StringBuffer

二、不可变目标的好处

  • 默认情况下,不可变目标是线程安全的,能够在并发环境中共享而无需同步。
  • 不可变目标简化了开发,由于它更容易在多个线程之间共享,而无需外部同步。 -经过减少代码中的同步,java应用程序的不可变目标提高性能。
  • 不可变目标的另一个重要长处是可重用性,您能够缓存不可变目标偏重用它们,就像字符串文本和整数相同。您能够运用静态工厂办法来供给valueOf()之类的办法,它能够从缓存回来现有的不可变目标,而不是创立新目标。

来看下他们三个的运用场景

  1. 假如不是在循环体中进行字符串拼接的话,直接运用 String 的 “+” 就好了。

  2. 单线程循环中操作很多字符串数据 → StringBuilder.append()

  3. 多线程循环中操作很多字符串数据 → StringBuffer.append()