不少時候咱們在編寫Java代碼時,判斷和猜想代碼問題時主要是經過運行結果來獲得答案,本博文主要是想經過Java字節碼的方式來進一步求證咱們已知的東西。這裏沒有對Java字節碼知識進行介紹,若是想了解更多的Java字節碼或對其感興趣的朋友能夠先閱讀字節碼基礎:JVM字節碼初探。 html
String字面量能夠經過'=='判斷兩個字符串是否相同,是由於你們都知道'=='是用來判斷兩個對象的值引用地址是否一致,兩個值同樣的字符串字面量定義是否指向同一個值內存地址呢?答案是確定的。 java
package com.jaffa.test.string; public class ConstPoolTest { public static void main(String[] args){ String str1 = "strVal_1"; String str2 = "strVal_1"; //print str1==str2 is true System.out.printf("str1==str2 is %b",str1==str2); } }
代碼中聲明瞭str1和str2的字面量值都爲strVal_1,而且打印出str1==str2爲true,說明兩個str1和str2變量同時指向同一個字符串常量值的內存地址,下面經過Java字節碼來驗證這個結果。 shell
在命令行咱們經過javap工具來查看一個class文件的字節碼。 工具
javap -v com.jaffa.test.string.ConstPoolTest
在Constant pool列表中看到#16爲一個String類型並值指向#17,而#17是一個utf8字符集編碼值爲strVal_1,因此#16和#17最終表達就是在常量池中有個String類型值爲strVal_1的常量數據。 編碼
那接下來須要確認str1和str2兩個變量值是否都是指向#16呢? .net
//ldc表示將一個常量加載到操做數棧 0: ldc #16 //將#16對應的常量值加載到操做數棧中 2: astore_1 //將當前操做數棧中賦於變量1,即str1 3: ldc #16 //再次將#16對應的常量值加載到操做數棧中 5: astore_2 //將當前操做數棧中賦於變量2,即str2
從上面字節碼執行來看,str1和str2都是被賦於同一個常量值,由此能夠得出兩個變動指向同一個內存地址。 命令行
經過一樣的方式,咱們來看一下若是是非字面量的狀況會是怎麼樣的,Java代碼以下: code
package com.jaffa.test.string; public class ConstPoolTest { public static void main(String[] args){ String str1 = "strVal_1"; String str2 = new String("strVal_1"); System.out.printf("str1==str2 is %b",str1==str2); } }
上面代碼輸出結果爲false,經過javap查看發現str2變量的字節碼指令發生了變化,以下現兩截圖: htm
//ldc表示將一個常量加載到操做數棧 0: ldc #16 //將#16對應的常量值加載到操做數棧中 2: astore_1 //將當前操做數棧中賦於變量1,即str1 3: new #18 //建立了一個類實例,#18指向一個實例類型String 6: dup //配合上行完成棧值複製操做指令,將new從新壓入棧頂 7: ldc #16 //將#16對應的常量值加載到操做數棧中 9: invokespecial #20 //調用String實例初始化方法,並將#16輸入 12: astore_2 //將new出來的實例賦於變量2,即str2
這時str2變量是建立一個新的內存地址,而非直接指向#16常量內存地址,因此str1==str2的結果爲false。同時能夠看到經過new String()帶來看更多的字節碼指令操做,運行上花費了更多的系統資源。 對象