JAVA String a = "abc" 中发生了什么

和上次int a = 1 后续
测试代码 javac TestCode.java

public class TestCode { public static void main(String[] args) { // 此处写aaaa是便于看字节码 String aaaa = "bbbb"; aaaa = "cccc"; } }

反编译 javap -v -p -l TestCode(l是小写的L)
注解:javap是jdk自带的反解析工具。作用是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
Classfile xxxxx/com/code/baseCode/TestCode.class Last modified 2021-11-22; size 475 bytes MD5 checksum 0a108c035194f620555926cf7d113c8b Compiled from "TestCode.java" public class com.code.baseCode.TestCode minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref#5.#21// java/lang/Object."":()V #2 = String#22// bbbb #3 = String#23// cccc #4 = Class#24// com/code/baseCode/TestCode #5 = Class#25// java/lang/Object #6 = Utf8 #7 = Utf8()V #8 = Utf8Code #9 = Utf8LineNumberTable #10 = Utf8LocalVariableTable #11 = Utf8this #12 = Utf8Lcom/code/baseCode/TestCode; #13 = Utf8main #14 = Utf8([Ljava/lang/String; )V #15 = Utf8args #16 = Utf8[Ljava/lang/String; #17 = Utf8aaaa #18 = Utf8Ljava/lang/String; #19 = Utf8SourceFile #20 = Utf8TestCode.java #21 = NameAndType#6:#7// "":()V #22 = Utf8bbbb #23 = Utf8cccc #24 = Utf8com/code/baseCode/TestCode #25 = Utf8java/lang/Object { public com.code.baseCode.TestCode(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/code/baseCode/TestCode; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String; )V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=2, args_size=1 0: ldc#2// String bbbb 2: astore_1 3: ldc#3// String cccc 5: astore_1 6: return LineNumberTable: line 11: 0 line 12: 3 line 55: 6 LocalVariableTable: StartLengthSlotNameSignature 070args[Ljava/lang/String; 341aaaaLjava/lang/String; } SourceFile: "TestCode.java"

反编译后,会发现aaaa存在于Constant pool(常量池)和LocalVariableTable(本地变量表)中
同时发现和int a = 1的区别在于字符串“bbbb”和“cccc”也会在常量池中
JVM中常量池的划分,请看上篇文章
图片解释 java1.8的内存布局
JAVA String a = "abc" 中发生了什么
文章图片

在编译后内存情况
JAVA String a = "abc" 中发生了什么
文章图片

运行时发生情况
如何查看Java bytecode指令?
https://en.wikipedia.org/wiki...
http://gityuan.com/2015/10/24...
其中java byte code为
0: ldc#2// String bbbb 2: astore_1 3: ldc#3// String cccc 5: astore_1

ldc:push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, or a dynamically-computed constant) onto the stack
意思就是:int、float或String型等常量从常量池推送至栈顶
astore_1:store a reference into local variable 1
意思就是将:一个引用放入到局部变量1中(也就是aaaa中)
ldc #2 JAVA String a = "abc" 中发生了什么
文章图片

astore_1 JAVA String a = "abc" 中发生了什么
文章图片

ldc #3 JAVA String a = "abc" 中发生了什么
文章图片

astore_1 JAVA String a = "abc" 中发生了什么
文章图片

图片解释:用new String()创建 将代码改成如下:
public class TestCode { public static void main(String[] args) { String aaaa = new String("bbbb"); aaaa = new String("cccc"); } }

则会发现常量池多了一些东西,并且执行指令多了。
其中执行指令如下
0: new#2// class java/lang/String 3: dup 4: ldc#3// String bbbb 6: invokespecial #4// Method java/lang/String."":(Ljava/lang/String; )V 9: astore_1 10: new#2// class java/lang/String 13: dup 14: ldc#5// String cccc 16: invokespecial #4// Method java/lang/String."":(Ljava/lang/String; )V 19: astore_1

new:创建类实例
dup:复制数值,并压入栈顶
ldc:从常量池推送至栈顶
【JAVA String a = "abc" 中发生了什么】invokespecial:调用特殊实例方法(包括实例初始化方法、父类方法)
astore_1:一个引用放入到局部变量1中(也就是aaaa中)
常量池如下
Constant pool: #1 = Methodref#7.#23// java/lang/Object."":()V #2 = Class#24// java/lang/String #3 = String#25// bbbb #4 = Methodref#2.#26// java/lang/String."":(Ljava/lang/String; )V #5 = String#27// cccc #6 = Class#28// com/code/baseCode/TestCode #7 = Class#29// java/lang/Object #8 = Utf8 #9 = Utf8()V #10 = Utf8Code #11 = Utf8LineNumberTable #12 = Utf8LocalVariableTable #13 = Utf8this #14 = Utf8Lcom/code/baseCode/TestCode; #15 = Utf8main #16 = Utf8([Ljava/lang/String; )V #17 = Utf8args #18 = Utf8[Ljava/lang/String; #19 = Utf8aaaa #20 = Utf8Ljava/lang/String; #21 = Utf8SourceFile #22 = Utf8TestCode.java #23 = NameAndType#8:#9// "":()V #24 = Utf8java/lang/String #25 = Utf8bbbb #26 = NameAndType#8:#30// "":(Ljava/lang/String; )V #27 = Utf8cccc #28 = Utf8com/code/baseCode/TestCode #29 = Utf8java/lang/Object #30 = Utf8(Ljava/lang/String; )V

new #2
JAVA String a = "abc" 中发生了什么
文章图片

dup
JAVA String a = "abc" 中发生了什么
文章图片

ldc #3
JAVA String a = "abc" 中发生了什么
文章图片

invokespecial #4
JAVA String a = "abc" 中发生了什么
文章图片

astore_1
JAVA String a = "abc" 中发生了什么
文章图片

后续的操作一样,因此会出现
String a = new String("bbbb"); String b = new String("bbbb"); System.out.println(a == b); // false

面试题 做下面的题是,需要知道几个知识点
1.编辑时,编译器会对确切的值进行优化:
比如String a = "a" + "b"; -> 比如String a = "ab";
final String a = "a"; String b = "a" + a; -> String b = "aa";
2.字符串变量相加会创建StringBuilder调用append方法,然后StringBuilder转换成new String
3.intern:Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。
String s11 = "a"; String s22 = "bc"; String s1 = "abc"; String s2 = "a" + "bc"; String s3 = new String("a") + "bc"; String s4 = new String("a") + new String("bc"); String s5 = new String("abc"); String s6 = s11 + s22; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s1 == s4); System.out.println(s1 == s5); System.out.println(s1 == s6); System.out.println(s3 == s4); System.out.println(s4 == s5); System.out.println(s1 == s5.intern()); System.out.println(s1 == s6.intern()); System.out.println(s1.equals(s5)); System.out.println(s1.equals(s6));

答案解释:https://zhuanlan.zhihu.com/p/...
结尾 之前出了int类型的情况,上述是String类型的情况,后续出包装类型的情况

    推荐阅读