String 类和常量池

String 对象的两种创建方式

  1. String str1 = "abcd";//先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"";
  2. String str2 = new String("abcd");//堆中创建一个新的对象
  3. String str3 = new String("abcd");//堆中创建一个新的对象
  4. System.out.println(str1==str2);//false
  5. System.out.println(str2==str3);//false

上述两种不同的创建方法的区别:

  • 第一种方式是在常量池中拿对象
  • 第二种方法是直接在堆内存中创建一个新的对象

    String 类型的常量池

    String 类型的常量池比较特殊,它有 2 中使用方法

  • 直接使用双引用“”声明出来的 String 对象会直接存储在常量池中

  • 如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern() 方法

String.intern() 方法是一个 Native 方法,它的作用是:如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则先在常量池中记录此字符串的引用,然后返回该引用

  1. String s1 = new String("计算机");
  2. String s2 = s1.intern();
  3. String s3 = "计算机";
  4. System.out.println(s2);//计算机
  5. System.out.println(s1 == s2);//false,因为一个是堆内存中的 String 对象一个是常量池中的 String 对象,
  6. System.out.println(s3 == s2);//true,因为两个都是常量池中的 String 对象

String s1 = new String(“abc”) 语句创建了几个字符串对象?

将创建 1 或 2 个字符串对象。如果常量池中已经存在字符串常量 “abc”,则只会在堆内存中创建一个字符串常量“abc”;如果没有,那么它将首先在常量池中创建,然后再在堆内存中创建,所以会创建 2 个对象

String 为什么是不可变的?

String 类内部实现使用 final 关键字修饰字符数组,被 final 关键字修饰的变量为常量。常量具有不可变特性,故 String 是不可变的

  1. // Java 9 之前
  2. private final char[] value;
  3. // Java 9 之后
  4. private final byte[] value;

String、StringBuilder 和 StringBuffer 有什么区别?

  • 在代码实现上:StringBuilderStringBuffer 有共同的父类 AbstractStringBuilderStringBuilderStringBuffer 实现代码基本一致,而 StringBuffer 对方法加了同步锁或对调用的方法加了同步锁,StringBuffer 是线程安全的
  • 在性能上:
    • String 类是不可变类,每次对 String 操作都会生成一个新的对象,然后将指针指向新对象,原对象在内存中等待回收
    • StringBuilderStringBuffer 操作原理类似,每次操作都是对原对象本身进行操作,而不是生成新的对象、也不会改变对象引用。区别在于,StringBuilder 不保证线程安全,而 StringBuffer 保证线程安全;为此 StringBuilder 性能略优于 StringBuffer

综上对比,使用时仅有少量操作时选择 String;单线程或不强调线程安全时选择 StringBuilder;而多线程或强制线程安全时使用 StringBuffer