Java 基礎(chǔ)面試知識(shí)點(diǎn)(重要?。。。?/h1>
Java 基礎(chǔ)知識(shí)相關(guān)
Java中 == 和 equals 和 hashCode 的區(qū)別
對(duì)于關(guān)系操作符 ==
若操作數(shù)的類型是基本數(shù)據(jù)類型,則該關(guān)系操作符判斷的是左右兩邊操作數(shù)的值是否相等
若操作數(shù)的類型是引用數(shù)據(jù)類型,則該關(guān)系操作符判斷的是左右兩邊操作數(shù)的內(nèi)存地址是否相同。也就是說,若此時(shí)返回true,則該操作符作用的一定是同一個(gè)對(duì)象。
對(duì)于使用 equals 方法,內(nèi)部實(shí)現(xiàn)分為三個(gè)步驟:
先比較引用是否相同(是否為同一對(duì)象),
再判斷類型是否一致(是否為同一類型),
最后比較內(nèi)容是否一致
Java 中所有內(nèi)置的類的 equals 方法的實(shí)現(xiàn)步驟均是如此,特別是諸如 Integer,Double 等包裝器類。如以下 String
中的 equals
方法實(shí)現(xiàn)
hashcode是系統(tǒng)用來快速檢索對(duì)象而使用
equals方法本意是用來判斷引用的對(duì)象是否一致
重寫equals方法和hashcode方法時(shí),equals方法中用到的成員變量也必定會(huì)在hashcode方法中用到,只不過前者作為比較項(xiàng),后者作為生成摘要的信息項(xiàng),本質(zhì)上所用到的數(shù)據(jù)是一樣的,從而保證二者的一致性
int、char、long各占多少字節(jié)數(shù)
Java 基本類型占用的字節(jié)數(shù)
1字節(jié): byte , boolean
2字節(jié): short , char
4字節(jié): int , float
8字節(jié): long , double
注:1字節(jié)(byte)=8位(bits)
int與integer的區(qū)別
Integer是int的包裝類,int則是java的一種基本數(shù)據(jù)類型
Integer變量必須實(shí)例化后才能使用,而int變量不需要
Integer實(shí)際是對(duì)象的引用,當(dāng)new一個(gè)Integer時(shí),實(shí)際上是生成一個(gè)指針指向此對(duì)象;而int則是直接存儲(chǔ)數(shù)據(jù)值
Integer的默認(rèn)值是null,int的默認(rèn)值是0
對(duì) Java 多態(tài)的理解
多態(tài)是指父類的某個(gè)方法被子類重寫時(shí),可以產(chǎn)生自己的功能行為,同一個(gè)操作作用于不同對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。
多態(tài)的三個(gè)必要條件:
繼承父類。
重寫父類的方法。
父類的引用指向子類對(duì)象
然后可以使用結(jié)合里氏替換法則進(jìn)一步的談理解
里氏替換法則 ---- 所有引用基類的地方必須能透明地使用其子類的對(duì)象
子類必須完全實(shí)現(xiàn)父類的方法
注意 ?在類中調(diào)用其他類時(shí)務(wù)必要使用父類或接口,如果不能使用父類或接口,則說明類的設(shè)計(jì)已經(jīng)違背了LSP原則
注意 ?如果子類不能完整地實(shí)現(xiàn)父類的方法,或者父類的某些方法在子類中已經(jīng)發(fā)生“畸變”,則建議斷開父子繼承關(guān)系,采用依賴、聚集、組合等關(guān)系代替繼承
子類可以有自己的個(gè)性
覆蓋或?qū)崿F(xiàn)父類的方法時(shí)輸入?yún)?shù)可以被放大
覆寫或?qū)崿F(xiàn)父類的方法時(shí)輸出結(jié)果可以被縮小
在項(xiàng)目中,采用里氏替換原則時(shí),盡量避免子類的“個(gè)性”,一旦子類有“個(gè)性”,這個(gè)子類和父類之間的關(guān)系就很難調(diào)和了, 把子類當(dāng)做父類使用,子類的“個(gè)性”被抹殺——委屈了點(diǎn);把子類單獨(dú)作為一個(gè)業(yè)務(wù)來使用,則會(huì)讓代碼間的耦合關(guān)系變得撲朔迷離——缺乏類替換的標(biāo)準(zhǔn)
String、StringBuffer、StringBuilder 區(qū)別
StringBuffer 和 String 一樣都是用來存儲(chǔ)字符串的,只不過由于他們內(nèi)部的實(shí)現(xiàn)方式不同,導(dǎo)致他們所使用的范圍不同,對(duì)于 StringBuffer 而言,他在處理字符串時(shí),若是對(duì)其進(jìn)行修改操作,它并不會(huì)產(chǎn)生一個(gè)新的字符串對(duì)象,所以說在內(nèi)存使用方面它是優(yōu)于 String 的
StringBuilder 也是一個(gè)可變的字符串對(duì)象,他與 StringBuffer 不同之處就在于它是線程不安全的,基于這點(diǎn),它的速度一般都比 StringBuffer 快
String 字符串的拼接會(huì)被 JVM 解析成 StringBuilder 對(duì)象拼接,在這種情況下 String 的速度比 StringBuffer 的速度快
str +=”b”等同于 str = new StringBuilder(str).append(“b”).toString();
詳解內(nèi)部類
內(nèi)部類使得多重繼承的解決方案變得更加完整
使用內(nèi)部類最吸引人的原因是:每個(gè)內(nèi)部類都能獨(dú)立地繼承一個(gè)(接口的)實(shí)現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對(duì)于內(nèi)部類都沒有影響
使用內(nèi)部類才能實(shí)現(xiàn)多重繼承
內(nèi)部類可以用多個(gè)實(shí)例,每個(gè)實(shí)例都有自己的狀態(tài)信息,并且與其他外圍對(duì)象的信息相互獨(dú)立。
在單個(gè)外圍類中,可以讓多個(gè)內(nèi)部類以不同的方式實(shí)現(xiàn)同一個(gè)接口,或者繼承同一個(gè)類。
創(chuàng)建內(nèi)部類對(duì)象的時(shí)刻并不依賴于外圍類對(duì)象的創(chuàng)建。
內(nèi)部類并沒有令人迷惑的“is-a”關(guān)系,他就是一個(gè)獨(dú)立的實(shí)體。
內(nèi)部類提供了更好的封裝,除了該外圍類,其他類都不能訪問。
當(dāng)我們?cè)趧?chuàng)建某個(gè)外圍類的內(nèi)部類對(duì)象時(shí),此時(shí)內(nèi)部類對(duì)象必定會(huì)捕獲一個(gè)指向那個(gè)外圍類對(duì)象的引用,只要我們?cè)谠L問外圍類的成員時(shí),就會(huì)用這個(gè)引用來選擇外圍類的成員
成員內(nèi)部類
在成員內(nèi)部類中要注意兩點(diǎn),
成員內(nèi)部類中不能存在任何 static 的變量和方法
成員內(nèi)部類是依附于外圍類的,所以只有先創(chuàng)建了外圍類才能夠創(chuàng)建內(nèi)部類
靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個(gè)最大的區(qū)別,我們知道非靜態(tài)內(nèi)部類在編譯完成之后會(huì)隱含地保存著一個(gè)引用,該引用是指向創(chuàng)建它的外圍內(nèi),但是靜態(tài)內(nèi)部類卻沒有。沒有這個(gè)引用就意味著:
它的創(chuàng)建是不需要依賴于外圍類的。
它不能使用任何外圍類的非static成員變量和方法。
為什么Java里的匿名內(nèi)部類只能訪問final修飾的外部變量?
匿名內(nèi)部類用法
編譯后的結(jié)果
因?yàn)槟涿麅?nèi)部類最終用會(huì)編譯成一個(gè)單獨(dú)的類,而被該類使用的變量會(huì)以構(gòu)造函數(shù)參數(shù)的形式傳遞給該類,例如:Integer paramInteger,如果變量 不定義成final的,paramInteger在匿名內(nèi)部類被可以被修改,進(jìn)而造成和外部的paramInteger不一致的問題,為了避免這種不一致的情況,因?yàn)镴ava 規(guī)定匿名內(nèi)部類只能訪問final修飾的外部變量
抽象類和接口的區(qū)別
默認(rèn)的方法實(shí)現(xiàn)抽象類可以有默認(rèn)的方法實(shí)現(xiàn)完全是抽象的。接口根本不存在方法的實(shí)現(xiàn)
實(shí)現(xiàn)抽象類使用 extends 關(guān)鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)。子類使用關(guān)鍵字 implements 來實(shí)現(xiàn)接口。它需要提供接口中所有聲明的方法的實(shí)現(xiàn)。
抽象類可以有構(gòu)造器,而接口不能有構(gòu)造器
抽象方法可以有public、protected和default這些修飾符。接口方法默認(rèn)修飾符是public。你不可以使用其它修飾符。
抽象類在java語言中所表示的是一種繼承關(guān)系,一個(gè)子類只能存在一個(gè)父類,但是可以存在多個(gè)接口。
抽象方法比接口速度要快,接口是稍微有點(diǎn)慢的,因?yàn)樗枰獣r(shí)間去尋找在類中實(shí)現(xiàn)的方法。
如果往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼。 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類
面試必備 |4年經(jīng)驗(yàn)匯總208道Java 最常見面試題清單+答案
泛型中 extends 和 super 的區(qū)別
父類的靜態(tài)方法能不能被子類重寫
靜態(tài)方法與靜態(tài)成員變量可以被繼承,但是不能被重寫。它對(duì)子類隱藏,因此靜態(tài)方法也不能實(shí)現(xiàn)多態(tài)
進(jìn)程和線程的區(qū)別
進(jìn)程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進(jìn)程有獨(dú)立的地址空間,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響,而線程只是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨(dú)的地址空間,一個(gè)線程死掉就等于整個(gè)進(jìn)程死掉,所以多進(jìn)程的程序要比多線程的程序健壯,但在進(jìn)程切換時(shí),耗費(fèi)資源較大,效率要差一些。但對(duì)于一些要求同時(shí)進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進(jìn)程。
final, finally, finalize的區(qū)別
final 用于聲明屬性,方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承.
finally 是異常處理語句結(jié)構(gòu)的一部分,表示總是執(zhí)行.
finalize 是Object類的一個(gè)方法,在垃圾收集器執(zhí)行的時(shí)候會(huì)調(diào)用被回收對(duì)象的此方法,可以覆蓋此方法提供垃圾收集時(shí)的其他資源回收,例如關(guān)閉文件等. JVM不保證此方法總被調(diào)用.
Parcelable和Serializable的區(qū)別
Serializable是Java中的序列化接口,其使用起來簡單但 是開銷很大,序列化和反序列化過程需要大量I/O操作。而Parcelable是Android中的序列化方式,因此更適合用在Android平臺(tái)上,它的缺點(diǎn)就是使用起來稍微麻煩 點(diǎn),但是它的效率很高,這是Android推薦的序列化方式,因此我們要首選Parcelable。Parcelable主要用在內(nèi)存序列化上,通過Parcelable將對(duì)象序列化到存儲(chǔ)設(shè)備 中或者將對(duì)象序列化后通過網(wǎng)絡(luò)傳輸也都是可以的,但是這個(gè)過程會(huì)稍顯復(fù)雜,因此在這兩種情況下建議大家使用Serializable。
為什么非靜態(tài)內(nèi)部類里不可以有靜態(tài)屬性
談?wù)剬?duì)kotlin的理解
因人而異,請(qǐng)自行整理答案
String 轉(zhuǎn)換成 integer的方式及原理
方式一源碼:
可以看出
方式一
最終調(diào)用的是方式二
通過toString()方法,可以把整數(shù)(包括0)轉(zhuǎn)化為字符串,但是 Integer 如果是 null 的話,就會(huì)報(bào)空指針異常
方式三源碼:
可以看出 當(dāng) Integer 是null的時(shí)候,返回的String是 字符串 "null" 而不是 null
適合初學(xué)者學(xué)習(xí)的2021最新Java學(xué)習(xí)視頻,書籍,面試題,PDF文檔,都是經(jīng)典干貨!