String 类和常量池
String 对象的两种创建方式
String str1 = "abcd";//先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"";
String str2 = new String("abcd");//堆中创建一个新的对象
String str3 = new String("abcd");//堆中创建一个新的对象
System.out.println(str1==str2);//false
System.out.println(str2==str3);//false
上述两种不同的创建方法的区别:
- 第一种方式是在常量池中拿对象
-
String 类型的常量池
String
类型的常量池比较特殊,它有 2 中使用方法 直接使用双引用“”声明出来的
String
对象会直接存储在常量池中- 如果不是用双引号声明的
String
对象,可以使用String
提供的intern()
方法
String.intern()
方法是一个 Native 方法,它的作用是:如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则先在常量池中记录此字符串的引用,然后返回该引用
String s1 = new String("计算机");
String s2 = s1.intern();
String s3 = "计算机";
System.out.println(s2);//计算机
System.out.println(s1 == s2);//false,因为一个是堆内存中的 String 对象一个是常量池中的 String 对象,
System.out.println(s3 == s2);//true,因为两个都是常量池中的 String 对象
String s1 = new String(“abc”) 语句创建了几个字符串对象?
将创建 1 或 2 个字符串对象。如果常量池中已经存在字符串常量 “abc”,则只会在堆内存中创建一个字符串常量“abc”;如果没有,那么它将首先在常量池中创建,然后再在堆内存中创建,所以会创建 2 个对象
String 为什么是不可变的?
String
类内部实现使用 final
关键字修饰字符数组,被 final
关键字修饰的变量为常量。常量具有不可变特性,故 String
是不可变的
// Java 9 之前
private final char[] value;
// Java 9 之后
private final byte[] value;
String、StringBuilder 和 StringBuffer 有什么区别?
- 在代码实现上:
StringBuilder
和StringBuffer
有共同的父类AbstractStringBuilder
。StringBuilder
和StringBuffer
实现代码基本一致,而StringBuffer
对方法加了同步锁或对调用的方法加了同步锁,StringBuffer
是线程安全的 - 在性能上:
String
类是不可变类,每次对String
操作都会生成一个新的对象,然后将指针指向新对象,原对象在内存中等待回收StringBuilder
和StringBuffer
操作原理类似,每次操作都是对原对象本身进行操作,而不是生成新的对象、也不会改变对象引用。区别在于,StringBuilder
不保证线程安全,而StringBuffer
保证线程安全;为此StringBuilder
性能略优于StringBuffer
综上对比,使用时仅有少量操作时选择 String
;单线程或不强调线程安全时选择 StringBuilder
;而多线程或强制线程安全时使用 StringBuffer