String类
String被声明为final,因此它不可被继承
在 Java 8 中,String 内部使用 char 数组存储数据。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
//...
}
在 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 不可变。
不可变的好处
- 可以缓存 hash 值
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
- String Pool的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
- 安全性
String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。
- 线程安全
String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
String对象的两种创建方式
//第一种方式是在常量池中拿对象
String str1 = "abcd";
//第二种方式是直接在堆内存空间创建一个新的对象
String str2 = new String("abcd");
System.out.println(str1==str2);//false
只要使用new方法,便需要创建新的对象。
new String(“abc”)
创建了两个字符串对象:
-
编译时期会再String Pool中创建一个字符串对象,指向这个“abc”字符串字面量
-
使用new会在堆中创建一个字符串对象,将String Pool中的字符串对象作为String构造函数的参数
-
根据String 构造函数的源码可知,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组
public String(String original) { this.value = original.value; this.hash = original.hash; }
-
-
字符串常量"abc"在编译期就已经确定放入常量池,而 Java 堆上的"abc"是在运行期初始化阶段才确定
字符串常量池
- 字符串常量池(String Pool)保存着所有字符串字面量,这些字面量在编译时期就确定。
- 在运行过程中,使用 String 的 intern() 方法将字符串添加到 String Pool 中。
主要使用方法有两种:
- 直接使用双引号声明出来的String对象会直接存储在常量池中
- 如果不是用双引号声明的String对象,可以使用String提供的intern方法
- String.intern() 是一个Native方法,它的作用是:
- 如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用
- 如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用
- String.intern() 是一个Native方法,它的作用是:
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对
- 在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。
- 而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
String字符串拼接
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";//常量池中的对象
String str4 = str1 + str2; //在堆上创建的新的对象
String str5 = "string";//常量池中的对象
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false
String StringBuffer StringBuilder
- String类中使用final关键字修饰字符数组保存字符串
private final char value[]
,所以String对象时不可变的 - StringBuilder、StringBuffer都继承自
AbstractStringBuilder
类,在AbstractStringBuilder
使用char[] value
保存字符串,所以可变- StringBuffer对方法加了同步锁或者对调用的方法加了同步锁