本文收錄于《面試小抄》系列,Github地址(可下載pdf):https://github.com/cosen1024/Java-Interview
國(guó)內(nèi)Gitee(可下載pdf):https://gitee.com/cosen1024/Java-Interview
這是一個(gè)很干的面試題合集,主要涉及Java基礎(chǔ)从绘、Java并發(fā)培他、JVM蜂奸、MySQL愉老、Redis、Spring啊犬、MyBatis灼擂、Kafka、操作系統(tǒng)觉至、計(jì)算機(jī)網(wǎng)絡(luò)等知識(shí)點(diǎn)剔应。
Java基礎(chǔ)內(nèi)容較多,我將分成兩篇语御。這是本期的 Java 基礎(chǔ)面試題目錄峻贮,看看你會(huì)哪些?
話不多說(shuō)沃暗,開(kāi)始發(fā)車了~
1. Java語(yǔ)言有哪些特點(diǎn)月洛?
- 面向?qū)ο螅ǚ庋b,繼承孽锥,多態(tài))嚼黔;
- 平臺(tái)無(wú)關(guān)性,平臺(tái)無(wú)關(guān)性的具體表現(xiàn)在于惜辑,Java 是“一次編寫唬涧,到處運(yùn)行(Write Once,Run any Where)”的語(yǔ)言盛撑,因此采用 Java 語(yǔ)言編寫的程序具有很好的可移植性碎节,而保證這一點(diǎn)的正是 Java 的虛擬機(jī)機(jī)制。在引入虛擬機(jī)之后抵卫,Java 語(yǔ)言在不同的平臺(tái)上運(yùn)行不需要重新編譯狮荔。
- 可靠性、安全性介粘;
- 支持多線程殖氏。C++ 語(yǔ)言沒(méi)有內(nèi)置的多線程機(jī)制,因此必須調(diào)用操作系統(tǒng)的多線程功能來(lái)進(jìn)行多線程程序設(shè)計(jì)姻采,而 Java 語(yǔ)言卻提供了多線程支持雅采;
- 支持網(wǎng)絡(luò)編程并且很方便。Java 語(yǔ)言誕生本身就是為簡(jiǎn)化網(wǎng)絡(luò)編程設(shè)計(jì)的慨亲,因此 Java 語(yǔ)言不僅支持網(wǎng)絡(luò)編程而且很方便婚瓜;
- 編譯與解釋并存;
2. Java和C++有什么關(guān)系刑棵,它們有什么區(qū)別巴刻?
- 都是面向?qū)ο蟮恼Z(yǔ)言,都支持封裝蛉签、繼承和多態(tài)胡陪;
- C++ 支持指針茂附,而 Java 沒(méi)有指針的概念;
- C++ 支持多繼承督弓,而 Java 不支持多重繼承,但允許一個(gè)類實(shí)現(xiàn)多個(gè)接口乒验;
- Java 自動(dòng)進(jìn)行無(wú)用內(nèi)存回收操作愚隧,不再需要程序員進(jìn)行手動(dòng)刪除,而 C++ 中必須由程序釋放內(nèi)存資源锻全,這就增加了程序員的負(fù)擔(dān)狂塘。
- Java 不支持操作符重載,操作符重載則被認(rèn)為是 C++ 的突出特征鳄厌;
- Java 是完全面向?qū)ο蟮恼Z(yǔ)言荞胡,并且還取消了 C/C++ 中的結(jié)構(gòu)和聯(lián)合,使編譯程序更加簡(jiǎn)潔了嚎;
- C 和 C++ 不支持字符串變量泪漂,在 C 和 C++ 程序中使用“Null”終止符代表字符串的結(jié)束。在 Java 中字符串是用類對(duì)象(String 和 StringBuffer)來(lái)實(shí)現(xiàn)的歪泳;
- goto 語(yǔ)句是 C 和 C++ 的“遺物”萝勤,Java 不提供 goto 語(yǔ)句,雖然 Java 指定 goto 作為關(guān)鍵字呐伞,但不支持它的使用敌卓,這使程序更簡(jiǎn)潔易讀;
3. JVM伶氢、JRE和JDK的關(guān)系是什么趟径?
JDK是(Java Development Kit)的縮寫,它是功能齊全的 Java SDK癣防。它擁有 JRE 所擁有的一切蜗巧,還有編譯器(javac)和工具(如 javadoc 和 jdb)。它能夠創(chuàng)建和編譯程序劣砍。
JRE是Java Runtime Environment縮寫惧蛹,它是運(yùn)行已編譯 Java 程序所需的所有內(nèi)容的集合,包括 Java 虛擬機(jī)(JVM)刑枝,Java 類庫(kù)香嗓,java 命令和其他的一些基礎(chǔ)構(gòu)件。但是装畅,它不能用于創(chuàng)建新程序靠娱。
JDK包含JRE,JRE包含JVM掠兄。
4. 什么是字節(jié)碼?采用字節(jié)碼的好處是什么?
這個(gè)問(wèn)題像云,面試官可以擴(kuò)展提問(wèn)锌雀,Java 是編譯執(zhí)行的語(yǔ)言,還是解釋執(zhí)行的語(yǔ)言?
Java之所以可以“一次編譯迅诬,到處運(yùn)行”腋逆,一是因?yàn)镴VM針對(duì)各種操作系統(tǒng)、平臺(tái)都進(jìn)行了定制侈贷,二是因?yàn)闊o(wú)論在什么平臺(tái)惩歉,都可以編譯生成固定格式的字節(jié)碼(.class文件)供JVM使用。因此俏蛮,也可以看出字節(jié)碼對(duì)于Java生態(tài)的重要性撑蚌。
之所以被稱之為字節(jié)碼,是因?yàn)樽止?jié)碼文件由十六進(jìn)制值組成搏屑,而JVM以兩個(gè)十六進(jìn)制值為一組争涌,即以字節(jié)為單位進(jìn)行讀取。在Java中一般是用javac命令編譯源代碼為字節(jié)碼文件辣恋,一個(gè).java文件從編譯到運(yùn)行的示例如圖所示亮垫。
Java語(yǔ)言通過(guò)字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問(wèn)題抑党,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn)包警。所以Java程序運(yùn)行時(shí)比較高效,而且底靠,由于字節(jié)碼并不專對(duì)一種特定的機(jī)器害晦,因此,Java程序無(wú)須重新編譯便可在多種不同的計(jì)算機(jī)上運(yùn)行暑中。
5. Oracle JDK 和 OpenJDK 的區(qū)別是什么壹瘟?
可能在看這個(gè)問(wèn)題之前很多人和我一樣并沒(méi)有接觸和使用過(guò) OpenJDK 。下面通過(guò)我通過(guò)我收集到一些資料對(duì)你解答這個(gè)被很多人忽視的問(wèn)題鳄逾。
- Oracle JDK 版本將每三年發(fā)布一次稻轨,而 OpenJDK 版本每三個(gè)月發(fā)布一次;
- OpenJDK 是一個(gè)參考模型并且是完全開(kāi)源的雕凹,而 Oracle JDK 是OpenJDK 的一個(gè)實(shí)現(xiàn)殴俱,并不是完全開(kāi)源的;
- Oracle JDK 比 OpenJDK 更穩(wěn)定枚抵。OpenJDK 和 Oracle JDK 的代碼幾乎相同线欲,但 Oracle JDK 有更多的類和一些錯(cuò)誤修復(fù)。因此汽摹,如果您想開(kāi)發(fā)企業(yè)/商業(yè)軟件李丰,建議選擇 Oracle JDK,因?yàn)樗?jīng)過(guò)了徹底的測(cè)試和穩(wěn)定逼泣。某些情況下趴泌,有些人提到在使用 OpenJDK 可能會(huì)遇到了許多應(yīng)用程序崩潰的問(wèn)題舟舒,但是,只需切換到 Oracle JDK 就可以解決問(wèn)題嗜憔;
- 在響應(yīng)性和 JVM 性能方面秃励,Oracle JDK 與 OpenJDK 相比提供了更好的性能;
- Oracle JDK 不會(huì)為即將發(fā)布的版本提供長(zhǎng)期支持吉捶,用戶每次都必須通過(guò)更新到最新版本獲得支持來(lái)獲取最新版本莺治;
- Oracle JDK 根據(jù)二進(jìn)制代碼許可協(xié)議獲得許可,而 OpenJDK 根據(jù) GPLv2 許可獲得許可帚稠。
6. Java有哪些數(shù)據(jù)類型?
Java 語(yǔ)言的數(shù)據(jù)類型分為兩種:基本數(shù)據(jù)類型和引用數(shù)據(jù)類型床佳。
基本數(shù)據(jù)類型包括 boolean(布爾型)滋早、float(單精度浮點(diǎn)型)、char(字符型)砌们、byte(字節(jié)型)杆麸、short(短整型)、int(整型)浪感、long(長(zhǎng)整型)和 double (雙精度浮點(diǎn)型)共 8 種昔头,如下圖所示。
image-
引用數(shù)據(jù)類型建立在基本數(shù)據(jù)類型的基礎(chǔ)上影兽,包括數(shù)組揭斧、類和接口。引用數(shù)據(jù)類型是由用戶自定義峻堰,用來(lái)限制其他數(shù)據(jù)的類型讹开。另外,Java 語(yǔ)言中不支持 C++ 中的指針類型捐名、結(jié)構(gòu)類型旦万、聯(lián)合類型和枚舉類型。
7. switch 是否能作用在 byte 上镶蹋,是否能作用在 long 上成艘,是否能作用在 String 上?
Java5 以前 switch(expr)中贺归,expr 只能是 byte淆两、short、char牧氮、int琼腔。
從 Java 5 開(kāi)始,Java 中引入了枚舉類型踱葛, expr 也可以是 enum 類型丹莲。
從 Java 7 開(kāi)始光坝,expr還可以是字符串(String),但是長(zhǎng)整型(long)在目前所有的版本中都是不可以的甥材。
8. 訪問(wèn)修飾符public盯另、private、protected洲赵、以及不寫(默認(rèn))時(shí)的區(qū)別鸳惯?
Java中,可以使用訪問(wèn)控制符來(lái)保護(hù)對(duì)類叠萍、變量芝发、方法和構(gòu)造方法的訪問(wèn)。Java 支持 4 種不同的訪問(wèn)權(quán)限苛谷。
- default (即默認(rèn)辅鲸,什么也不寫): 在同一包內(nèi)可見(jiàn),不使用任何修飾符腹殿。使用對(duì)象:類独悴、接口、變量锣尉、方法刻炒。
- private : 在同一類內(nèi)可見(jiàn)。使用對(duì)象:變量自沧、方法坟奥。注意:不能修飾類(外部類)
- public : 對(duì)所有類可見(jiàn)。使用對(duì)象:類拇厢、接口筏勒、變量、方法
- protected : 對(duì)同一包內(nèi)的類和所有子類可見(jiàn)旺嬉。使用對(duì)象:變量管行、方法。注意:不能修飾類(外部類)邪媳。
9. break ,continue ,return 的區(qū)別及作用捐顷?
- break 跳出總上一層循環(huán),不再執(zhí)行循環(huán)(結(jié)束當(dāng)前的循環(huán)體)
- continue 跳出本次循環(huán)雨效,繼續(xù)執(zhí)行下次循環(huán)(結(jié)束正在執(zhí)行的循環(huán) 進(jìn)入下一個(gè)循環(huán)條件)
- return 程序返回迅涮,不再執(zhí)行下面的代碼(結(jié)束當(dāng)前的方法 直接返回)
10. final、finally徽龟、finalize的區(qū)別叮姑?
final 用于修飾變量、方法和類。
- final 變量:被修飾的變量不可變传透,不可變分為
引用不可變
和對(duì)象不可變
耘沼,final 指的是引用不可變
,final 修飾的變量必須初始化朱盐,通常稱被修飾的變量為常量
群嗤。 - final 方法:被修飾的方法不允許任何子類重寫,子類可以使用該方法兵琳。
- final 類:被修飾的類不能被繼承狂秘,所有方法不能被重寫。
finally 作為異常處理的一部分躯肌,它只能在 try/catch
語(yǔ)句中者春,并且附帶一個(gè)語(yǔ)句塊表示這段語(yǔ)句最終一定被執(zhí)行(無(wú)論是否拋出異常),經(jīng)常被用在需要釋放資源的情況下清女,System.exit (0)
可以阻斷 finally 執(zhí)行碧查。
finalize 是在 java.lang.Object
里定義的方法,也就是說(shuō)每一個(gè)對(duì)象都有這么個(gè)方法校仑,這個(gè)方法在 gc
啟動(dòng),該對(duì)象被回收的時(shí)候被調(diào)用传惠。
一個(gè)對(duì)象的 finalize 方法只會(huì)被調(diào)用一次迄沫,finalize 被調(diào)用不一定會(huì)立即回收該對(duì)象,所以有可能調(diào)用 finalize 后卦方,該對(duì)象又不需要被回收了羊瘩,然后到了真正要被回收的時(shí)候,因?yàn)榍懊嬲{(diào)用過(guò)一次盼砍,所以不會(huì)再次調(diào)用 finalize 了尘吗,進(jìn)而產(chǎn)生問(wèn)題,因此不推薦使用 finalize 方法浇坐。
11. 為什么要用static關(guān)鍵字睬捶?
通常來(lái)說(shuō),用new創(chuàng)建類的對(duì)象時(shí)近刘,數(shù)據(jù)存儲(chǔ)空間才被分配擒贸,方法才供外界調(diào)用。但有時(shí)我們只想為特定域分配單一存儲(chǔ)空間觉渴,不考慮要?jiǎng)?chuàng)建多少對(duì)象或者說(shuō)根本就不創(chuàng)建任何對(duì)象介劫,再就是我們想在沒(méi)有創(chuàng)建對(duì)象的情況下也想調(diào)用方法。在這兩種情況下案淋,static關(guān)鍵字座韵,滿足了我們的需求。
12. ”static”關(guān)鍵字是什么意思踢京?Java中是否可以覆蓋(override)一個(gè)private或者是static的方法誉碴?
“static”關(guān)鍵字表明一個(gè)成員變量或者是成員方法可以在沒(méi)有所屬的類的實(shí)例變量的情況下被訪問(wèn)宦棺。
Java中static方法不能被覆蓋,因?yàn)榉椒ǜ采w是基于運(yùn)行時(shí)動(dòng)態(tài)綁定的翔烁,而static方法是編譯時(shí)靜態(tài)綁定的渺氧。static方法跟類的任何實(shí)例都不相關(guān),所以概念上不適用蹬屹。
13. 是否可以在static環(huán)境中訪問(wèn)非static變量侣背?
static變量在Java中是屬于類的,它在所有的實(shí)例中的值是一樣的慨默。當(dāng)類被Java虛擬機(jī)載入的時(shí)候贩耐,會(huì)對(duì)static變量進(jìn)行初始化。如果你的代碼嘗試不用實(shí)例來(lái)訪問(wèn)非static的變量厦取,編譯器會(huì)報(bào)錯(cuò)潮太,因?yàn)檫@些變量還沒(méi)有被創(chuàng)建出來(lái),還沒(méi)有跟任何實(shí)例關(guān)聯(lián)上虾攻。
14. static靜態(tài)方法能不能引用非靜態(tài)資源铡买?
不能,new的時(shí)候才會(huì)產(chǎn)生的東西霎箍,對(duì)于初始化后就存在的靜態(tài)資源來(lái)說(shuō)奇钞,根本不認(rèn)識(shí)它。
15. static靜態(tài)方法里面能不能引用靜態(tài)資源漂坏?
可以景埃,因?yàn)槎际穷惓跏蓟臅r(shí)候加載的,大家相互都認(rèn)識(shí)顶别。
16. 非靜態(tài)方法里面能不能引用靜態(tài)資源谷徙?
可以,非靜態(tài)方法就是實(shí)例方法驯绎,那是new之后才產(chǎn)生的完慧,那么屬于類的內(nèi)容它都認(rèn)識(shí)。
17. java靜態(tài)變量剩失、代碼塊骗随、和靜態(tài)方法的執(zhí)行順序是什么?
基本上代碼塊分為三種:Static靜態(tài)代碼塊赴叹、構(gòu)造代碼塊鸿染、普通代碼塊
代碼塊執(zhí)行順序靜態(tài)代碼塊——> 構(gòu)造代碼塊 ——> 構(gòu)造函數(shù)——> 普通代碼塊
繼承中代碼塊執(zhí)行順序:父類靜態(tài)塊——>子類靜態(tài)塊——>父類代碼塊——>父類構(gòu)造器——>子類代碼塊——>子類構(gòu)造器
想要深入了解,可以參考這篇文章 :https://juejin.cn/post/6844903986475040781
18. 面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別乞巧?
面向過(guò)程:
- 優(yōu)點(diǎn):性能比面向?qū)ο蟾哒墙罚驗(yàn)轭愓{(diào)用時(shí)需要實(shí)例化,開(kāi)銷比較大,比較消耗資源;比如單片機(jī)蚕冬、嵌入式開(kāi)發(fā)免猾、Linux/Unix等一般采用面向過(guò)程開(kāi)發(fā),性能是最重要的因素囤热。
- 缺點(diǎn):沒(méi)有面向?qū)ο笠拙S護(hù)猎提、易復(fù)用、易擴(kuò)展旁蔼。
面向?qū)ο?/strong>:
- 優(yōu)點(diǎn):易維護(hù)锨苏、易復(fù)用、易擴(kuò)展棺聊,由于面向?qū)ο笥蟹庋b伞租、繼承、多態(tài)性的特性限佩,可以設(shè)計(jì)出低耦合的系統(tǒng)葵诈,使系統(tǒng)更加靈活、更加易于維護(hù)祟同。
- 缺點(diǎn):性能比面向過(guò)程低作喘。
19. 講講面向?qū)ο笕筇匦?/h2>
- 封裝。封裝最好理解了晕城。封裝是面向?qū)ο蟮奶卣髦慌⑻梗菍?duì)象和類概念的主要特性。封裝广辰,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作主之,對(duì)不可信的進(jìn)行信息隱藏择吊。
- 繼承。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能槽奕,并在無(wú)需重新編寫原來(lái)的類的情況下對(duì)這些功能進(jìn)行擴(kuò)展几睛。通過(guò)繼承創(chuàng)建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”粤攒、“父類”或“超類”所森。
- 多態(tài)性。它是指在父類中定義的屬性和方法被子類繼承之后夯接,可以具有不同的數(shù)據(jù)類型或表現(xiàn)出不同的行為焕济,這使得同一個(gè)屬性或方法在父類及其各個(gè)子類中具有不同的含義。
20. Java語(yǔ)言是如何實(shí)現(xiàn)多態(tài)的盔几?
本質(zhì)上多態(tài)分兩種:
1晴弃、編譯時(shí)多態(tài)(又稱靜態(tài)多態(tài))
2、運(yùn)行時(shí)多態(tài)(又稱動(dòng)態(tài)多態(tài))
重載(overload)就是編譯時(shí)多態(tài)的一個(gè)例子,編譯時(shí)多態(tài)在編譯時(shí)就已經(jīng)確定上鞠,運(yùn)行的時(shí)候調(diào)用的是確定的方法际邻。
我們通常所說(shuō)的多態(tài)指的都是運(yùn)行時(shí)多態(tài),也就是編譯時(shí)不確定究竟調(diào)用哪個(gè)具體方法芍阎,一直延遲到運(yùn)行時(shí)才能確定世曾。這也是為什么有時(shí)候多態(tài)方法又被稱為延遲方法的原因。
Java實(shí)現(xiàn)多態(tài)有 3 個(gè)必要條件:繼承谴咸、重寫和向上轉(zhuǎn)型轮听。只有滿足這 3 個(gè)條件,開(kāi)發(fā)人員才能夠在同一個(gè)繼承結(jié)構(gòu)中使用統(tǒng)一的邏輯實(shí)現(xiàn)代碼處理不同的對(duì)象寿冕,從而執(zhí)行不同的行為蕊程。
- 繼承:在多態(tài)中必須存在有繼承關(guān)系的子類和父類。
- 重寫:子類對(duì)父類中某些方法進(jìn)行重新定義驼唱,在調(diào)用這些方法時(shí)就會(huì)調(diào)用子類的方法藻茂。
- 向上轉(zhuǎn)型:在多態(tài)中需要將子類的引用賦給父類對(duì)象,只有這樣該引用才既能可以調(diào)用父類的方法玫恳,又能調(diào)用子類的方法辨赐。
Java多態(tài)的實(shí)現(xiàn)原理可看這篇文章:https://my.oschina.net/u/4432600/blog/4535042
21. 重載(Overload)和重寫(Override)的區(qū)別是什么?
方法的重載和重寫都是實(shí)現(xiàn)多態(tài)的方式京办,區(qū)別在于前者實(shí)現(xiàn)的是編譯時(shí)的多態(tài)性掀序,而后者實(shí)現(xiàn)的是運(yùn)行時(shí)的多態(tài)性。
- 重寫發(fā)生在子類與父類之間, 重寫方法返回值和形參都不能改變惭婿,與方法返回值和訪問(wèn)修飾符無(wú)關(guān)不恭,即重載的方法不能根據(jù)返回類型進(jìn)行區(qū)分。即外殼不變财饥,核心重寫换吧!
- 重載(overloading) 是在一個(gè)類里面,方法名字相同钥星,而參數(shù)不同沾瓦。返回類型可以相同也可以不同。每個(gè)重載的方法(或者構(gòu)造函數(shù))都必須有一個(gè)獨(dú)一無(wú)二的參數(shù)類型列表谦炒。最常用的地方就是構(gòu)造器的重載贯莺。
22. 重載的方法能否根據(jù)返回值類型進(jìn)行區(qū)分?
不能根據(jù)返回值類型來(lái)區(qū)分重載的方法宁改。因?yàn)檎{(diào)用時(shí)不指定類型信息缕探,編譯器不知道你要調(diào)用哪個(gè)函數(shù)。
float max(int a, int b);
int max(int a, int b);
當(dāng)調(diào)用max(1,2);
時(shí)無(wú)法確定調(diào)用的是哪個(gè)还蹲,單從這一點(diǎn)上來(lái)說(shuō)撕蔼,僅返回值類型不同的重載是不應(yīng)該允許的豁鲤。
23. 構(gòu)造器(constructor)是否可被重寫(override)?
構(gòu)造器不能被繼承鲸沮,因此不能被重寫琳骡,但可以被重載。每一個(gè)類必須有自己的構(gòu)造函數(shù)讼溺,負(fù)責(zé)構(gòu)造自己這部分的構(gòu)造楣号。子類不會(huì)覆蓋父類的構(gòu)造函數(shù),相反必須一開(kāi)始調(diào)用父類的構(gòu)造函數(shù)怒坯。
24. 抽象類和接口的區(qū)別是什么炫狱?
語(yǔ)法層面上的區(qū)別:
- 抽象類可以提供成員方法的實(shí)現(xiàn)細(xì)節(jié),而接口中只能存在public abstract 方法剔猿;
- 抽象類中的成員變量可以是各種類型的视译,而接口中的成員變量只能是public static final類型的;
- 接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法归敬,而抽象類可以有靜態(tài)代碼塊和靜態(tài)方法酷含;
- 一個(gè)類只能繼承一個(gè)抽象類,而一個(gè)類卻可以實(shí)現(xiàn)多個(gè)接口汪茧。
設(shè)計(jì)層面上的區(qū)別:
- 抽象類是對(duì)一種事物的抽象椅亚,即對(duì)類抽象,而接口是對(duì)行為的抽象舱污。抽象類是對(duì)整個(gè)類整體進(jìn)行抽象呀舔,包括屬性、行為扩灯,但是接口卻是對(duì)類局部(行為)進(jìn)行抽象媚赖。
- 設(shè)計(jì)層面不同,抽象類作為很多子類的父類珠插,它是一種模板式設(shè)計(jì)惧磺。而接口是一種行為規(guī)范,它是一種輻射式設(shè)計(jì)丧失。
想要深入了解豺妓,可以參考這篇文章 :https://www.cnblogs.com/dolphin0520/p/3811437.html
25. 抽象類能使用 final 修飾嗎惜互?
不能布讹,定義抽象類就是讓其他類繼承的,如果定義為 final 該類就不能被繼承训堆,這樣彼此就會(huì)產(chǎn)生矛盾描验,所以 final 不能修飾抽象類
26. java 創(chuàng)建對(duì)象有哪幾種方式?
java中提供了以下四種創(chuàng)建對(duì)象的方式:
- new創(chuàng)建新對(duì)象
- 通過(guò)反射機(jī)制
- 采用clone機(jī)制
- 通過(guò)序列化機(jī)制
前兩者都需要顯式地調(diào)用構(gòu)造方法坑鱼。對(duì)于clone機(jī)制,需要注意淺拷貝和深拷貝的區(qū)別膘流,對(duì)于序列化機(jī)制需要明確其實(shí)現(xiàn)原理絮缅,在java中序列化可以通過(guò)實(shí)現(xiàn)Externalizable或者Serializable來(lái)實(shí)現(xiàn)。
27. 什么是不可變對(duì)象?好處是什么?
不可變對(duì)象指對(duì)象一旦被創(chuàng)建,狀態(tài)就不能再改變,任何修改都會(huì)創(chuàng)建一個(gè)新的對(duì)象,如 String呼股、Integer及其它包裝類.不可變對(duì)象最大的好處是線程安全.
28. 能否創(chuàng)建一個(gè)包含可變對(duì)象的不可變對(duì)象?
當(dāng)然可以,比如final Person[] persons = new Persion[]{}
. persons
是不可變對(duì)象的引用,但其數(shù)組中的Person實(shí)例卻是可變的.這種情況下需要特別謹(jǐn)慎,不要共享可變對(duì)象的引用.這種情況下,如果數(shù)據(jù)需要變化時(shí),就返回原對(duì)象的一個(gè)拷貝.
29. 值傳遞和引用傳遞的區(qū)別的什么耕魄?為什么說(shuō)Java中只有值傳遞?
值傳遞:指的是在方法調(diào)用時(shí)彭谁,傳遞的參數(shù)是按值的拷貝傳遞吸奴,傳遞的是值的拷貝堡妒,也就是說(shuō)傳遞后就互不相關(guān)了孝凌。
引用傳遞:指的是在方法調(diào)用時(shí)妖混,傳遞的參數(shù)是按引用進(jìn)行傳遞庆聘,其實(shí)傳遞的是引用的地址燥透,也就是變量所對(duì)應(yīng)的內(nèi)存空間的地址概荷。傳遞的是值的引用峭跳,也就是說(shuō)傳遞前和傳遞后都指向同一個(gè)引用(也就是同一個(gè)內(nèi)存空間)猜惋。
基本類型作為參數(shù)被傳遞時(shí)肯定是值傳遞唱矛;引用類型作為參數(shù)被傳遞時(shí)也是值傳遞罚舱,只不過(guò)“值”為對(duì)應(yīng)的引用。
想要深入了解揖赴,可以參考這篇文章 :http://www.itwanger.com/java/2019/11/26/java-yinyong-value.html
30. == 和 equals 區(qū)別是什么馆匿?
==
常用于相同的基本數(shù)據(jù)類型之間的比較,也可用于相同類型的對(duì)象之間的比較燥滑;
- 如果
==
比較的是基本數(shù)據(jù)類型渐北,那么比較的是兩個(gè)基本數(shù)據(jù)類型的值是否相等; - 如果
==
是比較的兩個(gè)對(duì)象铭拧,那么比較的是兩個(gè)對(duì)象的引用赃蛛,也就是判斷兩個(gè)對(duì)象是否指向了同一塊內(nèi)存區(qū)域;
equals方法主要用于兩個(gè)對(duì)象之間搀菩,檢測(cè)一個(gè)對(duì)象是否等于另一個(gè)對(duì)象
看一看Object類中equals方法的源碼:
public boolean equals(Object obj) {
return (this == obj);
}
它的作用也是判斷兩個(gè)對(duì)象是否相等呕臂,般有兩種使用情況:
- 情況1,類沒(méi)有覆蓋equals()方法肪跋。則通過(guò)equals()比較該類的兩個(gè)對(duì)象時(shí)歧蒋,等價(jià)于通過(guò)“==”比較這兩個(gè)對(duì)象。
- 情況2州既,類覆蓋了equals()方法谜洽。一般,我們都覆蓋equals()方法來(lái)兩個(gè)對(duì)象的內(nèi)容相等吴叶;若它們的內(nèi)容相等阐虚,則返回true(即,認(rèn)為這兩個(gè)對(duì)象相等)蚌卤。
java語(yǔ)言規(guī)范要求equals方法具有以下特性:
- 自反性实束。對(duì)于任意不為null的引用值x奥秆,x.equals(x)一定是true。
- 對(duì)稱性)咸灿。對(duì)于任意不為null的引用值x和y构订,當(dāng)且僅當(dāng)x.equals(y)是true時(shí),y.equals(x)也是true避矢。
- 傳遞性鲫咽。對(duì)于任意不為null的引用值x、y和z谷异,如果x.equals(y)是true分尸,同時(shí)y.equals(z)是true,那么x.equals(z)一定是true歹嘹。
- 一致性箩绍。對(duì)于任意不為null的引用值x和y,如果用于equals比較的對(duì)象信息沒(méi)有被修改的話尺上,多次調(diào)用時(shí)x.equals(y)要么一致地返回true要么一致地返回false材蛛。
- 對(duì)于任意不為null的引用值x,x.equals(null)返回false怎抛。
31. 介紹下hashCode()卑吭?
hashCode() 的作用是獲取哈希碼,也稱為散列碼马绝;它實(shí)際上是返回一個(gè)int整數(shù)豆赏。這個(gè)哈希碼的作用是確定該對(duì)象在哈希表中的索引位置。hashCode() 定義在JDK的Object.java中富稻,這就意味著Java中的任何類都包含有hashCode()函數(shù)掷邦。
散列表存儲(chǔ)的是鍵值對(duì)(key-value),它的特點(diǎn)是:能根據(jù)“鍵”快速的檢索出對(duì)應(yīng)的“值”椭赋。這其中就利用到了散列碼8Ц凇(可以快速找到所需要的對(duì)象)
32. 為什么要有 hashCode?
以“HashSet 如何檢查重復(fù)”為例子來(lái)說(shuō)明為什么要有 hashCode:
當(dāng)你把對(duì)象加入 HashSet 時(shí),HashSet 會(huì)先計(jì)算對(duì)象的 hashcode 值來(lái)判斷對(duì)象加入的位置哪怔,同時(shí)也會(huì)與其他已經(jīng)加入的對(duì)象的 hashcode 值作比較宣蔚,如果沒(méi)有相符的hashcode,HashSet會(huì)假設(shè)對(duì)象沒(méi)有重復(fù)出現(xiàn)认境。
但是如果發(fā)現(xiàn)有相同 hashcode 值的對(duì)象胚委,這時(shí)會(huì)調(diào)用 equals()方法來(lái)檢查 hashcode 相等的對(duì)象是否真的相同。如果兩者相同元暴,HashSet 就不會(huì)讓其加入操作成功篷扩。如果不同的話兄猩,就會(huì)重新散列到其他位置茉盏。這樣我們就大大減少了 equals 的次數(shù)鉴未,相應(yīng)就大大提高了執(zhí)行速度。
33. hashCode(),equals()兩種方法是什么關(guān)系?
Java 對(duì)于 eqauls()
方法和 hashCode()
方法是這樣規(guī)定的:
- 同一對(duì)象上多次調(diào)用 hashCode() 方法鸠姨,總是返回相同的整型值铜秆。
- 如果 a.equals(b),則一定有 a.hashCode() 一定等于 b.hashCode()讶迁。
- 如果 !a.equals(b)连茧,則 a.hashCode() 不一定等于 b.hashCode()。此時(shí)如果 a.hashCode() 總是不等于 b.hashCode()巍糯,會(huì)提高 hashtables 的性能啸驯。
- a.hashCode()==b.hashCode() 則 a.equals(b) 可真可假
- a.hashCode()!= b.hashCode() 則 a.equals(b) 為假祟峦。
上面結(jié)論簡(jiǎn)記:
- 如果兩個(gè)對(duì)象 equals罚斗,Java 運(yùn)行時(shí)環(huán)境會(huì)認(rèn)為他們的 hashCode 一定相等。
- 如果兩個(gè)對(duì)象不 equals宅楞,他們的 hashCode 有可能相等针姿。
- 如果兩個(gè)對(duì)象 hashCode 相等,他們不一定 equals厌衙。
- 如果兩個(gè)對(duì)象 hashCode 不相等距淫,他們一定不 equals
補(bǔ)充:關(guān)于 equals() 和 hashCode() 的重要規(guī)范
- 規(guī)范1:若重寫 equals() 方法,有必要重寫 hashcode()方法婶希,確保通過(guò) equals()方法判斷結(jié)果為 true 的兩個(gè)對(duì)象具備相等的 hashcode() 方法返回值榕暇。說(shuō)得簡(jiǎn)單點(diǎn)就是:“如果兩個(gè)對(duì)象相同,那么他們的 hashCode 應(yīng)該相等”喻杈。不過(guò)請(qǐng)注意:這個(gè)只是規(guī)范拐揭,如果非要寫一個(gè)類讓 equals() 方法返回 true 而 hashCode() 方法返回兩個(gè)不相等的值,編譯和運(yùn)行都是不會(huì)報(bào)錯(cuò)的奕塑。不過(guò)這樣違反了 Java 規(guī)范堂污,程序也就埋下了 BUG。
- 規(guī)范2:如果 equals() 方法返回 false龄砰,即兩個(gè)對(duì)象“不相同”盟猖,并不要求對(duì)這兩個(gè)對(duì)象調(diào)用 hashCode() 方法得到兩個(gè)不相同的數(shù)。說(shuō)的簡(jiǎn)單點(diǎn)就是:“如果兩個(gè)對(duì)象不相同换棚,他們的 hashCode 可能相同”式镐。
34. 為什么重寫 equals 方法必須重寫 hashcode 方法 ?
斷的時(shí)候先根據(jù)hashcode進(jìn)行的判斷固蚤,相同的情況下再根據(jù)equals()方法進(jìn)行判斷娘汞。如果只重寫了equals方法,而不重寫hashcode的方法夕玩,會(huì)造成hashcode的值不同你弦,而equals()方法判斷出來(lái)的結(jié)果為true惊豺。
在Java中的一些容器中,不允許有兩個(gè)完全相同的對(duì)象禽作,插入的時(shí)候尸昧,如果判斷相同則會(huì)進(jìn)行覆蓋。這時(shí)候如果只重寫了equals()的方法旷偿,而不重寫hashcode的方法烹俗,Object中hashcode是根據(jù)對(duì)象的存儲(chǔ)地址轉(zhuǎn)換而形成的一個(gè)哈希值。這時(shí)候就有可能因?yàn)闆](méi)有重寫hashcode方法萍程,造成相同的對(duì)象散列到不同的位置而造成對(duì)象的不能覆蓋的問(wèn)題幢妄。
35. String,StringBuffer, StringBuilder 的區(qū)別是什么?
1.可變與不可變茫负。String類中使用字符數(shù)組保存字符串磁浇,因?yàn)橛小癴inal”修飾符,所以string對(duì)象是不可變的朽褪。對(duì)于已經(jīng)存在的String對(duì)象的修改都是重新創(chuàng)建一個(gè)新的對(duì)象,然后把新的值保存進(jìn)去.
private final char value[];
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類置吓,在AbstractStringBuilder中也是使用字符數(shù)組保存字符串,這兩種對(duì)象都是可變的缔赠。
char[] value;
2.是否線程安全衍锚。
String中的對(duì)象是不可變的,也就可以理解為常量嗤堰,顯然線程安全戴质。
StringBuilder是非線程安全的。
StringBuffer對(duì)方法加了同步鎖或者對(duì)調(diào)用的方法加了同步鎖踢匣,所以是線程安全的告匠。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
3.如果你只是在單線程中使用字符串緩沖區(qū),那么StringBuilder的效率會(huì)更高些离唬。值得注意的是StringBuilder是在JDK1.5版本中增加的后专。以前版本的JDK不能使用該類。
36. String為什么要設(shè)計(jì)成不可變的输莺?
1.便于實(shí)現(xiàn)字符串池(String pool)
在Java中戚哎,由于會(huì)大量的使用String常量,如果每一次聲明一個(gè)String都創(chuàng)建一個(gè)String對(duì)象嫂用,那將會(huì)造成極大的空間資源的浪費(fèi)型凳。Java提出了String pool的概念,在堆中開(kāi)辟一塊存儲(chǔ)空間String pool嘱函,當(dāng)初始化一個(gè)String變量時(shí)甘畅,如果該字符串已經(jīng)存在了,就不會(huì)去創(chuàng)建一個(gè)新的字符串變量,而是會(huì)返回已經(jīng)存在了的字符串的引用疏唾。
String a = "Hello world!";
String b = "Hello world!";
如果字符串是可變的蓄氧,某一個(gè)字符串變量改變了其值,那么其指向的變量的值也會(huì)改變荸实,String pool將不能夠?qū)崿F(xiàn)!
2.使多線程安全
在并發(fā)場(chǎng)景下缴淋,多個(gè)線程同時(shí)讀一個(gè)資源准给,是安全的,不會(huì)引發(fā)競(jìng)爭(zhēng)重抖,但對(duì)資源進(jìn)行寫操作時(shí)是不安全的露氮,不可變對(duì)象不能被寫,所以保證了多線程的安全钟沛。
3.避免安全問(wèn)題
在網(wǎng)絡(luò)連接和數(shù)據(jù)庫(kù)連接中字符串常常作為參數(shù)畔规,例如,網(wǎng)絡(luò)連接地址URL恨统,文件路徑path叁扫,反射機(jī)制所需要的String參數(shù)。其不可變性可以保證連接的安全性畜埋。如果字符串是可變的莫绣,黑客就有可能改變字符串指向?qū)ο蟮闹担敲磿?huì)引起很嚴(yán)重的安全問(wèn)題悠鞍。
4.加快字符串處理速度
由于String是不可變的对室,保證了hashcode的唯一性,于是在創(chuàng)建對(duì)象時(shí)其hashcode就可以放心的緩存了咖祭,不需要重新計(jì)算掩宜。這也就是Map喜歡將String作為Key的原因,處理速度要快過(guò)其它的鍵對(duì)象么翰。所以HashMap中的鍵往往都使用String牺汤。
總體來(lái)說(shuō),String不可變的原因要包括 設(shè)計(jì)考慮浩嫌,效率優(yōu)化慧瘤,以及安全性這三大方面。