@@ -706,9 +706,14 @@ System.out.println(aa==bb); // true
706
706
707
707
### String s1 = new String (" abc" );这句话创建了几个字符串对象?
708
708
709
- 会创建 1 或 2 个字符串对象。
709
+ 先说答案: 会创建 1 或 2 个字符串对象。
710
710
711
- 1 、如果字符串常量池中不存在字符串对象 “abc”,那么它首先会在字符串常量池中创建字符串对象 " abc" ,然后在堆内存中再创建其中一个字符串对象 " abc"
711
+ 1. 字符串常量池中不存在 " abc" :会创建 2 个 字符串对象。一个在字符串常量池中,由 `ldc` 指令触发创建。一个在堆中,由 `new String ()` 创建,并使用常量池中的 " abc" 进行初始化。
712
+ 2. 字符串常量池中已存在 " abc" :会创建 1 个 字符串对象。该对象在堆中,由 `new String ()` 创建,并使用常量池中的 " abc" 进行初始化。
713
+
714
+ 下面开始详细分析。
715
+
716
+ 1 、如果字符串常量池中不存在字符串对象 “abc”,那么它首先会在字符串常量池中创建字符串对象 " abc" ,然后在堆内存中再创建其中一个字符串对象 " abc" 。
712
717
713
718
示例代码(JDK 1.8 ):
714
719
@@ -718,9 +723,33 @@ String s1 = new String("abc");
718
723
719
724
对应的字节码:
720
725
721
- ! [](https: // oss.javaguide.cn/github/javaguide/open-source-project/image-20220413175809959.png)
726
+ ```java
727
+ // 在堆内存中分配一个尚未初始化的 String 对象。
728
+ // #2 是常量池中的一个符号引用,指向 java/lang/String 类。
729
+ // 在类加载的解析阶段,这个符号引用会被解析成直接引用,即指向实际的 java/lang/String 类。
730
+ 0 new #2 <java/lang/String>
731
+ // 复制栈顶的 String 对象引用,为后续的构造函数调用做准备。
732
+ // 此时操作数栈中有两个相同的对象引用:一个用于传递给构造函数,另一个用于保持对新对象的引用,后续将其存储到局部变量表。
733
+ 3 dup
734
+ // JVM 先检查字符串常量池中是否存在 "abc"。
735
+ // 如果常量池中已存在 "abc",则直接返回该字符串的引用;
736
+ // 如果常量池中不存在 "abc",则 JVM 会在常量池中创建该字符串字面量并返回它的引用。
737
+ // 这个引用被压入操作数栈,用作构造函数的参数。
738
+ 4 ldc #3 <abc>
739
+ // 调用构造方法,使用从常量池中加载的 "abc" 初始化堆中的 String 对象
740
+ // 新的 String 对象将包含与常量池中的 "abc" 相同的内容,但它是一个独立的对象,存储于堆中。
741
+ 6 invokespecial #4 <java/lang/String .<init> : (Ljava / lang/ String ;)V >
742
+ // 将堆中的 String 对象引用存储到局部变量表
743
+ 9 astore_1
744
+ // 返回,结束方法
745
+ 10 return
746
+ ```
722
747
723
- `ldc (load constant)` 指令的作用是从常量池中加载常量,包括字符串常量、整数常量、浮点数常量、或者类引用。这里用于判断字符串常量池中是否保存了对应的字符串对象,如果保存了的话会将它的引用加载到操作数栈,如果没有保存的话,会在字符串常量池中创建对应的字符串对象,并将其引用加载到操作数栈中。
748
+ `ldc (load constant)` 指令的确是从常量池中加载各种类型的常量,包括字符串常量、整数常量、浮点数常量,甚至类引用等。对于字符串常量,`ldc` 指令的行为如下:
749
+
750
+ 1. ** 从常量池加载字符串** :`ldc` 首先检查字符串常量池中是否已经有内容相同的字符串对象。
751
+ 2. ** 复用已有字符串对象** :如果字符串常量池中已经存在内容相同的字符串对象,`ldc` 会将该对象的引用加载到操作数栈上。
752
+ 3. ** 没有则创建新对象并加入常量池** :如果字符串常量池中没有相同内容的字符串对象,JVM 会在常量池中创建一个新的字符串对象,并将其引用加载到操作数栈中。
724
753
725
754
2 、如果字符串常量池中已存在字符串对象“abc”,则只会在堆中创建 1 个字符串对象“abc”。
726
755
@@ -735,7 +764,16 @@ String s2 = new String("abc");
735
764
736
765
对应的字节码:
737
766
738
- ! [](https: // oss.javaguide.cn/github/javaguide/open-source-project/image-20220413180021072.png)
767
+ ```java
768
+ 0 ldc #2 < abc>
769
+ 2 astore_1
770
+ 3 new #3 <java/lang/String>
771
+ 6 dup
772
+ 7 ldc #2 <abc>
773
+ 9 invokespecial #4 <java/lang/String .<init> : (Ljava / lang/ String ;)V >
774
+ 12 astore_2
775
+ 13 return
776
+ ```
739
777
740
778
这里就不对上面的字节码进行详细注释了,7 这个位置的 `ldc` 命令不会在堆中创建新的字符串对象“abc”,这是因为 0 这个位置已经执行了一次 `ldc` 命令,已经在堆中创建过一次字符串对象“abc”了。7 这个位置执行 `ldc` 命令会直接返回字符串常量池中字符串对象“abc”对应的引用。
741
779
0 commit comments