前言
這是我大概回憶整理的Java面試題凡泣,里面有很多知識(shí)點(diǎn),主要還是在于Java基礎(chǔ)皮假,當(dāng)然數(shù)據(jù)結(jié)構(gòu)跟算法最好也要懂鞋拟,是加分項(xiàng),如果不會(huì)惹资,果斷說贺纲,否則,面試官深入問褪测,問到你愣住了猴誊,這樣浪費(fèi)了自己的面試題和時(shí)間潦刃,自己不擅長的地方一定要知道,揚(yáng)長避短懈叹。當(dāng)然這些肯定是會(huì)問的乖杠,對(duì)于算法因?yàn)樽髡邊⒓恿怂惴ù筚悾愃茊栴}問的少澄成,但是第四面還是提到了動(dòng)態(tài)規(guī)劃問題滑黔,所以也要準(zhǔn)備一下,數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)也要注意復(fù)習(xí)一下环揽,以備面試官提起。
正文——部分面試題及答案整理
1. 什么是Java虛擬機(jī)庵佣?為什么Java被稱作是“平臺(tái)無關(guān)的編程語言”歉胶?
參考答案:
Java虛擬機(jī)是一個(gè)可以執(zhí)行Java字節(jié)碼的虛擬機(jī)進(jìn)程。Java源文件被編譯成能被Java虛擬機(jī)執(zhí)行的字節(jié)碼文件巴粪。
Java被設(shè)計(jì)成允許應(yīng)用程序可以運(yùn)行在任意的平臺(tái)通今,而不需要程序員為每一個(gè)平臺(tái)單獨(dú)重寫或者是重新編譯。Java虛擬機(jī)讓這個(gè)變?yōu)榭赡芨馗驗(yàn)樗赖讓佑布脚_(tái)的指令長度和其他特性辫塌。
2. JDK和JRE的區(qū)別是什么?
參考答案:
Java運(yùn)行時(shí)環(huán)境(JRE)派哲。它包括Java虛擬機(jī)臼氨、Java核心類庫和支持文件。它不包含開發(fā)工具(JDK)--編譯器芭届、調(diào)試器和其他工具储矩。
Java開發(fā)工具包(JDK)是完整的Java軟件開發(fā)包,包含了JRE褂乍,編譯器和其他的工具(比如:JavaDoc持隧,Java調(diào)試器),可以讓開發(fā)者開發(fā)逃片、編譯屡拨、執(zhí)行Java應(yīng)用程序。
3. ”static”關(guān)鍵字是什么意思褥实?Java中是否可以覆蓋(override)一個(gè)private或者是static的方法呀狼?
參考答案:
“static”關(guān)鍵字表明一個(gè)成員變量或者是成員方法可以在沒有所屬的類的實(shí)例變量的情況下被訪問。
Java中static方法不能被覆蓋性锭,因?yàn)榉椒ǜ采w是基于運(yùn)行時(shí)動(dòng)態(tài)綁定的赠潦,而static方法是編譯時(shí)靜態(tài)綁定的。static方法跟類的任何實(shí)例都不相關(guān)草冈,所以概念上不適用她奥。
java中也不可以覆蓋private的方法瓮增,因?yàn)閜rivate修飾的變量和方法只能在當(dāng)前類中使用,如果是其他的類繼承當(dāng)前類是不能訪問到private變量或方法的哩俭,當(dāng)然也不能覆蓋绷跑。
4. 是否可以在static環(huán)境中訪問非static變量?
參考答案:
static變量在Java中是屬于類的凡资,它在所有的實(shí)例中的值是一樣的砸捏。當(dāng)類被Java虛擬機(jī)載入的時(shí)候,會(huì)對(duì)static變量進(jìn)行初始化隙赁。如果你的代碼嘗試不用實(shí)例來訪問非static的變量垦藏,編譯器會(huì)報(bào)錯(cuò),因?yàn)檫@些變量還沒有被創(chuàng)建出來伞访,還沒有跟任何實(shí)例關(guān)聯(lián)上掂骏。
5. Java支持的數(shù)據(jù)類型有哪些?什么是自動(dòng)拆裝箱厚掷?
參考答案:
Java語言支持的8種基本數(shù)據(jù)類型是:
- byte
- short
- int
- long
- float
- double
- boolean
- char
自動(dòng)裝箱是Java編譯器在基本數(shù)據(jù)類型和對(duì)應(yīng)的對(duì)象包裝類型之間做的一個(gè)轉(zhuǎn)化弟灼。比如:把int轉(zhuǎn)化成Integer,double轉(zhuǎn)化成Double冒黑,等等田绑。反之就是自動(dòng)拆箱。
Java支持的數(shù)據(jù)類型包括兩種:一種是基本數(shù)據(jù)類型抡爹,包含byte掩驱,char,short, boolean ,int , long, float,double;另一種是引用類型:如String等,其實(shí)是對(duì)象的引用冬竟,JVM中虛擬棧中存的是對(duì)象的地址昙篙,創(chuàng)建的對(duì)象實(shí)質(zhì)在堆中,通過地址來找到堆中的對(duì)象的過程诱咏,即為引用類型苔可。自動(dòng)裝箱就是Java編譯器在基本數(shù)據(jù)類型和對(duì)應(yīng)的對(duì)象包裝類型間的轉(zhuǎn)化,即int轉(zhuǎn)化為Integer,自動(dòng)拆箱是Integer調(diào)用其方法將其轉(zhuǎn)化為int的過程
6. Java中的方法覆蓋(Overriding)和方法重載(Overload)是什么意思袋狞?
參考答案:
Java中的方法重載發(fā)生在同一個(gè)類里面兩個(gè)或者是多個(gè)方法的方法名相同但是參數(shù)不同的情況焚辅。與此相對(duì),方法覆蓋是說子類重新定義了父類的方法苟鸯。方法覆蓋必須有相同的方法名同蜻,參數(shù)列表和返回類型。覆蓋者可能不會(huì)限制它所覆蓋的方法的訪問早处。
7. Java中湾蔓,什么是構(gòu)造方法?什么是構(gòu)造方法重載砌梆?什么是復(fù)制構(gòu)造方法默责?
參考答案:
當(dāng)新對(duì)象被創(chuàng)建的時(shí)候贬循,構(gòu)造方法會(huì)被調(diào)用。每一個(gè)類都有構(gòu)造方法桃序。在程序員沒有給類提供構(gòu)造方法的情況下杖虾,Java編譯器會(huì)為這個(gè)類創(chuàng)建一個(gè)默認(rèn)的構(gòu)造方法。
Java中構(gòu)造方法重載和方法重載很相似媒熊∑媸剩可以為一個(gè)類創(chuàng)建多個(gè)構(gòu)造方法。每一個(gè)構(gòu)造方法必須有它自己唯一的參數(shù)列表芦鳍。
Java不支持像C++中那樣的復(fù)制構(gòu)造方法嚷往,這個(gè)不同點(diǎn)是因?yàn)槿绻悴蛔约簩憳?gòu)造方法的情況下,Java不會(huì)創(chuàng)建默認(rèn)的復(fù)制構(gòu)造方法柠衅。
8. Java支持多繼承么间影?
參考答案:
Java中類不支持多繼承,只支持單繼承(即一個(gè)類只有一個(gè)父類)茄茁。 但是java中的接口支持多繼承,巩割,即一個(gè)子接口可以有多個(gè)父接口裙顽。(接口的作用是用來擴(kuò)展對(duì)象的功能,一個(gè)子接口繼承多個(gè)父接口宣谈,說明子接口擴(kuò)展了多個(gè)功能愈犹,當(dāng)類實(shí)現(xiàn)接口時(shí),類就擴(kuò)展了相應(yīng)的功能)闻丑。
9. 接口和抽象類的區(qū)別是什么漩怎?
參考答案:
Java提供和支持創(chuàng)建抽象類和接口。它們的實(shí)現(xiàn)有共同點(diǎn)嗦嗡,不同點(diǎn)在于:
- 接口中所有的方法隱含的都是抽象的勋锤。而抽象類則可以同時(shí)包含抽象和非抽象的方法。
- 類可以實(shí)現(xiàn)很多個(gè)接口侥祭,但是只能繼承一個(gè)抽象類
- 類可以不實(shí)現(xiàn)抽象類和接口聲明的所有方法叁执,當(dāng)然,在這種情況下矮冬,類也必須得聲明成是抽象的谈宛。
- 抽象類可以在不提供接口方法實(shí)現(xiàn)的情況下實(shí)現(xiàn)接口。
- Java接口中聲明的變量默認(rèn)都是final的胎署。抽象類可以包含非final的變量吆录。
- Java接口中的成員函數(shù)默認(rèn)是public的。抽象類的成員函數(shù)可以是private琼牧,protected或者是public恢筝。
- 接口是絕對(duì)抽象的哀卫,不可以被實(shí)例化,抽象類也不可以被實(shí)例化滋恬。
- 也可以參考JDK8中抽象類和接口的區(qū)別
10. 什么是值傳遞和引用傳遞聊训?
參考答案:
值傳遞是對(duì)基本型變量而言的,傳遞的是該變量的一個(gè)副本,改變副本不影響原變量.
引用傳遞一般是對(duì)于對(duì)象型變量而言的,傳遞的是該對(duì)象地址的一個(gè)副本, 并不是原對(duì)象本身 。
一般認(rèn)為,java內(nèi)的基礎(chǔ)類型數(shù)據(jù)傳遞都是值傳遞. java中實(shí)例對(duì)象的傳遞是引用傳遞
11. 進(jìn)程和線程的區(qū)別是什么恢氯?
參考答案:
進(jìn)程是執(zhí)行著的應(yīng)用程序带斑,而線程是進(jìn)程內(nèi)部的一個(gè)執(zhí)行序列。一個(gè)進(jìn)程可以有多個(gè)線程勋拟。線程又叫做輕量級(jí)進(jìn)程勋磕。
線程與進(jìn)程的區(qū)別歸納:
- 地址空間和其它資源:進(jìn)程間相互獨(dú)立,同一進(jìn)程的各線程間共享敢靡。某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見挂滓。
- 通信:進(jìn)程間通信IPC,線程間可以直接讀寫進(jìn)程數(shù)據(jù)段(如全局變量)來進(jìn)行通信——需要進(jìn)程同步和互斥手段的輔助啸胧,以保證數(shù)據(jù)的一致性赶站。
- 調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多。
- 在多線程OS中纺念,進(jìn)程不是一個(gè)可執(zhí)行的實(shí)體贝椿。
12. 創(chuàng)建線程有幾種不同的方式?你喜歡哪一種陷谱?為什么烙博?
參考答案:
有4種方式可以用來創(chuàng)建線程:
- 繼承Thread類
- 實(shí)現(xiàn)Runnable接口
- 應(yīng)用程序可以使用Executor框架來創(chuàng)建線程池
- 實(shí)現(xiàn)Runnable接口這種方式更受歡迎,因?yàn)檫@不需要繼承Thread類烟逊。在應(yīng)用設(shè)計(jì)中已經(jīng)繼承了別的對(duì)象的情況下渣窜,這需要多繼承(而Java不支持多繼承),只能實(shí)現(xiàn)接口宪躯。同時(shí)乔宿,線程池也是非常高效的,很容易實(shí)現(xiàn)和使用访雪。
- 還有一種方式是實(shí)現(xiàn)Callable接口
13. 概括的解釋下線程的幾種可用狀態(tài)予颤。
參考答案:
新建( new ):新創(chuàng)建了一個(gè)線程對(duì)象。
可運(yùn)行( runnable ):線程對(duì)象創(chuàng)建后冬阳,其他線程(比如 main 線程)調(diào)用了該對(duì)象 的 start ()方法蛤虐。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中肝陪,獲 取 cpu 的使用權(quán) 驳庭。
運(yùn)行( running ):可運(yùn)行狀態(tài)( runnable )的線程獲得了 cpu 時(shí)間片( timeslice ) ,執(zhí)行程序代碼。
阻塞( block ):阻塞狀態(tài)是指線程因?yàn)槟撤N原因放棄了 cpu 使用權(quán)饲常,也即讓出了 cpu timeslice 蹲堂,暫時(shí)停止運(yùn)行。直到線程進(jìn)入可運(yùn)行( runnable )狀態(tài)贝淤,才有 機(jī)會(huì)再次獲得 cpu timeslice 轉(zhuǎn)到運(yùn)行( running )狀態(tài)柒竞。阻塞的情況分三種:
- a. 等待阻塞:運(yùn)行( running )的線程執(zhí)行 o . wait ()方法, JVM 會(huì)把該線程放 入等待隊(duì)列( waitting queue )中播聪。
- b. 同步阻塞:運(yùn)行( running )的線程在獲取對(duì)象的同步鎖時(shí)朽基,若該同步鎖 被別的線程占用,則 JVM 會(huì)把該線程放入鎖池( lock pool )中离陶。
- c. 其他阻塞: 運(yùn)行( running )的線程執(zhí)行 Thread . sleep ( long ms )或 t . join ()方法稼虎,或者發(fā)出了 I / O 請(qǐng)求時(shí), JVM 會(huì)把該線程置為阻塞狀態(tài)招刨。 當(dāng) sleep ()狀態(tài)超時(shí)霎俩、 join ()等待線程終止或者超時(shí)、或者 I / O 處理完畢時(shí)沉眶,線程重新轉(zhuǎn)入可運(yùn)行( runnable )狀態(tài)打却。
- 死亡( dead ):線程 run ()、 main () 方法執(zhí)行結(jié)束谎倔,或者因異常退出了 run ()方法柳击,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生传藏。
14. 同步方法和同步代碼塊的區(qū)別是什么?
參考答案:
區(qū)別:
- 同步方法默認(rèn)用this或者當(dāng)前類class對(duì)象作為鎖彤守;
- 同步代碼塊可以選擇以什么來加鎖毯侦,比同步方法要更細(xì)顆粒度,我們可以選擇只同步會(huì)發(fā)生同步問題的部分代碼而不是整個(gè)方法具垫;
- 同步方法使用關(guān)鍵字 synchronized修飾方法侈离,而同步代碼塊主要是修飾需要進(jìn)行同步的代碼,用 synchronized(object){代碼內(nèi)容}進(jìn)行修飾筝蚕;
15. 在監(jiān)視器(Monitor)內(nèi)部卦碾,是如何做線程同步的?程序應(yīng)該做哪種級(jí)別的同步起宽?
參考答案:
監(jiān)視器和鎖在Java虛擬機(jī)中是一塊使用的洲胖。監(jiān)視器監(jiān)視一塊同步代碼塊,確保一次只有一個(gè)線程執(zhí)行同步代碼塊坯沪。每一個(gè)監(jiān)視器都和一個(gè)對(duì)象引用相關(guān)聯(lián)绿映。線程在獲取鎖之前不允許執(zhí)行同步代碼。
16. 什么是死鎖(deadlock)?
參考答案:
所謂死鎖是指多個(gè)進(jìn)程因競爭資源而造成的一種僵局(互相等待)叉弦,若無外力作用丐一,這些進(jìn)程都將無法向前推進(jìn)。死鎖產(chǎn)生的4個(gè)必要條件:
- 互斥條件:進(jìn)程要求對(duì)所分配的資源(如打印機(jī))進(jìn)行排他性控制淹冰,即在一段時(shí)間內(nèi)某 資源僅為一個(gè)進(jìn)程所占有库车。此時(shí)若有其他進(jìn)程請(qǐng)求該資源,則請(qǐng)求進(jìn)程只能等待樱拴。
- 不剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前柠衍,不能被其他進(jìn)程強(qiáng)行奪走,即只能 由獲得該資源的進(jìn)程自己來釋放(只能是主動(dòng)釋放)疹鳄。
- 請(qǐng)求和保持條件:進(jìn)程已經(jīng)保持了至少一個(gè)資源拧略,但又提出了新的資源請(qǐng)求,而該資源 已被其他進(jìn)程占有瘪弓,此時(shí)請(qǐng)求進(jìn)程被阻塞垫蛆,但對(duì)自己已獲得的資源保持不放。
- 循環(huán)等待條件:存在一種進(jìn)程資源的循環(huán)等待鏈腺怯,鏈中每一個(gè)進(jìn)程已獲得的資源同時(shí)被 鏈中下一個(gè)進(jìn)程所請(qǐng)求袱饭。
17. 如何確保N個(gè)線程可以訪問N個(gè)資源同時(shí)又不導(dǎo)致死鎖?
參考答案:
使用多線程的時(shí)候呛占,一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序虑乖,并強(qiáng)制線程按照指定的順序獲取鎖。因此晾虑,如果所有的線程都是以同樣的順序加鎖和釋放鎖疹味,就不會(huì)出現(xiàn)死鎖了。
18. Java集合類框架的基本接口有哪些帜篇?
參考答案:
集合類接口指定了一組叫做元素的對(duì)象糙捺。集合類接口的每一種具體的實(shí)現(xiàn)類都可以選擇以它自己的方式對(duì)元素進(jìn)行保存和排序。有的集合類允許重復(fù)的鍵笙隙,有些不允許洪灯。
Java集合類提供了一套設(shè)計(jì)良好的支持對(duì)一組對(duì)象進(jìn)行操作的接口和類。Java集合類里面最基本的接口有:
- Collection:代表一組對(duì)象竟痰,每一個(gè)對(duì)象都是它的子元素签钩。
- Set:不包含重復(fù)元素的Collection。
- List:有順序的collection坏快,并且可以包含重復(fù)元素铅檩。
- Map:可以把鍵(key)映射到值(value)的對(duì)象,鍵不能重復(fù)莽鸿。
19. 為什么集合類沒有實(shí)現(xiàn)Cloneable和Serializable接口柠并?
參考答案:
克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實(shí)現(xiàn)相關(guān)的。因此,應(yīng)該由集合類的具體實(shí)現(xiàn)來決定如何被克隆或者是序列化臼予。
20. 什么是迭代器(Iterator)鸣戴?
參考答案:
Iterator接口提供了很多對(duì)集合元素進(jìn)行迭代的方法。每一個(gè)集合類都包含了可以返回迭代器實(shí)例的迭代方法粘拾。迭代器可以在迭代的過程中刪除底層集合的元素,但是不可以直接調(diào)用集合的remove(Object Obj)刪除窄锅,可以通過迭代器的remove()方法刪除。
21. Iterator和ListIterator的區(qū)別是什么缰雇?
參考答案:
下面列出了他們的區(qū)別:
- Iterator可用來遍歷Set和List集合入偷,但是ListIterator只能用來遍歷List。
- Iterator對(duì)集合只能是前向遍歷械哟,ListIterator既可以前向也可以后向疏之。
- ListIterator實(shí)現(xiàn)了Iterator接口,并包含其他的功能暇咆,比如:增加元素锋爪,替換元素,獲取前一個(gè)和后一個(gè)元素的索引爸业,等等其骄。
22. 快速失敗(fail-fast)和安全失敗(fail-safe)的區(qū)別是什么?
參考答案:
①:快速失敵犊酢(fail—fast)
- 在用迭代器遍歷一個(gè)集合對(duì)象時(shí)拯爽,如果遍歷過程中對(duì)集合對(duì)象的結(jié)構(gòu)進(jìn)行了修改(增加、刪除)钧忽,則會(huì)拋出Concurrent Modification Exception毯炮。
- 原理:迭代器在遍歷時(shí)直接訪問集合中的內(nèi)容,并且在遍歷過程中使用一個(gè) modCount 變量耸黑。集合在被遍歷期間如果結(jié)構(gòu)發(fā)生變化桃煎,就會(huì)改變modCount的值。每當(dāng)?shù)魇褂胔ashNext()/next()遍歷下一個(gè)元素之前崎坊,都會(huì)檢測modCount變量是否為expectedmodCount值备禀,是的話就返回遍歷洲拇;否則拋出異常奈揍,終止遍歷跷究。
- 注意:這里異常的拋出條件是檢測到 modCount今野!=expectedmodCount 這個(gè)條件。如果集合發(fā)生變化時(shí)修改modCount值剛好又設(shè)置為了expectedmodCount值囊陡,則異常不會(huì)拋出纽乱。因此蛾绎,不能依賴于這個(gè)異常是否拋出而進(jìn)行并發(fā)操作的編程,這個(gè)異常只建議用于檢測并發(fā)修改的bug。
- 場景:java.util包下的集合類都是快速失敗的租冠,不能在多線程下發(fā)生并發(fā)修改(迭代過程中被修改)鹏倘。
②:安全失敗(fail—safe)
- 采用安全失敗機(jī)制的集合容器顽爹,在遍歷時(shí)不是直接在集合內(nèi)容上訪問的纤泵,而是先復(fù)制原有集合內(nèi)容,在拷貝的集合上進(jìn)行遍歷镜粤。
- 原理:由于迭代時(shí)是對(duì)原集合的拷貝進(jìn)行遍歷捏题,所以在遍歷過程中對(duì)原集合所作的修改并不能被迭代器檢測到,所以不會(huì)觸發(fā)Concurrent Modification Exception肉渴。
- 缺點(diǎn):基于拷貝內(nèi)容的優(yōu)點(diǎn)是避免了Concurrent Modification Exception公荧,但同樣地,迭代器并不能訪問到修改后的內(nèi)容同规,即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝循狰,在遍歷期間原集合發(fā)生的修改迭代器是不知道的。
- 場景:java.util.concurrent包下的容器都是安全失敗捻浦,可以在多線程下并發(fā)使用晤揣,并發(fā)修改。
23. Java中的HashMap的工作原理是什么朱灿?
參考答案:
Java中的HashMap是以鍵值對(duì)(key-value)的形式存儲(chǔ)元素的昧识。HashMap需要一個(gè)hash函數(shù),它使用hashCode()和equals()方法來向集合/從集合添加和檢索元素盗扒。當(dāng)調(diào)用put()方法的時(shí)候跪楞,HashMap會(huì)計(jì)算key的hash值,然后把鍵值對(duì)存儲(chǔ)在集合中合適的索引上侣灶。如果key已經(jīng)存在了甸祭,value會(huì)被更新成新值。HashMap的一些重要的特性是它的容量(capacity)褥影,負(fù)載因子(load factor)和擴(kuò)容極限(threshold resizing)池户。
24. hashCode()和equals()方法的重要性體現(xiàn)在什么地方?
參考答案:
Java中的HashMap使用hashCode()和equals()方法來確定鍵值對(duì)的索引凡怎,當(dāng)根據(jù)鍵獲取值的時(shí)候也會(huì)用到這兩個(gè)方法校焦。如果沒有正確的實(shí)現(xiàn)這兩個(gè)方法,兩個(gè)不同的鍵可能會(huì)有相同的hash值统倒,因此寨典,可能會(huì)被集合認(rèn)為是相等的。而且房匆,這兩個(gè)方法也用來發(fā)現(xiàn)重復(fù)元素耸成。所以這兩個(gè)方法的實(shí)現(xiàn)對(duì)HashMap的精確性和正確性是至關(guān)重要的报亩。
25. HashMap和Hashtable有什么區(qū)別?
參考答案:
HashMap和Hashtable都實(shí)現(xiàn)了Map接口井氢,因此很多特性非常相似弦追。但是,他們有以下不同點(diǎn):
- HashMap允許鍵和值是null花竞,而Hashtable不允許鍵或者值是null骗卜。
- Hashtable是同步的,而HashMap不是左胞。因此寇仓,HashMap更適合于單線程環(huán)境,而Hashtable適合于多線程環(huán)境烤宙。
- HashMap提供了可供應(yīng)用迭代的鍵的集合遍烦,因此,HashMap是快速失敗的躺枕。另一方面服猪,Hashtable提供了對(duì)鍵的列舉(Enumeration)。
- 一般認(rèn)為Hashtable是一個(gè)遺留的類拐云。
26. 數(shù)組(Array)和列表(ArrayList)有什么區(qū)別罢猪?什么時(shí)候應(yīng)該使用Array而不是ArrayList?
參考答案:
下面列出了Array和ArrayList的不同點(diǎn):
- Array可以包含基本類型和對(duì)象類型叉瘩,ArrayList只能包含對(duì)象類型膳帕。
- Array大小是固定的,ArrayList的大小是動(dòng)態(tài)變化的薇缅。
- ArrayList提供了更多的方法和特性危彩,比如:addAll(),removeAll()泳桦,iterator()等等汤徽。
- 對(duì)于基本類型數(shù)據(jù),集合使用自動(dòng)裝箱來減少編碼工作量灸撰。但是谒府,當(dāng)處理固定大小的基本數(shù)據(jù)類型的時(shí)候,這種方式相對(duì)比較慢浮毯。
27. ArrayList和LinkedList有什么區(qū)別完疫?
參考答案:
ArrayList和LinkedList都實(shí)現(xiàn)了List接口,他們有以下的不同點(diǎn):
- ArrayList是基于索引的數(shù)據(jù)接口亲轨,它的底層是數(shù)組趋惨。它可以以O(shè)(1)時(shí)間復(fù)雜度對(duì)元素進(jìn)行隨機(jī)訪問鸟顺。與此對(duì)應(yīng)惦蚊,LinkedList是以元素列表的形式存儲(chǔ)它的數(shù)據(jù)器虾,每一個(gè)元素都和它的前一個(gè)和后一個(gè)元素鏈接在一起,在這種情況下蹦锋,查找某個(gè)元素的時(shí)間復(fù)雜度是O(n)兆沙。
- 相對(duì)于ArrayList,LinkedList的插入莉掂,添加葛圃,刪除操作速度更快,因?yàn)楫?dāng)元素被添加到集合任意位置的時(shí)候憎妙,不需要像數(shù)組那樣重新計(jì)算大小或者是更新索引库正。
- LinkedList比ArrayList更占內(nèi)存,因?yàn)長inkedList為每一個(gè)節(jié)點(diǎn)存儲(chǔ)了兩個(gè)引用厘唾,一個(gè)指向前一個(gè)元素褥符,一個(gè)指向下一個(gè)元素。
- 也可以參考ArrayList vs. LinkedList抚垃。
28. Comparable和Comparator接口是干什么的喷楣?列出它們的區(qū)別。
參考答案:
Java提供了只包含一個(gè)compareTo()方法的Comparable接口鹤树。這個(gè)方法可以個(gè)給兩個(gè)對(duì)象排序铣焊。具體來說,它返回負(fù)數(shù)罕伯,0曲伊,正數(shù)來表明已經(jīng)存在的對(duì)象小于,等于追他,大于輸入對(duì)象熊昌。
Java提供了包含compare()和equals()兩個(gè)方法的Comparator接口。compare()方法用來給兩個(gè)輸入?yún)?shù)排序湿酸,返回負(fù)數(shù)婿屹,0,正數(shù)表明第一個(gè)參數(shù)是小于推溃,等于昂利,大于第二個(gè)參數(shù)。equals()方法需要一個(gè)對(duì)象作為參數(shù)铁坎,它用來決定輸入?yún)?shù)是否和comparator相等蜂奸。只有當(dāng)輸入?yún)?shù)也是一個(gè)comparator并且輸入?yún)?shù)和當(dāng)前comparator的排序結(jié)果是相同的時(shí)候,這個(gè)方法才返回true硬萍。
29. 什么是Java優(yōu)先級(jí)隊(duì)列(Priority Queue)扩所?
參考答案:
PriorityQueue是一個(gè)基于優(yōu)先級(jí)堆的無界隊(duì)列,它的元素是按照自然順序(natural order)排序的朴乖。在創(chuàng)建的時(shí)候祖屏,我們可以給它提供一個(gè)負(fù)責(zé)給元素排序的比較器助赞。PriorityQueue不允許null值,因?yàn)樗麄儧]有自然順序袁勺,或者說他們沒有任何的相關(guān)聯(lián)的比較器雹食。最后,PriorityQueue不是線程安全的期丰,入隊(duì)和出隊(duì)的時(shí)間復(fù)雜度是O(log(n))群叶。
30. 你了解大O符號(hào)(big-O notation)么?你能給出不同數(shù)據(jù)結(jié)構(gòu)的例子么钝荡?
參考答案:
大O符號(hào)描述了當(dāng)數(shù)據(jù)結(jié)構(gòu)里面的元素增加的時(shí)候街立,算法的規(guī)模或者是一個(gè)漸進(jìn)上界 埠通。
大O符號(hào)也可用來描述其他的行為几晤,比如:內(nèi)存消耗。因?yàn)榧项悓?shí)際上是數(shù)據(jù)結(jié)構(gòu)植阴,我們一般使用大O符號(hào)基于時(shí)間蟹瘾,內(nèi)存和性能來選擇最好的實(shí)現(xiàn)。大O符號(hào)可以對(duì)大量數(shù)據(jù)的性能給出一個(gè)很好的說明掠手。
31. 如何權(quán)衡是使用無序的數(shù)組還是有序的數(shù)組憾朴?
參考答案:
有序數(shù)組最大的好處在于查找的時(shí)間復(fù)雜度是O(log n),而無序數(shù)組是O(n)喷鸽。有序數(shù)組的缺點(diǎn)是插入操作的時(shí)間復(fù)雜度是O(n)众雷,因?yàn)橹荡蟮脑匦枰笠苿?dòng)來給新元素騰位置。相反做祝,無序數(shù)組的插入時(shí)間復(fù)雜度是常量O(1)砾省。
32. Java集合類框架的最佳實(shí)踐有哪些?
參考答案:
根據(jù)應(yīng)用的需要正確選擇要使用的集合的類型對(duì)性能非常重要混槐,比如:假如元素的數(shù)量是固定的编兄,而且能事先知道,我們就應(yīng)該用Array而不是ArrayList声登。
有些集合類允許指定初始容量狠鸳。因此,如果我們能估計(jì)出存儲(chǔ)的元素的數(shù)目悯嗓,我們可以設(shè)置初始容量來避免重新計(jì)算hash值或者是擴(kuò)容件舵。
為了類型安全,可讀性和健壯性的原因總是要使用泛型脯厨。同時(shí)铅祸,使用泛型還可以避免運(yùn)行時(shí)的ClassCastException。
使用JDK提供的不變類(immutable class)作為Map的鍵可以避免為我們自己的類實(shí)現(xiàn)hashCode()和equals()方法合武。
編程的時(shí)候接口優(yōu)于實(shí)現(xiàn)临梗。
底層的集合實(shí)際上是空的情況下涡扼,返回長度是0的集合或者是數(shù)組,不要返回null夜焦。
33. Enumeration接口和Iterator接口的區(qū)別有哪些?
參考答案:
Enumeration速度是Iterator的2倍岂贩,同時(shí)占用更少的內(nèi)存茫经。但是,Iterator遠(yuǎn)遠(yuǎn)比Enumeration安全萎津,因?yàn)槠渌€程不能夠修改正在被iterator遍歷的集合里面的對(duì)象卸伞。同時(shí),Iterator允許調(diào)用者刪除底層集合里面的元素锉屈,這對(duì)Enumeration來說是不可能的荤傲。
34. HashSet和TreeSet有什么區(qū)別?
參考答案:
HashSet是由一個(gè)hash表來實(shí)現(xiàn)的颈渊,因此遂黍,它的元素是無序的。add()俊嗽,remove()雾家,contains()方法的時(shí)間復(fù)雜度是O(1)。
另一方面绍豁,TreeSet是由一個(gè)樹形的結(jié)構(gòu)來實(shí)現(xiàn)的芯咧,它里面的元素是有序的。因此竹揍,add()敬飒,remove(),contains()方法的時(shí)間復(fù)雜度是O(logn)芬位。
35. Java中垃圾回收有什么目的无拗?什么時(shí)候進(jìn)行垃圾回收?
參考答案:
垃圾回收是在內(nèi)存中存在沒有引用的對(duì)象或超過作用域的對(duì)象時(shí)進(jìn)行昧碉。
垃圾回收的目的是識(shí)別并且丟棄應(yīng)用不再使用的對(duì)象來釋放和重用資源蓝纲。
36. System.gc()和Runtime.gc()會(huì)做什么事情?
參考答案:
這兩個(gè)方法用來提示JVM要進(jìn)行垃圾回收晌纫。但是税迷,立即開始還是延遲進(jìn)行垃圾回收是取決于JVM的。
37. finalize()方法什么時(shí)候被調(diào)用锹漱?析構(gòu)函數(shù)(finalization)的目的是什么箭养?
參考答案:
垃圾回收器(garbage collector)決定回收某對(duì)象時(shí),就會(huì)運(yùn)行該對(duì)象的finalize()方法 但是在Java中很不幸哥牍,如果內(nèi)存總是充足的毕泌,那么垃圾回收可能永遠(yuǎn)不會(huì)進(jìn)行喝检,也就是說filalize()可能永遠(yuǎn)不被執(zhí)行,顯然指望它做收尾工作是靠不住的撼泛。 那么finalize()究竟是做什么的呢挠说?它最主要的用途是回收特殊渠道申請(qǐng)的內(nèi)存。Java程序有垃圾回收器愿题,所以一般情況下內(nèi)存問題不用程序員操心损俭。但有一種JNI(Java Native Interface)調(diào)用non-Java程序(C或C++),finalize()的工作就是回收這部分的內(nèi)存潘酗。
38. 如果對(duì)象的引用被置為null杆兵,垃圾收集器是否會(huì)立即釋放對(duì)象占用的內(nèi)存?
參考答案:
不會(huì)仔夺,在下一個(gè)垃圾回收周期中琐脏,這個(gè)對(duì)象將是可被回收的。
39. Java堆的結(jié)構(gòu)是什么樣子的缸兔?什么是堆中的永久代(Perm Gen space)?
參考答案:
JVM的堆是運(yùn)行時(shí)數(shù)據(jù)區(qū)日裙,所有類的實(shí)例和數(shù)組都是在堆上分配內(nèi)存。它在JVM啟動(dòng)的時(shí)候被創(chuàng)建惰蜜。對(duì)象所占的堆內(nèi)存是由自動(dòng)內(nèi)存管理系統(tǒng)也就是垃圾收集器回收阅签。
堆內(nèi)存是由存活和死亡的對(duì)象組成的。存活的對(duì)象是應(yīng)用可以訪問的蝎抽,不會(huì)被垃圾回收政钟。死亡的對(duì)象是應(yīng)用不可訪問尚且還沒有被垃圾收集器回收掉的對(duì)象。一直到垃圾收集器把這些對(duì)象回收掉之前樟结,他們會(huì)一直占據(jù)堆內(nèi)存空間养交。
永久代是用于存放靜態(tài)文件,如Java類瓢宦、方法等碎连。持久代對(duì)垃圾回收沒有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class驮履,例如Hibernate 等鱼辙,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類,永久代中一般包含:
- 類的方法(字節(jié)碼...)
- 類名(Sring對(duì)象)
- .class文件讀到的常量信息
- class對(duì)象相關(guān)的對(duì)象列表和類型列表 (e.g., 方法對(duì)象的array).
- JVM創(chuàng)建的內(nèi)部對(duì)象
- JIT編譯器優(yōu)化用的信息
40. 串行(serial)收集器和吞吐量(throughput)收集器的區(qū)別是什么玫镐?
參考答案:
吞吐量收集器使用并行版本的新生代垃圾收集器倒戏,它用于中等規(guī)模和大規(guī)模數(shù)據(jù)的應(yīng)用程序。而串行收集器對(duì)大多數(shù)的小應(yīng)用(在現(xiàn)代處理器上需要大概100M左右的內(nèi)存)就足夠了恐似。