String对象的不可变原因,String对象的内存布局,及String对象之间的执行==
,equals
,+
运算时的分析。
Author: Msuenb
Date: 2023-02-16
java.lang.String
类代表字符串。String 对象用于保存字符串,也就是一组字符序列。字符串是常量,它们的值在创建后不能更改。
String 的特点
-
String 是 final 类,不能被其他的类继承
-
String 类实现了 Comparable 接口,String 对象可以比较大小
-
String 对象内部是用字符数组保存字符串内容的
JDK9之前是
private final char value[]
数组,JDK9之后是 byte[] 数组"hello"
等效于char[] value = {'h', 'e', 'l', 'l', 'o'}
-
String 对象是不可变对象,一旦进行修改,就会产生新对象
注意:
- String 类的 char[] value 数组是 final 修饰的,说明 value 数组地址不可变,不是数组元素不可变
- 由于 value 数组是 private 的,所以在 String 类外无法直接修改 value 数组的元素值(除非用反射)
- String 类中的方法涉及到 value 数组长度变化,或修改元素值,都是用用新对象来表示修改后内容
final char[] value = {'h', 'e', 'l', 'l', 'o'}; // value = new char[5]; // value 的地址不可修改 value[0] = 'H'; // 可以修改 value 元素值 String str = "hello"; str = "world"; // 不会修改 "hello", 会在池中创建一个新的String对象接受 "world" str指向这个新对象 共会创建两个对象
-
String 对象不可变,可以共享(节省内存)。Java 中把需要共享的字符串常量对象存放在常量池中。
String s1 = "hello"; String s2 = "hello"; System.out.println(s1 == s2); // true // 内存中只有一个 "hello" 对象被创建,同时被s1和s2共享
创建String对象
总的来说,String 对象的创建方式有两种:直接赋值 和 使用构造器,这两种方式的机制是不一样的
- 直接赋值:
String str1 = "hello";
- 先从常量池中查看是否有 "hello" 数据空间,如果有,直接指向;如果没有,重新创建,然后指向。
- str1 最终指向的是常量池的空间地址
- 调用构造器:
String str2 = new String("hello")
- 先在堆中创建空间,里面维护了
char[] value
属性。 - 如果常量池中有 "hello" 数据空间,value 直接指向 "hello" 空间;如果没有,重新创建,然后指向。
- str2 最终指向的是堆中的空间地址;value 才是指向常量池的空间地址
- 先在堆中创建空间,里面维护了
String 内存分析
针对下面两行代码分析 String 对象的创建过程和内存布局
String str1 = "hello";
String str2 = new String("hello");
String 内存布局:
String 对象创建过程:
String str1 = "hello";
- 先去查看常量池中是否有 "hello" 数据空间,没有,创建 "hello" 数据空间
- 将 "hello" 数据空间的地址返回给 String 对象引用 str
String str2 = new String("hello");
3. 先在堆中开辟空间,里面维护了 value 属性,str2 指向这片空间
4. 检查常量池中是否有 "hello" 数据空间,有,value 直接指向 "hello" 数据空间
哪些字符串对象地址放入字符串常量池:
需要共享的字符串地址记录到字符串常量池的table表中,不需要共享的字符串对象其地址值不需要记录到字符串常量池的table表中。
除了以下2种,其他的都不放入字符串常量池:
1. "..." 字符串字面量
2. 字符串对象.intern()的结果
其他:
1. 直接new
2. valueOf,copyValueOf等
3. 字符串对象拼接:concat拼接 以及 +左右两边出现 非字符串字面量拼接
4. toUpperCase,toLowerCase,substring,repalce等各种String方法得到的字符串
其实下面这些方式,本质都是新new的,其地址都是指向堆空间。
String对象的创建形式
String对象的创建方式有两种,使用其它形式生成String对象,像valueOf()
或toString()
,它们在底层也是调用String的构造方法
-
直接赋值
String str = "hello";
-
构造方法
用于创建 String 对象的构造方法有很多,比较常用的有以下几个:
public String()
:其表示创建空字符序列。String(String original)
:其表示创建一个与参数相同的字符序列;也即新创建的字符串是该参数字符串的副本。public String(char[] value)
:通过当前参数中的字符数组来构造新的String。public String(byte[] bytes)
:通过使用默认字符集解码当前参数中的字节数组来构造新的String。也可以指定字符集
String str1 = new String(); // 创建一个空字符串 String str2 = new String("hello"); System.out.println(str1.equals("")); // true char[] value = {'h', 'e', 'l', 'l', 'o'}; // 字符数组 String str3 = new String(value); byte[] bytes = {104, 101, 108, 108, 111}; // 字节数组 String str5 = new String(bytes); // hello
-
valueOf
和copyValueOf
方法:-
static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
-
static String valueOf(char[] data) : 返回指定数组中表示该字符序列的 String
-
static String valueOf(xx value):xx支持各种数据类型,返回各种数据类型的value参数的字符串表示形式。
char[] data = {'h','e','l','l','o','j','a','v','a'}; String s1 = String
-