文章導航:
1.描述:
被真相矇蔽,是一件很痛苦的事。
希望讀者依然本着懷疑的態度,帶着求知慾到達理想彼岸。
2.代碼案例(JDK8):
public class TopicString {
public static void main(String[] args) {
String s1 = "1";
String s2 = new String("1");
String s3 = "1" + "2" + "3";
String s4 = "123";
String s5 = "1" + "3" + new String("1") + "4";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println(s3 == s4);
System.out.println(s3.equals(s4));
}
}
3.提出問題:
- 輸出結果(不許偷偷的運行代碼哦)
- s1,s2,s3,s4,s5各創建了幾個對象?分析原因?
4.分析代碼:
問1:如何分析以上代碼?心中沒有思路...沒有底氣!!
答1:java文件被javac編譯,然後交給jvm,jvm調用機器指令和計算底層對話,並返回結果(總體探索思路)
問2:javac做了什麼事?產生的結果是什麼?
答2:java文件被編譯後,產生一個我們熟悉的class文件(你真的熟悉class文件),那看看class信息吧
a.案例代碼class文件主要操作指令信息:
- 查看信息命令:
javap -v java文件名
或者javap -c java文件名
這裏筆者用java -v輸出的信息
。解讀class文件,筆者後續會寫一篇解讀class信息的文章。暫時看不懂沒有關係,主要部分都要圖解。
F:\install-dev\java8\bin\javap.exe -v com.lsz.interview.questions.TopicString
Classfile /F:/project-my/pro-github/books/big_talk_java_performance/out/production/big_talk_java_performance/com/lsz/interview/questions/TopicString.class
Last modified 2020-4-28; size 1186 bytes
MD5 checksum 2b15426e9c5edaf119e013adbebdc723
Compiled from "TopicString.java"
public class com.lsz.interview.questions.TopicString
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #16.#40 // java/lang/Object."<init>":()V
#2 = String #41 // 1
#3 = Class #42 // java/lang/String
#4 = Methodref #3.#43 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = String #44 // 123
#6 = Class #45 // java/lang/StringBuilder
#7 = Methodref #6.#40 // java/lang/StringBuilder."<init>":()V
#8 = String #46 // 13
#9 = Methodref #6.#47 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#10 = String #48 // 4
#11 = Methodref #6.#49 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#12 = Fieldref #50.#51 // java/lang/System.out:Ljava/io/PrintStream;
#13 = Methodref #52.#53 // java/io/PrintStream.println:(Z)V
#14 = Methodref #3.#54 // java/lang/String.equals:(Ljava/lang/Object;)Z
#15 = Class #55 // com/lsz/interview/questions/TopicString
#16 = Class #56 // java/lang/Object
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 LocalVariableTable
#22 = Utf8 this
#23 = Utf8 Lcom/lsz/interview/questions/TopicString;
#24 = Utf8 main
#25 = Utf8 ([Ljava/lang/String;)V
#26 = Utf8 args
#27 = Utf8 [Ljava/lang/String;
#28 = Utf8 s1
#29 = Utf8 Ljava/lang/String;
#30 = Utf8 s2
#31 = Utf8 s3
#32 = Utf8 s4
#33 = Utf8 s5
#34 = Utf8 StackMapTable
#35 = Class #27 // "[Ljava/lang/String;"
#36 = Class #42 // java/lang/String
#37 = Class #57 // java/io/PrintStream
#38 = Utf8 SourceFile
#39 = Utf8 TopicString.java
#40 = NameAndType #17:#18 // "<init>":()V
#41 = Utf8 1
#42 = Utf8 java/lang/String
#43 = NameAndType #17:#58 // "<init>":(Ljava/lang/String;)V
#44 = Utf8 123
#45 = Utf8 java/lang/StringBuilder
#46 = Utf8 13
#47 = NameAndType #59:#60 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#48 = Utf8 4
#49 = NameAndType #61:#62 // toString:()Ljava/lang/String;
#50 = Class #63 // java/lang/System
#51 = NameAndType #64:#65 // out:Ljava/io/PrintStream;
#52 = Class #57 // java/io/PrintStream
#53 = NameAndType #66:#67 // println:(Z)V
#54 = NameAndType #68:#69 // equals:(Ljava/lang/Object;)Z
#55 = Utf8 com/lsz/interview/questions/TopicString
#56 = Utf8 java/lang/Object
#57 = Utf8 java/io/PrintStream
#58 = Utf8 (Ljava/lang/String;)V
#59 = Utf8 append
#60 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#61 = Utf8 toString
#62 = Utf8 ()Ljava/lang/String;
#63 = Utf8 java/lang/System
#64 = Utf8 out
#65 = Utf8 Ljava/io/PrintStream;
#66 = Utf8 println
#67 = Utf8 (Z)V
#68 = Utf8 equals
#69 = Utf8 (Ljava/lang/Object;)Z
{
public com.lsz.interview.questions.TopicString();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/lsz/interview/questions/TopicString;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=6, args_size=1
0: ldc #2 // String 1
2: astore_1
3: new #3 // class java/lang/String
6: dup
7: ldc #2 // String 1
9: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
12: astore_2
13: ldc #5 // String 123
15: astore_3
16: ldc #5 // String 123
18: astore 4
20: new #6 // class java/lang/StringBuilder
23: dup
24: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
27: ldc #8 // String 13
29: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: new #3 // class java/lang/String
35: dup
36: ldc #2 // String 1
38: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
41: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: ldc #10 // String 4
46: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: astore 5
54: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
57: aload_1
58: aload_2
59: if_acmpne 66
62: iconst_1
63: goto 67
66: iconst_0
67: invokevirtual #13 // Method java/io/PrintStream.println:(Z)V
70: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
73: aload_1
74: aload_2
75: invokevirtual #14 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
78: invokevirtual #13 // Method java/io/PrintStream.println:(Z)V
81: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
84: aload_3
85: aload 4
87: if_acmpne 94
90: iconst_1
91: goto 95
94: iconst_0
95: invokevirtual #13 // Method java/io/PrintStream.println:(Z)V
98: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
101: aload_3
102: aload 4
104: invokevirtual #14 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
107: invokevirtual #13 // Method java/io/PrintStream.println:(Z)V
110: return
指令結束.....
省略部分代碼
}
SourceFile: "TopicString.java"
分析得出初始圖:
b.main方法中的指令解讀::
編譯之後,class文件只是一個普通本地文件,存在C盤或者D盤而已,和txt、excel、word一樣都是一個"靜態文件"。因此java文件,
接下會進行class類加載,將靜態文件class加載到內存中,jvm讀取指令然後操作指令
# 表示:多少號,例如:#2表示常量池中的2號位置
常量池數據類型:boolean、byte、short、int、char、float、reference或者returnAddress
第一組指令:String s1 = "1";
- 指令:
ldc
將常量池中的數據類型推入棧頂 - 指令:
astore_1
將棧頂類型值保存到局部變量表slot=1位置
將常量池中 #2 位置的值推到棧頂,從反編譯得知:#2 最終的結果是:String類型的’1’,但是常量池中的數據類型沒有String啊,是的!因此這裏保存的是// 執行指令 0: ldc #2 // String 1 2: astore_1 // 對應源碼 String s1 = "1";
引用地址
,也就是常量池數據類型中的reference類型
,出棧。執行指令後:
第二組指令:String s2 = new String("1");
-
指令:
new
創建新的對象實例,並將引用(reference)地址
壓入棧 -
指令:
dup
複製棧頂數值,並將複製值壓入棧頂 -
指令:
ldc
將常量池中的數據類型推入棧頂 -
指令:
invokespecial
編譯時調用方法,初始化對象值,出棧。 -
指令:
astore_2
將棧頂類型保存到局部變量表slot=2的位置,出棧。// 執行指令 3: new #3 // class java/lang/String 6: dup 7: ldc #2 // String 1 9: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 12: astore_2 // 對應的源碼 String s2 = new String("1");
1.執行
3: new #3
指令和dup
指令
2.執行7: ldc #2
指令和9: invokespecial #4
指令:
3.執行
12: astore_2
指令:
第三組指令:String s3 = "1" + "2" + "3";
和String s4 = "123";
-
類比上邊的參數解讀
// 執行指令 13: ldc #5 // String 123 15: astore_3 16: ldc #5 // String 123 18: astore 4 // 對應的源碼 String s3 = "1" + "2" + "3"; String s4 = "123";
問:爲什麼s3和s4的地址一樣?
答:因爲java編譯器在編譯的時候優化了,這個概念稱爲:常量摺疊,感興趣的童鞋可以去看javac源碼
第四組指令:String s5 = "1" + "3" + new String("1") + "4";
-
類比上邊的參數解讀
// 執行指令 20: new #6 // class java/lang/StringBuilder 23: dup 24: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 27: ldc #8 // String 13 29: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: new #3 // class java/lang/String 35: dup 36: ldc #2 // String 1 38: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 41: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 44: ldc #10 // String 4 46: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 49: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 52: astore 5 // 對應的源碼 String s5 = "1" + "3" + new String("1") + "4";
5.總圖:
5.留給讀者的問題:
輸出的結果?(現在可以運行看看了)
s1,s2,s3,s4,s5各創建了幾個對象?
...有答案了麼?
6.結語:
不要輕易被真相所矇蔽,也不要輕易被別人矇蔽,更不要輕易被自己矇蔽!簡單的東西真的簡單麼?
有問題可以私信或者評論下方留言哦!! 希望對你有幫助!!!