看看我在基礎(chǔ)數(shù)據(jù)類型方面埋了什么坑
說說看,Java有多少種基本的數(shù)據(jù)類型搜变?多大采缚?
Java有8中基本的數(shù)據(jù)類型,分別是
- byte挠他,占據(jù)1個字節(jié)8位
- char扳抽,占據(jù)2個字節(jié)16位
- short,占據(jù)2個字節(jié)16位
- int殖侵,占據(jù)4個字節(jié)32位
- float贸呢,占據(jù)4個字節(jié)32位
- long,占據(jù)8個字節(jié)64位
- double拢军,占據(jù)8個字節(jié)64位
- boolean楞陷,占據(jù)一個字節(jié)8位
<u>錯,將boolean默認(rèn)為一個字節(jié)基本是所有初學(xué)者的通茉唉。</u>
注意:boolean的大小是未知的固蛾,雖然我們看boolean只有:true、false兩種情況赌渣,可以使用 1 bit 來存儲魏铅,但是實際上沒有明確規(guī)定是1bit,因為因為對虛擬機(jī)來說根本就不存在 boolean 這個類型坚芜。在《Java虛擬機(jī)規(guī)范》中給出了兩種定義览芳,分別是4個字節(jié)和boolean數(shù)組時1個字節(jié)的定義,但是具體還要看虛擬機(jī)實現(xiàn)是否按照規(guī)范來鸿竖,1個字節(jié)沧竟、4個字節(jié)都是有可能的铸敏。這其實是運(yùn)算效率和存儲空間之間的博弈,兩者都非常的重要悟泵。
那Integer這些算什么呢杈笔?
這些算是包裝類型,每個基本類型都有對應(yīng)的包裝類型糕非,基本類型與其對應(yīng)的包裝類型之間的賦值使用自動裝箱與拆箱完成蒙具。比如:
Integer number1 = 2; // 裝箱 調(diào)用了 Integer.valueOf(2)
int number2 = number1; // 拆箱 調(diào)用了 number1.intValue()
記得挺牢的,那 new Integer(1024) 和Integer.valueOf(1024) 有沒有什么區(qū)別呢朽肥?
首先禁筏,new Integer(1024) 每次都會新建一個對象,而Integer.valueOf(1024) 會使用緩沖池中的對象衡招,多次調(diào)用會取得同一個對象的引用篱昔。
我舉個例子:
<u>錯,這是我埋著的坑點始腾,我曾經(jīng)用這一招坑了多個候選人州刽。</u>
注意:Integer.valueOf(1024) 和Integer.valueOf(1024) 缺不等于true,而是false浪箭。Integer.valueOf從緩沖池取的數(shù)值是有大小限制的穗椅,并不是任何數(shù)
我們可以看看valueOf() 的源碼,其實也比較簡單奶栖,就是先判斷值是否在緩存池中房待,如果在的話就直接返回緩沖池的內(nèi)容。
目前我的jdk版本是 8 驼抹,在jdk8中Integer 緩沖池的大小默認(rèn)為 -128~127。
<u>做為一個面試官拜鹤,我很喜歡挖別人回答問題時暴露的細(xì)節(jié)點</u>框冀。你剛剛說到了自動裝箱和拆箱,說說看你的理解敏簿?
編譯器會在自動裝箱過程中調(diào)用 valueOf() 方法明也,因此多個值相同且值在緩存池范圍內(nèi)的 Integer 實例使用自動裝箱來創(chuàng)建,那么就會引用相同的對象惯裕,因此對比的時候會返回true温数。
<u>繼續(xù)往深挖,看看候選人對知識點的掌握有多深蜻势。</u>那說說看你知道的緩沖池有哪些撑刺?
目前基本類型對應(yīng)的緩沖池如下:
- boolean 緩沖池,true and false
- byte緩沖池
- short 緩沖池
- int 緩沖池
- char 緩沖池
因此我們在使用這些基本類型對應(yīng)的包裝類型時握玛,如果該數(shù)值范圍在緩沖池范圍內(nèi)够傍,那么就可以直接使用緩沖池中的對象甫菠。
<u>繼續(xù)挖,看看他有沒有看過緩沖池的源碼冕屯。</u>你說的這些緩沖池的上限下限都是不變的嗎寂诱?還是說可以設(shè)定的?
基本上都是不可變的安聘,不過在 jdk 1.8 中痰洒,Integer 的緩沖池 IntegerCache 很特殊,這個緩沖池的下界是 - 128浴韭,上界默認(rèn)是 127丘喻,但是這個上界是可調(diào)的。我們可以看源碼
在啟動 jvm 的時候囱桨,我們可以通過通過 -XX:AutoBoxCacheMax=<size> 來指定這個緩沖池的大小仓犬,在JVM初始化的時候,這個設(shè)置會設(shè)定一個名為 java.lang.IntegerCache.high 系統(tǒng)屬性舍肠,然后 IntegerCache 初始化的時候就會讀取該系統(tǒng)屬性來決定上界搀继。
總結(jié):上面的坑分別有boolean的大小、緩沖池的大小翠语、自動拆箱和裝箱叽躯、緩沖池的大小是否可變,基本上這幾個坑點可以坑倒百分之六十的候選人肌括,其次是做為一個面試官点骑,我很喜歡挖候選人回答問題的細(xì)節(jié),畢竟深挖可以看得出你是不是真的有料5病:诘巍!
看看我在String方面埋了什么坑
你剛剛說了基本類型了紧索,說說看你對String的了解吧
String 被聲明為 final袁辈,因此它不可被繼承。在 Java 8 中珠漂,String 內(nèi)部使用 char 數(shù)組存儲數(shù)據(jù)晚缩,并且聲明為 final,這意味著 value 數(shù)組初始化之后就不能再引用其它數(shù)組媳危,String 內(nèi)部也沒有改變 value 數(shù)組的方法荞彼,因此可以保證 String 不可變。
<u>繼續(xù)深挖</u> 說說看不可變的好處待笑?
這個問題的回答比較泛鸣皂,可以說的點比較多,大致可以分為:
首先是不可變自然意味著安全,當(dāng)String 作為參數(shù)引用的時候签夭,不可變性可以保證參數(shù)不可變齐邦。
其次是可以緩存 hash 值,實際上第租,我們開發(fā)的時候經(jīng)常會用來當(dāng)做map的key措拇,不可變的特性可以使得 hash 值也不可變,因此只需要進(jìn)行一次計算慎宾。
最后自然是String Pool 的需要丐吓,如果一個 String 對象已經(jīng)被創(chuàng)建過了,那么就會從 String Pool 中取得引用趟据,而自然只有 String 是不可變的券犁,才可能使用 String Pool。如果是可變的汹碱,那么 String Pool也就無法被設(shè)計出來了粘衬。
<u>繼續(xù)深挖</u> 有沒有用過StringBuffer 和 StringBuilder,說說看String, StringBuffer 以及StringBuilder三者的區(qū)別咳促?
首先他們都是被final修飾的類稚新,都是不可被繼承,不過從可變性上來說跪腹,String 我們剛剛說到了褂删,是不可變的,而StringBuffer 和 StringBuilder 可變的冲茸,這是內(nèi)部結(jié)構(gòu)導(dǎo)致的屯阀,StringBuffer 和StringBuilder 內(nèi)部放數(shù)據(jù)的數(shù)組沒有被final修飾。
其次從線程安全方面來說
String 不可變轴术,是線程安全的
StringBuilder 不是線程安全的难衰,因為內(nèi)部并沒有使用任何的安全處理
StringBuffer 是線程安全的,內(nèi)部使用 synchronized 進(jìn)行同步
<u>繼續(xù)挖細(xì)節(jié)點</u>你剛剛有說到String Pool 逗栽,說說看你的理解
String Pool也就是我們經(jīng)常說的字符串常量池召衔,它保存著所有字符串字面量,而且是在編譯時期就確定了祭陷。
String Pool是在編譯時期就確定了,那么請問是否不可變的呢趣席?
是的兵志。
<u>錯,所有初學(xué)者都會犯的一個問題宣肚,那就是忽略了String.intern的存在想罕,我經(jīng)常用這個坑點來區(qū)分初學(xué)者和中級水平的候選人的區(qū)別!!按价!</u>
我們可以使用 String 的 intern() 方法在運(yùn)行過程將字符串添加到 String Pool 中惭适。
我們可以看到
這是一個本地方法,看不到源碼楼镐,不過我們可以看到注釋
大致意思就是當(dāng)一個字符串調(diào)用 intern() 方法時癞志,如果 String Pool 中已經(jīng)存在一個字符串和該字符串值相等(使用 equals() 方法進(jìn)行確定),那么就會返回 String Pool 中字符串的引用框产;否則凄杯,就會在 String Pool 中添加一個新的字符串,并返回這個新字符串的引用秉宿。
用個demo來解釋這個流程
我上面的s1 和 s2 采用 new String() 的方式新建了兩個不同字符串戒突,而 s3 和 s4 是通過 s1.intern() 和 s2.intern() 方法取得同一個字符串引用。第一個intern() 首先把 "飯談編程" 放到 String Pool 中描睦,然后返回這個字符串引用膊存,而第二個intern()則直接從String Pool 讀取了,因此 s3 和 s4 引用的是同一個字符串忱叭。
<u>繼續(xù)挖坑隔崎,準(zhǔn)備埋了候選人</u>剛剛說到 new String("飯談編程") != new String("飯談編程") ,那么 "飯談編程" 和 "飯談編程"相等嗎窑多?說下流程仍稀?
是相等的,我們可以看到
流程是因為:采用這種字面量的形式創(chuàng)建字符串埂息,JVM會自動地將字符串放入 String Pool 中技潘,因此它們兩個是相等的。
<u>繼續(xù)往細(xì)節(jié)挖千康,這是一個比較刁鉆的問題</u> new String("飯談編程") JVM做了啥享幽?
首先使用這種方式一共會創(chuàng)建兩個字符串對象,當(dāng)然了拾弃,前提是 String Pool 中還沒有 "飯談編程" 這個字符串對象值桩,因此編譯時期會在 String Pool 中創(chuàng)建一個字符串對象,指向這個 "飯談編程" 字符串字面量豪椿。
然后在使用 new 的方式的時候奔坟,在堆中創(chuàng)建一個字符串對象,這一步我們可以結(jié)合String的構(gòu)造函數(shù)來看看
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
可以看到搭盾,在將一個字符串對象作為另一個字符串對象的構(gòu)造函數(shù)參數(shù)時咳秉,JVM會從String Pool 中將這個字符串對象取出來,當(dāng)做參數(shù)傳進(jìn)String的構(gòu)造函數(shù)中鸯隅,將 value 數(shù)組和hash值賦予這個新的對象澜建。
總結(jié):String我們在日常開發(fā)中經(jīng)常用到,不過一個合格的候選人應(yīng)該要吃透String、StringBuilder 和StringBuffer的區(qū)別炕舵,并且要對String Pool的原理了解的盡量多一些何之,不要被我上面挖的坑給埋了。
看看我在運(yùn)算方面埋了什么坑
請問在Java中方法參數(shù)的傳遞方式是引用傳遞呢咽筋?還是值傳遞呢溶推?
這個要分情況,如果參數(shù)是基本類型的話晤硕,就是值傳遞悼潭,如果是引用類型的話,則是引用傳遞舞箍。
<u>錯舰褪,這是很多初學(xué)者容易搞錯的地方,也是我日常挖坑埋人的地方</u>
Java 的參數(shù)全都是是以值傳遞的形式傳入方法中疏橄,而不是引用傳遞占拍。如果參數(shù)是基本類型,則傳遞的是基本類型的字面量值的拷貝捎迫。而如果參數(shù)是引用類型的話晃酒,傳遞的則值該參數(shù)所引用的對象在堆中地址值的拷貝。
請看題 float f = 2.2窄绒,這么寫有沒有問題贝次?
看起來是沒問題的,其實是有問題的彰导,這個其實一般我不會用來面試蛔翅,而是用來放在筆試題中。
2.2這個字面量屬于 double 類型的位谋,因此不能直接將 2.2 直接賦值給 float 變量山析,因為這是向下轉(zhuǎn)型,記住Java 不能隱式執(zhí)行向下轉(zhuǎn)型掏父,因為這會使得精度降低笋轨。
正常寫法是
float f = 2.2f;
<u>繼續(xù)挖坑</u>,那么float f = 2.2f; f += 2.2;可以嗎
這同樣是我會放進(jìn)筆試題考研候選人基礎(chǔ)的一道題赊淑,是可以的爵政,因為使用 += 或者 ++ 運(yùn)算符,JVM會執(zhí)行隱式類型轉(zhuǎn)換陶缺。
上面的語句相當(dāng)于將 s1 + 1 的計算結(jié)果進(jìn)行了向下轉(zhuǎn)型:
f = (float) (f + 2.2);
總結(jié):在運(yùn)算方面埋的坑比較基礎(chǔ)钾挟,一般是放在面試題中,而且其實用idea開發(fā)的話實際上可以在開發(fā)期就會報錯了组哩,但是這并不意味著idea可以檢測出來的東西,你就可以不懂,特別是要來我司面試伶贰,這意味著你的專業(yè)能力是否過關(guān)蛛砰。
看看我在修飾符方面埋了什么坑
說說看對修飾符final的理解
首先是在變量上使用了final,意味著聲明數(shù)據(jù)為常量黍衙,可以是編譯時常量泥畅,也可以是在運(yùn)行時被初始化后不能被改變的常量,作用可以分為:
- 對于基本類型琅翻,final 使數(shù)值不變位仁;
- 對于引用類型,final 使引用不變方椎,也就不能引用其它對象聂抢。
如果是在方法上使用了final,則聲明方法不能被子類重寫棠众。
如果是在類上使用了final琳疏,則聲明方法不允許被繼承。
<u>開始挖坑了闸拿,等著你跳</u> 挺好的空盼,按照你的說法,final int b = 1; b之后是不可以改的新荤;那如果是這樣的例子揽趾,A對象的x可以改嗎
是可以改的,這也是引用類型的那一種苛骨,fianl是作用在A對象的引用上篱瞎,而不是作用在A對象的數(shù)據(jù)成員x上,因此是可以改的智袭。
<u>繼續(xù)挖坑</u> 剛剛你說到聲明方法不能被子類重寫奔缠,那么問題來了,為啥這樣可以
<u>一般候選人都會在這里支支吾吾的說不出個所以然來吼野。</u>
其實他回答的理論是對的校哎,只是他沒有實際上嘗試過我這種寫法。實際上在private 方法隱式地被指定為 final的時候瞳步,如果在子類中定義的方法和基類中的一個 private 方法簽名相同闷哆,此時子類的方法并不是重寫了基類方法,而是在子類中定義了一個新的方法单起。
聊聊看你對修飾符static的了解
首先是用在變量上的話抱怔,這個變量我們一般稱之為靜態(tài)變量,也可以稱之為類變嘀倒,也就是說這個變量屬于類的屈留,類所有的實例都共享靜態(tài)變量局冰,一般我們是直接通過類名來訪問它,需要注意的一點事灌危,靜態(tài)變量在內(nèi)存中只存在一份康二。
而如果是用在方法上的話,就被稱之為靜態(tài)方法勇蝙,這個靜態(tài)方法在類加載的時候就存在了沫勿,它不依賴于任何實例,因此靜態(tài)方法必須有實現(xiàn)味混,也就是說它不能是抽象方法产雹。
<u>開始挖坑</u> 可以在靜態(tài)方法內(nèi)使用this或者super關(guān)鍵字嗎
不可以的,只能訪問所屬類的靜態(tài)字段和靜態(tài)方法翁锡,方法中不能有 this 和 super 關(guān)鍵字蔓挖,可以說static和this和super是互相矛盾的存在。
<u>坑點來了</u> 之前來了個實習(xí)生盗誊,寫代碼的時候就犯了這個錯时甚,你看看下面的執(zhí)行結(jié)果是啥
正確答案是
這里記住一個點就可以了,靜態(tài)語句塊優(yōu)先于普通語句塊哈踱,而普通語句塊優(yōu)先于構(gòu)造函數(shù)荒适。
<u>繼續(xù)深坑</u> 那么如果是有繼承關(guān)系在的時候呢?比如這道題开镣,說說他們的執(zhí)行順序
大部分初級的候選人都會在這道題被絆倒刀诬,正確答案應(yīng)該是:
也就是說,存在繼承的情況下邪财,初始化順序為:
- 父類(靜態(tài)變量陕壹、靜態(tài)語句塊)
- 子類(靜態(tài)變量、靜態(tài)語句塊)
- 父類(實例變量树埠、普通語句塊)
- 父類(構(gòu)造函數(shù))
- 子類(實例變量糠馆、普通語句塊)
- 子類(構(gòu)造函數(shù))
總結(jié):雖然看起來修飾符是一個比較小的東西,但是如果實際開發(fā)中采坑了怎憋,卻會造成比較大的風(fēng)險又碌,比如執(zhí)行順序搞錯了,而且也可以通過候選人對修飾符的了解情況绊袋,可以看出這個人實際的編程水平毕匀,這也是我們作為面試官想要迫切知道的地方。
最后
后續(xù)系列文章安排:
- 談?wù)勎以贠bject挖的坑
- 談?wù)勎以诩贤诘目?/li>
- 談?wù)勎以趎etty系列挖的坑…
你好癌别,我是Java面試官飯談編程皂岔,我將會從面試官角度告訴你我面試候選人期間挖的坑。
好好面試系列將會分多篇文章進(jìn)行展姐,基本上看完該系列的文章躁垛,Java基礎(chǔ)這塊便可以遇神殺神了剖毯,畢竟來來去去就這些,后續(xù)精彩請等待教馆!
公眾號:飯談編程
原文鏈接:https://mp.weixin.qq.com/s/F73r_f5YcBOayPdsin4gBg
謝謝點贊支持??????速兔!