這個問題相信每個學習java的同學都不陌生,作為一個經典的面試題,到現在作業這么多年了我真是認為挺操蛋的一個問題,在網上到現在你仍然可以看見很多討論這個問題的人,其中不乏作業很多年的人都有爭論,我認為還是有必要來說一說這個問題的,
從方法區說起
常量池存在于方法區,而方法區在jdk1.7版本前后改變比較大,所以還是先來說說方法區的演變,
在jdk1.7版本之前,常量池存在于方法區,方法區是堆的一個邏輯部分,他有一個名字叫做非堆,

1.7版本把字串常量池放到了堆中,

而在1.8以后,則是移除了永久代,方法區概念保留,方法區的實作改為了元空間,常量池還是在堆中,

為什么要說方法區的改變,只是為了文章接下來的內容不會由于JDK的版本而產生分歧,接下來內容都會以jdk1.8版本作為基礎來討論,
String s = new String(“xyz”);
先來一段代碼
public class Test {
public static void main(String[] args) {
String s = "xyz";
}
}
接著我們javac編譯代碼,然后用javap來反編譯,執行javap -c Test

從結果來看,ldc命令在常量池中創建了一個"xyz"的物件,然后把他推至運算元堆疊頂,然后astore保存到區域變數,return回傳,
接著看第二段面試題中的代碼
public class Test {
public static void main(String[] args) {
String s = new String("xyz");
}
}
同樣反編譯分析

很明顯,我們看到new 創建了一個String物件,同時ldc在常量池中創建了"xyz"字串物件,之后invokespecial執行建構式,astore_1賦值,return回傳,
通過以上兩個例子,可以知道String s = new String(“xyz”); 創建了2個物件,而有些答案說的3個物件,則是把參考s也算作一個物件,
還有答案說xyz存在就創建了2個,不存在就創建了3個(包含參考s),再來測驗一下,
public class Test {
public static void main(String[] args) {
String s = "xyz";
String s2 = new String("xyz");
}
}

從這里,很明顯的發現這就是我們例子1和2的一個結合,但是注意兩次ldc后面的**#2**,#號代表著索引,說明第二次new String(“xyz”)的時候并沒有重新創建xyz物件,
一些常見的指令助記符含義:
- nop, 什么都不做,
- aconst_null,將 null 推送至堆疊頂,
- iconst_i(變數數字),將 int 型 i 推送至堆疊頂,同理有lconst_0,fconst_0這種你應該知道什么意思了
- ldc,將 int,float 或 String 型常量值從常量池中推送至堆疊頂,
- iload,將指定的 int 型區域變數推送至堆疊頂,
- istore,將堆疊頂 int 型數值存入指定區域變數,同理astore_i代表將堆疊頂參考型數值存入第i個區域變數,
- dup,復制堆疊頂數值并將復制值壓入堆疊頂,
- invokevirtual,呼叫實體方法,
- invokespecial,呼叫超類構造方法,實體初始化方法,私有方法,
- invokestatic,呼叫靜態方法,
- invokeinterface,呼叫介面方法,
- invokedynamic,呼叫動態鏈接方法,
- new,創建一個物件,并將其參考值壓入堆疊頂,
總結
到底創建了幾個物件呢?
-
如果xyz不存在,參考算物件的話,那就是3個
-
如果xyz不存在,參考不算物件的話,那就是2個
-
如果xyz存在,參考算物件的話,那就是2個
-
如果xyz存在,參考不算物件的話,那就是1個
當然,我認為參考肯定是不算物件的,最終答案應該是1或者2個,這個面試題說實話不應該出現在初級面試題里,
另外,如果你不看不懂反編譯后的位元組碼指令,關注公眾號回復關鍵字111可獲取《Java虛擬機規范》電子書,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/131727.html
標籤:AI
上一篇:mybatis-plus代碼生成器:mybatis-plus自動生成controller、service、dao、mapper、pojo代碼,可靈活配置生成路徑!!!程式猿的福音!!!
