共计 4620 个字符,预计需要花费 12 分钟才能阅读完成。
和上次 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."<init>":()V
#2 = String #22 // bbbb
#3 = String #23 // cccc
#4 = Class #24 // com/code/baseCode/TestCode
#5 = Class #25 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 LocalVariableTable
#11 = Utf8 this
#12 = Utf8 Lcom/code/baseCode/TestCode;
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 args
#16 = Utf8 [Ljava/lang/String;
#17 = Utf8 aaaa
#18 = Utf8 Ljava/lang/String;
#19 = Utf8 SourceFile
#20 = Utf8 TestCode.java
#21 = NameAndType #6:#7 // "<init>":()V
#22 = Utf8 bbbb
#23 = Utf8 cccc
#24 = Utf8 com/code/baseCode/TestCode
#25 = Utf8 java/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."<init>":()V
4: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/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:
Start Length Slot Name Signature
0 7 0 args [Ljava/lang/String;
3 4 1 aaaa Ljava/lang/String;
}
SourceFile: "TestCode.java"
反编译后,会发现 aaaa 存在于 Constant pool(常量池)和 LocalVariableTable(本地变量表)中
同时发现和 int a = 1 的区别在于字符串“bbbb”和“cccc”也会在常量池中
JVM 中常量池的划分,请看上篇文章
图片解释
java1.8 的内存布局
在编译后内存状况
运行时产生状况
如何查看 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
astore_1
ldc #3
astore_1
图片解释:用 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."<init>":(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."<init>":(Ljava/lang/String;)V
19: astore_1
new:创立类实例
dup:复制数值,并压入栈顶
ldc:从常量池推送至栈顶
invokespecial:调用非凡实例办法(包含实例初始化办法、父类办法)
astore_1:一个援用放入到局部变量 1 中 (也就是 aaaa 中)
常量池如下
Constant pool:
#1 = Methodref #7.#23 // java/lang/Object."<init>":()V
#2 = Class #24 // java/lang/String
#3 = String #25 // bbbb
#4 = Methodref #2.#26 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = String #27 // cccc
#6 = Class #28 // com/code/baseCode/TestCode
#7 = Class #29 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lcom/code/baseCode/TestCode;
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 aaaa
#20 = Utf8 Ljava/lang/String;
#21 = Utf8 SourceFile
#22 = Utf8 TestCode.java
#23 = NameAndType #8:#9 // "<init>":()V
#24 = Utf8 java/lang/String
#25 = Utf8 bbbb
#26 = NameAndType #8:#30 // "<init>":(Ljava/lang/String;)V
#27 = Utf8 cccc
#28 = Utf8 com/code/baseCode/TestCode
#29 = Utf8 java/lang/Object
#30 = Utf8 (Ljava/lang/String;)V
new #2
dup
ldc #3
invokespecial #4
astore_1
后续的操作一样,因而会呈现
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 类型的状况,后续出包装类型的状况