久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當(dāng)前位置:首頁(yè)>職場(chǎng)>java高級(jí)經(jīng)典面試題及答案(Java面試題大全帶答案)

java高級(jí)經(jīng)典面試題及答案(Java面試題大全帶答案)

發(fā)布時(shí)間:2024-01-24閱讀(13)

導(dǎo)讀本人發(fā)現(xiàn)網(wǎng)上雖然有不少Java相關(guān)的面試題,但第一未必全,第二未必有答案,第三雖然有答案,但未必能在面試中說,所以在本文里,會(huì)不斷收集各種面試題,并站在面試....

本人發(fā)現(xiàn)網(wǎng)上雖然有不少Java相關(guān)的面試題,但第一未必全,第二未必有答案,第三雖然有答案,但未必能在面試中說,所以在本文里,會(huì)不斷收集各種面試題,并站在面試官的立場(chǎng)上,給出我自己的答案。

第一部分、Java 基礎(chǔ)

1. JDK 和 JRE 有什么區(qū)別?

JDK是java的開發(fā)工具包,有JDK8,9甚至到14的差別,安裝以后,不僅包含了java 的開發(fā)環(huán)境,比如java.exe,還包含了運(yùn)行環(huán)境(jre)相關(guān)包。

JRE是java 運(yùn)行環(huán)境,一般裝好JDK后,系統(tǒng)里會(huì)有對(duì)應(yīng)的JRE環(huán)境。

2. 說下你對(duì)== 和 equals 的認(rèn)識(shí),它們有什么差別?

對(duì)于==

基本類型,比如int等,==比較的是值是否相同;

引用類型,比如自定義對(duì)象:比較地址是否相同;

尤其地,對(duì)常量,由于常量被放在常量池里管理,所以對(duì)String等常量,==也是比較值

對(duì)于equals 方法

對(duì)于String,ArrayList等,equals方法是比較值;

但在Object里,equals還是比較地址;

如果自己創(chuàng)建了一個(gè)類,但沒有重寫equals方法,還是會(huì)比較地址

3. 如果兩個(gè)對(duì)象的 hashCode值一樣,則它們用equals()比較也是為 true,是不是?

不是

hashCode是定義在HashMap里,用以快速索引;

Object里,hashCode和equals是兩個(gè)不同的方法,默認(rèn)hashCode是返回對(duì)象地址,equals方法也是對(duì)比地址;

兩者不是一回事,可以通過重寫對(duì)象的hashCode方法,讓不同值的對(duì)象有相同的hashCode,但它們的equals方法未必相同

4. 綜合說下final的作用

修飾在類上,該類不能被繼承。

修飾在方法上,該方法不能被重寫。

修飾在變量上,叫常量,該常量必須初始化,初始化之后值就不能被修改,而常量一般全都是用大寫來命名。

5. Math.round(-2.5) 等于多少?

結(jié)果是-2,因?yàn)樵摵瘮?shù)在數(shù)軸上,表現(xiàn)是向右取整,由此 Math.round(1.3) = 2。

6. String 是基本數(shù)據(jù)類型嗎?

String 不是基礎(chǔ)類型,基礎(chǔ)類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 是對(duì)象。

但說到這里,你要多說句。

String s = "abc";,這是常量,放常量池管理。

不建議頻繁對(duì)String修改,因?yàn)闀?huì)產(chǎn)生內(nèi)存碎片。

7. 對(duì)字符串的都有哪些方法?詳細(xì)說明下。

具體有String、StringBuffer和StringBuilder這三個(gè)類。

String是不可變類,每次操作都會(huì)生成新的String對(duì)象,并將結(jié)果指針指向新的對(duì)象,由此會(huì)產(chǎn)生內(nèi)存碎片。

如果要頻繁對(duì)字符串修改,建議采用StringBuffer 和 StringBuilder。

StringBuffer 和 StringBuilder的差別在于,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,由于無需維護(hù)線程安全的操作,所以StringBuilder 的性能要高于 StringBuffer,所以在單線程環(huán)境下推薦使用 StringBuilder,多線程環(huán)境下推薦使用 StringBuffer。由于大多數(shù)環(huán)境下是單線程,所以大多是用 StringBuilder。

8. String str="abc"與 String str=new String("abc")的定義方法一樣嗎?

不一樣,String str="abc"的方式,java 虛擬機(jī)會(huì)將其分配到常量池中;所以建議這種寫法。

而 String str=new String("abc") 則會(huì)被分到堆內(nèi)存中,如果再頻繁修改,會(huì)導(dǎo)致內(nèi)存碎片。

9. 如何將字符串反轉(zhuǎn)?

使用 StringBuilder 或 stringBuffer 的 reverse() 方法。

10. String 類的常用方法都有那些?

indexOf():返回指定字符的索引。

length():返回字符串長(zhǎng)度。

equals():字符串比較。

replace():字符串替換。

trim():去除字符串兩端空白。

split():分割字符串,返回一個(gè)分割后的字符串?dāng)?shù)組。

toLowerCase():將字符串轉(zhuǎn)成小寫字母。

toUpperCase():將字符串轉(zhuǎn)成大寫字符。

substring():截取字符串。

你面試時(shí),說出其中的一兩個(gè)即可,但需要說明如下的意思。

String s = "abc";,這是常量,放常量池管理。

不建議頻繁對(duì)String修改,因?yàn)闀?huì)產(chǎn)生內(nèi)存碎片。

如果要頻繁對(duì)字符串修改,建議采用StringBuffer 和 StringBuilder

11. 抽象類必須要有抽象方法嗎?

不需要的,抽象類不一定非要有抽象方法。但從面向?qū)ο笏枷虢嵌葋矸治觯唤ㄗh這樣做。

因?yàn)樵谠O(shè)計(jì)的時(shí)候,會(huì)把邏輯上存在但實(shí)際不存在的類設(shè)置成抽象類,比如動(dòng)物類,畢竟不能直接展示“動(dòng)物”。

正因?yàn)椴淮嬖冢岳锩娴姆椒ㄎ幢啬軐?shí)現(xiàn),比如“奔跑”方法,所以此類方法需要設(shè)置成沒方法體的抽象方法。

如果在抽象類里方法,全都有方法體,那么要么是抽象類設(shè)計(jì)不當(dāng),或者實(shí)現(xiàn)了未必能實(shí)現(xiàn)的方法,所以建議修改。

12. 一般的類和抽象類有哪些區(qū)別?

一般的類不能包含沒有方法體的抽象方法,而抽象類可以包含抽象方法。

抽象類不能直接用new來實(shí)例化,普通類可以直接實(shí)例化。

13. 抽象類能使用 final 修飾嗎?

首先說明,語法上不能,然后再進(jìn)一步從面向?qū)ο笏枷虢嵌葋碚f明。

定義抽象類的本意是,讓其它類繼承的,從而進(jìn)一步完善對(duì)象。如果定義為 final 該類就不能被繼承,這樣就會(huì)有矛盾,所以 final 不能修飾抽象類。

14. 接口和抽象類有什么區(qū)別?

抽象類的子類要用 extends 來繼承;而實(shí)現(xiàn)接口要用 implements 。

抽象類可以定義構(gòu)造函數(shù),而接口不能。

抽象類里可以定義 main 方法,但接口不能有 main 方法。

實(shí)現(xiàn)數(shù)量:類可以實(shí)現(xiàn)很多個(gè)接口;但是只能繼承一個(gè)抽象類。

訪問修飾符:接口中的方法默認(rèn)使用 public 修飾;抽象類中的方法可以是任意訪問修飾符。

上述是從語法上來歸納,然后建議大家再?gòu)拿嫦驅(qū)ο笏枷氲慕嵌葋碚f明

抽象類是對(duì)邏輯的歸納,比如動(dòng)物類可以是抽象類,人類可以extends動(dòng)物這個(gè)抽象類。

而接口是對(duì)功能的歸納,比如可以定義一個(gè)“提供數(shù)據(jù)庫(kù)訪問功能”的 接口,在其中封裝若干操作數(shù)據(jù)庫(kù)的方法。

15. java 中 IO 流分為幾種?

按功能來分可以分輸入流(input)和輸出流(output)。從類型來分可以是字節(jié)流和字符流。

16. BIO、NIO、AIO 有什么區(qū)別?

BIO的英語全稱是Block IO, 同步阻塞式 IO,就是平常經(jīng)常使用的傳統(tǒng) IO,特點(diǎn)是簡(jiǎn)單方便,但并發(fā)處理能力低。

NIO,叫New IO, 同步非阻塞 IO,客戶端和服務(wù)器端通過 Channel(通道)通訊,實(shí)現(xiàn)了多路復(fù)用。

AIO,Asynchronous IO, 是 NIO 的升級(jí),實(shí)現(xiàn)了異步非堵塞 IO ,它是基于事件和回調(diào)機(jī)制。

17. Files的常用方法都有哪些?

Files.exists():檢測(cè)路徑是否存在。

Files.createFile():創(chuàng)建文件。

Files.createDirectory():創(chuàng)建文件夾。

Files.delete():刪除文件或文件夾。

Files.copy():復(fù)制文件。

Files.move():移動(dòng)文件,即復(fù)制后刪除。

Files.size():查看文件的個(gè)數(shù)。

Files.read():讀取文件。

Files.write():寫入文件。

第二部分,Java的集合,也叫容器

18. java 的集合容器都有哪些?

如下給出了大致的結(jié)構(gòu)

所有線性表對(duì)象的父類是Collection

有線性表類,比如ArrayList和Set等。

有鍵值對(duì)類,比如HashMap。

19. Collection 和 Collections 有什么區(qū)別?

Collection 是一個(gè)集合接口,是所有線性表對(duì)象的父類。

Collections是集合類的一個(gè)工具類,包含了對(duì)集合元素進(jìn)行排序和線程安全等各種操作方法。

20. List、Set、Map 之間的區(qū)別是什么?

21. HashMap 和 Hashtable 有什么區(qū)別?

首先說,兩者都是鍵值類的對(duì)象

HashTable線程安全的,而HashMap線程不安全的,大多數(shù)的場(chǎng)景是單線程環(huán)境,在單線程環(huán)境下,HashMap效率上比hashTable要高。

HashMap允許空鍵值,而hashTable不允許。

22. 如何決定使用 HashMap 還是 TreeMap?

對(duì)于在Map中進(jìn)行插入、刪除和定位元素這類操作,可以選HashMap。但如果你要對(duì)一個(gè)有序的key集合進(jìn)行遍歷,需要選TreeMap。

23. 說一下 HashMap 的實(shí)現(xiàn)原理?

HashMap是基于數(shù)據(jù)結(jié)構(gòu)里的散列表,在大數(shù)據(jù)情況下,能保證get的高效性。

HashMap不保證映射的順序,特別是它不保證該順序恒久不變。

HashMap實(shí)際上是一個(gè)“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體。

當(dāng)向Hashmap對(duì)象里put元素時(shí),會(huì)根據(jù)key的hashcode計(jì)算hash值,根據(jù)hash值得到這個(gè)元素在數(shù)組中的位置,如果該數(shù)組在該位置上已經(jīng)存放了其他元素,那么在這個(gè)位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放入鏈尾.如果數(shù)組中該位置沒有元素,就直接將該元素放到數(shù)組的該位置上。

注意Jdk 1.8中對(duì)HashMap的實(shí)現(xiàn)做了優(yōu)化,當(dāng)鏈表中的節(jié)點(diǎn)數(shù)據(jù)超過八個(gè)之后,該鏈表會(huì)轉(zhuǎn)為紅黑樹來提高查詢效率,從原來的O(n)到O(logn)

24. 說一下 HashSet 的實(shí)現(xiàn)原理?

HashSet在底層上,是由HashMap實(shí)現(xiàn)的

HashSet的值放在HashMap的key上

HashMap的value統(tǒng)一為PRESENT

25. ArrayList 和 LinkedList 的區(qū)別是什么?

ArrrayList底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機(jī)訪問,而 LinkedList 的底層數(shù)據(jù)結(jié)構(gòu)是雙向循環(huán)鏈表,不支持隨機(jī)訪問。

使用下標(biāo)訪問一個(gè)元素,ArrayList 的時(shí)間復(fù)雜度是 O(1),而 LinkedList 是 O(n)。

26. 如何做到數(shù)組和 List之間的轉(zhuǎn)換?

List對(duì)象轉(zhuǎn)換成為數(shù)組:可以調(diào)用ArrayList(或其它List)的toArray方法。

數(shù)組轉(zhuǎn)換成為L(zhǎng)ist:調(diào)用Arrays的asList方法。

27. ArrayList 和 Vector 的區(qū)別是什么?(面試大概率會(huì)問)

Vector是線程安全的,而ArrayList不是。所以在單線程情況下,建議使用ArrayList

在擴(kuò)容時(shí),Vector是擴(kuò)容100%,但ArrayList是50%,后者更節(jié)省內(nèi)存

結(jié)論:大多數(shù)開發(fā)場(chǎng)景是單線程環(huán)境,所以建議使用ArrayList

28. Array 和 ArrayList 有何區(qū)別?

Array能容納基本數(shù)據(jù)類型和自定義對(duì)象,而ArrayList只能容納自定義的對(duì)象,對(duì)于基本數(shù)據(jù)類型,需要轉(zhuǎn)換成封裝類才能存儲(chǔ)。

Array是指定大小的,要手動(dòng)擴(kuò)容,而ArrayList大小雖然可以在定義時(shí)指定,但遇到容量滿時(shí)會(huì)自動(dòng)擴(kuò)容。

Array沒有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

所以建議使用ArrayList

29. 在 Queue 中 poll()和 remove()有什么區(qū)別?

poll() 和 remove() 都是從隊(duì)列中取出一個(gè)元素,但是 poll() 在獲取元素失敗的時(shí)候會(huì)返回空,但是 remove() 失敗的時(shí)候會(huì)拋出異常。

30. 哪些集合類是線程安全的?

Vector:就比arraylist多了個(gè)同步化機(jī)制(線程安全),因?yàn)樾瘦^低,現(xiàn)在已經(jīng)不太建議使用。在web應(yīng)用中,特別是前臺(tái)頁(yè)面,往往效率(頁(yè)面響應(yīng)速度)是優(yōu)先考慮的。

Statck:堆棧類,先進(jìn)后出,項(xiàng)目中用得并不多。

Hashtable:就比hashmap多了個(gè)線程安全,所以建議使用HashMap。

enumeration:枚舉,所以現(xiàn)在建議用Iterator來迭代。

結(jié)論是,如果在單線程情況下,不建議使用這些線程安全對(duì)象。

31. 迭代器 Iterator 是什么?

迭代器是一種設(shè)計(jì)模式,也是一個(gè)對(duì)象,可以用來遍歷并選擇序列(比如ArrayList或HashMap)中的對(duì)象,而開發(fā)人員不需要了解該序列的底層結(jié)構(gòu)。

迭代器通常被稱為“輕量級(jí)”對(duì)象,因?yàn)閯?chuàng)建它的代價(jià)小。

32. Iterator 怎么用?有什么特點(diǎn)?

Iterator比較好用,而且只能單向移動(dòng):

(1) 使用方法iterator()要求容器返回一個(gè)Iterator。第一次調(diào)用Iterator的next()方法時(shí),它返回序列的第一個(gè)元素。比如list.iterator()

(2) 用next()得到序列中的下一個(gè)元素。

(3) 使用hasNext()檢查是否還有其它元素。

(4) 使用remove()將迭代器新返回的元素刪除。但不建議一遍迭代一邊刪除,有可能引發(fā)并發(fā)問題。

Iterator是Java迭代器最簡(jiǎn)單的實(shí)現(xiàn),為L(zhǎng)ist設(shè)計(jì)的ListIterator具有更多的功能,它可以從兩個(gè)方向遍歷List,也可以從List中插入和刪除元素。

33. Iterator 和 ListIterator 有什么區(qū)別?

Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。

Iterator對(duì)集合只能是前向遍歷,ListIterator既可以前向也可以后向。

ListIterator實(shí)現(xiàn)了Iterator接口,并包含其他的功能,比如:增加元素,替換元素,獲取前一個(gè)和后一個(gè)元素的索引等等,但在實(shí)際用的時(shí)候,大多也是只用到迭代的功能。

一般只建議使用Iterator,別區(qū)分地對(duì)List對(duì)象用ListIterator。

第三部分、多線程

35. 并行和并發(fā)有什么區(qū)別?

并行是指兩個(gè)或多個(gè)事件在同一時(shí)刻發(fā)生;而并發(fā)是指兩個(gè)或多個(gè)事件在同一時(shí)間間隔發(fā)生。

并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一實(shí)體上的多個(gè)事件。

在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù),在多臺(tái)處理器上同時(shí)處理多個(gè)任務(wù)。如hadoop分布式集群。

實(shí)際應(yīng)用場(chǎng)景里,一般是考慮多并發(fā)問題,而不是多并行問題。

36. 線程和進(jìn)程的區(qū)別?

進(jìn)程是程序運(yùn)行和資源分配的基本單位,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程,但一個(gè)進(jìn)程一般有多個(gè)線程。

進(jìn)程在運(yùn)行過程中,需要擁有獨(dú)立的內(nèi)存單元,否則如果申請(qǐng)不到,就會(huì)掛起。而多個(gè)線程能共享內(nèi)存資源,這樣就能降低運(yùn)行的門檻,從而效率更高。

線程是是cpu調(diào)度和分派的基本單位,在實(shí)際開發(fā)過程中,一般是考慮多線程并發(fā)。

37. 守護(hù)線程是什么?

守護(hù)線程(daemon thread),是個(gè)服務(wù)線程,用來監(jiān)視和服務(wù)其它線程。

38. 創(chuàng)建線程有哪幾種方式?

①. 繼承Thread類創(chuàng)建線程類

通過extends Thread定義Thread類的子類,并重寫該類的run方法。

創(chuàng)建Thread子類的實(shí)例,并調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程。

②. 通過Runnable接口創(chuàng)建線程類

implements Runnable接口的實(shí)現(xiàn)類,并重寫該接口的run()方法。

創(chuàng)建 Runnable實(shí)現(xiàn)類的實(shí)例,并依此實(shí)例作為Thread的target來創(chuàng)建Thread對(duì)象,該Thread對(duì)象才是真正的線程對(duì)象。

調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程。

③. 通過Callable和Future創(chuàng)建線程

創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)call()方法,該call()方法將作為線程執(zhí)行體,并且有返回值。

創(chuàng)建Callable實(shí)現(xiàn)類的實(shí)例,使用FutureTask類來包裝Callable對(duì)象,該FutureTask對(duì)象封裝了該Callable對(duì)象的call()方法的返回值。

使用FutureTask對(duì)象作為Thread對(duì)象的target創(chuàng)建并啟動(dòng)新線程。

調(diào)用FutureTask對(duì)象的get()方法來獲得子線程執(zhí)行結(jié)束后的返回值。

另外,還有通過線程池來創(chuàng)建線程

39. 說一下 runnable 和 callable 有什么區(qū)別?

Runnable接口中的run()方法的返回值是void,在其中可以定義線程的工作任務(wù),但無法返回值。

Callable接口中的call()方法是有返回值的,是一個(gè)泛型,一般會(huì)和Future、FutureTask配合,能異步地得到線程的執(zhí)行結(jié)果。

40. 線程有哪些狀態(tài)?

線程通常都有五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡。

創(chuàng)建狀態(tài)。創(chuàng)建好線程對(duì)象,并沒有調(diào)用該對(duì)象的start方法,此時(shí)線程處于創(chuàng)建狀態(tài)。

就緒狀態(tài)。當(dāng)調(diào)用線程對(duì)象的start方法之后,該線程就進(jìn)入了就緒狀態(tài),但是此時(shí)線程調(diào)度程序還沒有把該線程設(shè)置為當(dāng)前線程,也就是說還沒進(jìn)入運(yùn)行狀態(tài)。或者在線程運(yùn)行之后,從等待或者睡眠狀態(tài)中回來之后,也會(huì)處于就緒狀態(tài),等待被調(diào)度進(jìn)入運(yùn)行狀態(tài)。

運(yùn)行狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設(shè)置為當(dāng)前線程,此時(shí)線程就進(jìn)入了運(yùn)行狀態(tài),開始運(yùn)行run函數(shù)當(dāng)中的代碼。

阻塞狀態(tài)。線程正在運(yùn)行的時(shí)候,被暫停,通常是為了等待某個(gè)實(shí)踐的發(fā)生(比如說某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行。wait方法都可以導(dǎo)致線程阻塞。

死亡狀態(tài)。如果一個(gè)線程的run方法執(zhí)行結(jié)束或者調(diào)用stop方法后,該線程就會(huì)死亡。對(duì)于已經(jīng)死亡的線程,無法再使用start方法令其進(jìn)入就緒   

41. sleep() 和 wait() 有什么區(qū)別?

sleep():這是線程類(Thread)的靜態(tài)方法,讓線程進(jìn)入睡眠狀態(tài),等休眠時(shí)間結(jié)束后,線程進(jìn)入就緒狀態(tài),和其他線程一起競(jìng)爭(zhēng)cpu的執(zhí)行時(shí)間。

因?yàn)閟leep() 是static靜態(tài)的方法,他不能改變對(duì)象的機(jī)鎖,當(dāng)一個(gè)synchronized塊中調(diào)用了sleep() 方法,線程雖然進(jìn)入休眠,但是對(duì)象的機(jī)鎖沒有被釋放,其他線程依然無法訪問這個(gè)對(duì)象,這時(shí)就會(huì)引發(fā)問題,此類現(xiàn)象請(qǐng)注意。

wait():wait()是Object類的方法,當(dāng)一個(gè)線程執(zhí)行到wait方法時(shí),它就進(jìn)入到一個(gè)和該對(duì)象相關(guān)的等待池,同時(shí)釋放對(duì)象的機(jī)鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。

42. notify()和 notifyAll()有什么區(qū)別?

如果線程調(diào)用了對(duì)象的 wait()方法,那么線程便會(huì)處于該對(duì)象的等待池中,等待池中的線程不會(huì)去競(jìng)爭(zhēng)該對(duì)象的鎖,而且被wait的線程,無法自動(dòng)再進(jìn)入到喚醒狀態(tài)。

當(dāng)有線程調(diào)用了對(duì)象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機(jī)喚醒一個(gè) wait 線程),被喚醒的的線程便會(huì)進(jìn)入該對(duì)象的鎖池中,鎖池中的線程會(huì)去競(jìng)爭(zhēng)該對(duì)象鎖。也就是說,調(diào)用了notify后只要一個(gè)線程會(huì)由等待池進(jìn)入鎖池,而notifyAll會(huì)將該對(duì)象等待池內(nèi)的所有線程移動(dòng)到鎖池中,等待鎖競(jìng)爭(zhēng)。

優(yōu)先級(jí)高的線程競(jìng)爭(zhēng)到對(duì)象鎖的概率大,假若某線程沒有競(jìng)爭(zhēng)到該對(duì)象鎖,它還會(huì)留在鎖池中,唯有線程再次調(diào)用 wait()方法,它才會(huì)重新回到等待池中。而競(jìng)爭(zhēng)到對(duì)象鎖的線程則繼續(xù)往下執(zhí)行,直到執(zhí)行完了 synchronized 代碼塊,它會(huì)釋放掉該對(duì)象鎖,這時(shí)鎖池中的線程會(huì)繼續(xù)競(jìng)爭(zhēng)該對(duì)象鎖。

43. 線程的 run()和 start()有什么區(qū)別?

每個(gè)線程都是通過運(yùn)行自身run()來完成其操作的。而一般要通過調(diào)用Thread類的start()方法(不是run方法)來啟動(dòng)一個(gè)線程。

start()方法來啟動(dòng)一個(gè)線程,真正實(shí)現(xiàn)了多線程運(yùn)行。這時(shí)無需等待run方法體代碼執(zhí)行完畢,可以直接繼續(xù)執(zhí)行下面的代碼; 這時(shí)此線程是處于就緒狀態(tài), 并沒有運(yùn)行。

然后通過此Thread類調(diào)用方法run()來完成其運(yùn)行狀態(tài), Run方法運(yùn)行結(jié)束后, 此線程終止。然后CPU再調(diào)度其它線程。

run()方法是在本線程里的,只是線程里的一個(gè)函數(shù),而不是多線程的。 如果直接調(diào)用run(),其實(shí)就相當(dāng)于是調(diào)用了一個(gè)普通函數(shù)而已,而不是以多線程的方式來運(yùn)行。

總之,在多線程執(zhí)行時(shí)要使用start()方法而不是run()方法。

44. 創(chuàng)建線程池有哪幾種方式?

①. newFixedThreadPool(int nThreads)

創(chuàng)建一個(gè)固定長(zhǎng)度的線程池,每當(dāng)提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到達(dá)到線程池的最大數(shù)量,這時(shí)線程規(guī)模將不再變化,當(dāng)線程發(fā)生未預(yù)期的錯(cuò)誤而結(jié)束時(shí),線程池會(huì)補(bǔ)充一個(gè)新的線程。

②. newCachedThreadPool()

創(chuàng)建一個(gè)可緩存的線程池,如果線程池的規(guī)模超過了處理需求,將自動(dòng)回收空閑線程,而當(dāng)需求增加時(shí),則可以自動(dòng)添加新線程,線程池的規(guī)模不存在任何限制。

③. newSingleThreadExecutor()

這是一個(gè)單線程的Executor,它創(chuàng)建單個(gè)工作線程來執(zhí)行任務(wù),如果這個(gè)線程異常結(jié)束,會(huì)創(chuàng)建一個(gè)新的來替代它;它的特點(diǎn)是能確保依照任務(wù)在隊(duì)列中的順序來串行執(zhí)行。

④. newScheduledThreadPool(int corePoolSize)

創(chuàng)建了一個(gè)固定長(zhǎng)度的線程池,而且以延遲或定時(shí)的方式來執(zhí)行任務(wù),類似于Timer。

45. 線程池都有哪些狀態(tài)?

線程池有5種狀態(tài):Running、ShutDown、Stop、Tidying、Terminated。

線程池各個(gè)狀態(tài)切換框架圖:

46. 線程池中 submit()和 execute()方法有什么區(qū)別?

接收的參數(shù)不一樣

submit有返回值,而execute沒有

submit方法能進(jìn)行Exception處理

47. 在 java 程序中怎么保證多線程的運(yùn)行安全?

線程安全在三個(gè)方面體現(xiàn):

原子性:提供互斥訪問,同一時(shí)刻只能有一個(gè)線程對(duì)數(shù)據(jù)進(jìn)行操作,(atomic,synchronized);

可見性:一個(gè)線程對(duì)主內(nèi)存的修改可以及時(shí)地被其他線程看到,(synchronized,volatile);

有序性:一個(gè)線程觀察其他線程中的指令執(zhí)行順序,由于指令重排序,該觀察結(jié)果一般雜亂無序,(happens-before原則)。

48. 多線程鎖的升級(jí)原理是什么?

在Java中,鎖共有4種狀態(tài),級(jí)別從低到高依次為:無狀態(tài)鎖,偏向鎖,輕量級(jí)鎖和重量級(jí)鎖狀態(tài),這幾個(gè)狀態(tài)會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)。鎖可以升級(jí)但不能降級(jí)。

但是在實(shí)際開發(fā)過程中,寧可用到類或者組件自身帶的鎖管理機(jī)制,因?yàn)檫@經(jīng)歷過其它項(xiàng)目的考驗(yàn),比較可靠,別自己定義各種鎖,更別自己定義鎖的升級(jí)策略,因?yàn)檫@部分的代碼沒完整測(cè)試過,很容易引發(fā)問題。

49. 什么是死鎖?

死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,由于競(jìng)爭(zhēng)資源而導(dǎo)致相互等待,由此代碼無法繼續(xù)下。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖。

50. 怎么防止死鎖?

死鎖的四個(gè)必要條件:

互斥條件:進(jìn)程對(duì)所分配到的資源不允許其他進(jìn)程進(jìn)行訪問,若其他進(jìn)程訪問該資源,只能等待,直至占有該資源的進(jìn)程使用完成后釋放該資源

請(qǐng)求和保持條件:進(jìn)程獲得一定的資源之后,又對(duì)其他資源發(fā)出請(qǐng)求,但是該資源可能被其他進(jìn)程占有,此事請(qǐng)求阻塞,但又對(duì)自己獲得的資源保持不放

不可剝奪條件:是指進(jìn)程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放

環(huán)路等待條件:是指進(jìn)程發(fā)生死鎖后,若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系

這四個(gè)條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會(huì)發(fā)生死鎖。

理解了死鎖的原因,尤其是產(chǎn)生死鎖的四個(gè)必要條件,就可以最大可能地避免、預(yù)防和 解除死鎖。

所以,在系統(tǒng)設(shè)計(jì)、進(jìn)程調(diào)度等方面注意如何不讓這四個(gè)必要條件成立,如何確 定資源的合理分配算法,避免進(jìn)程永久占據(jù)系統(tǒng)資源。

如上是萬金油類的正確的廢話,理論層面這樣說總不會(huì)錯(cuò),那么在實(shí)際操作中是怎么做的?

1 比如在數(shù)據(jù)庫(kù)的數(shù)據(jù)隔離級(jí)別方面,別設(shè)太高,否則很容易引發(fā)數(shù)據(jù)庫(kù)里的等待,乃至死鎖。

2 預(yù)防死鎖的代價(jià)要比監(jiān)控死鎖的代價(jià)大很多,所以系統(tǒng)里一般是監(jiān)控 解決,比如用監(jiān)控系統(tǒng)(Cat等),看是否有長(zhǎng)時(shí)間運(yùn)行的SQL語句或線程,這可以預(yù)先設(shè)置,比如運(yùn)行時(shí)間超過60秒就報(bào)警,然后人工介入。

3 代碼在上線前,在測(cè)試環(huán)境充分壓力測(cè)試,發(fā)現(xiàn)死鎖點(diǎn)再解決。上線后,不能保證一定沒死鎖,一般也是采用監(jiān)控 人工解決的方式。

51. ThreadLocal 是什么?有哪些使用場(chǎng)景?

這是線程局部變量,屬于線程自身私有,不在多個(gè)線程間共享。

Java提供ThreadLocal類來支持線程局部變量,是一種實(shí)現(xiàn)線程安全的方式。

請(qǐng)注意,任何線程局部變量一旦在工作完成后沒有釋放,Java 就會(huì)有內(nèi)存泄露乃至OOM的風(fēng)險(xiǎn)。

52.說一下 synchronized 底層實(shí)現(xiàn)原理?

synchronized可以保證方法或者代碼塊在運(yùn)行時(shí),同一時(shí)刻只有一個(gè)方法可以進(jìn)入到臨界區(qū),同時(shí)它還可以保證共享變量的內(nèi)存可見性。

Java中每一個(gè)對(duì)象都可以作為鎖,這是synchronized實(shí)現(xiàn)同步的基礎(chǔ):

普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象

靜態(tài)同步方法,鎖是當(dāng)前類的class對(duì)象

同步方法塊,鎖是括號(hào)里面的對(duì)象

一旦有線程對(duì)上述對(duì)象加鎖,那么其它線程進(jìn)入前就會(huì)檢查并等待,等到鎖釋放后再進(jìn)入。

53. synchronized 和 volatile 的區(qū)別是什么?

volatile是在告訴jvm當(dāng)前變量在自身線程的內(nèi)存區(qū)里,值是不確定的,需要從主存中讀取; synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。

volatile僅能使用在變量級(jí)別;synchronized則可以使用在變量、方法、和類級(jí)別的。

volatile僅能實(shí)現(xiàn)變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性。

volatile不會(huì)造成線程的阻塞;synchronized可能會(huì)造成線程的阻塞。

volatile標(biāo)記的變量不會(huì)被編譯器優(yōu)化;synchronized標(biāo)記的變量可以被編譯器優(yōu)化。

54. synchronized 和 Lock 有什么區(qū)別?

synchronized是java關(guān)鍵字,Lock是個(gè)java類;

synchronized無法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖;

synchronized會(huì)自動(dòng)釋放鎖,Lock需在finally中手工釋放鎖(unlock()方法釋放鎖);

用synchronized關(guān)鍵字的兩個(gè)線程1和線程2,如果當(dāng)前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會(huì)一直等待下去,而Lock鎖就不一定會(huì)等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結(jié)束了;

synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可);

Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。

結(jié)論: synchronized很重,而且大多只能加在單個(gè)方法上,而Lock可以作用在調(diào)用多個(gè)業(yè)務(wù)的方法上,使用起來比較簡(jiǎn)便。

55. synchronized 和 ReentrantLock 區(qū)別是什么?

synchronized是關(guān)鍵字,ReentrantLock是類,這是二者的本質(zhì)區(qū)別。

ReentrantLock是類,所以synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量

ReentrantLock比synchronized的擴(kuò)展性體現(xiàn)在幾點(diǎn)上:

ReentrantLock可以對(duì)獲取鎖的等待時(shí)間進(jìn)行設(shè)置,這樣就避免了死鎖

ReentrantLock可以獲取各種鎖的信息

ReentrantLock可以靈活地實(shí)現(xiàn)多路通知

另外,二者的鎖機(jī)制其實(shí)也是不一樣的:ReentrantLock底層調(diào)用的是Unsafe的park方法加鎖,synchronized操作的應(yīng)該是對(duì)象頭中mark word。

56. 說一下 atomic 的原理?

Atomic包中的類基本的特性就是在多線程環(huán)境下,當(dāng)有多個(gè)線程同時(shí)對(duì)單個(gè)(包括基本類型及引用類型封裝類)變量進(jìn)行操作時(shí),具有排斥性,即當(dāng)多個(gè)線程同時(shí)對(duì)該變量的值進(jìn)行更新時(shí),僅有一個(gè)線程能成功,而未成功的線程可以向自旋鎖一樣,繼續(xù)嘗試,一直等到執(zhí)行成功。

Atomic系列的類中的核心方法都會(huì)調(diào)用unsafe類中的幾個(gè)本地方法。我們需要先知道一個(gè)東西就是Unsafe類,全名為:sun.misc.Unsafe,這個(gè)類包含了大量的對(duì)C代碼的操作,包括很多直接內(nèi)存分配以及原子操作的調(diào)用,而它之所以標(biāo)記為非安全的

這個(gè)里面大量的方法調(diào)用都會(huì)存在安全隱患,需要小心使用,否則會(huì)導(dǎo)致嚴(yán)重的后果,例如在通過unsafe分配內(nèi)存的時(shí)候,如果自己指定某些區(qū)域可能會(huì)導(dǎo)致一些類似C 一樣的指針越界到其他進(jìn)程的問題。

結(jié)論,你面試時(shí)說說就行了,實(shí)際項(xiàng)目里用的話需要非常謹(jǐn)慎。

第四部分、反射

57. 什么是反射?

反射主要是指程序可以訪問、檢測(cè)和修改它本身狀態(tài)或行為的一種能力

反射的物質(zhì)基礎(chǔ)是Class類,其中C是大寫的。

Java反射:

在Java運(yùn)行時(shí)環(huán)境中,對(duì)于任意一個(gè)類,能否知道這個(gè)類有哪些屬性和方法?對(duì)于任意一個(gè)對(duì)象,能否調(diào)用它的任意一個(gè)方法

Java反射機(jī)制主要提供了以下功能:

在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類。

在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象。

在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。

在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法。

58. 什么是 java 序列化?什么情況下需要序列化?

序列化是指,把Java對(duì)象轉(zhuǎn)換成一個(gè)字節(jié)序列,以便傳輸。

什么情況下需要序列化:

a)當(dāng)你想把的內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫(kù)中時(shí)候;

b)想在網(wǎng)上傳輸時(shí),比如想用套接字傳輸或用RMI或Dubbo調(diào)用時(shí);

59. 動(dòng)態(tài)代理是什么?有哪些應(yīng)用?

動(dòng)態(tài)代理:

當(dāng)想要給實(shí)現(xiàn)了某個(gè)接口的類中的方法,加一些額外的處理。比如說加日志,加事務(wù)等,就可以給這個(gè)類創(chuàng)建一個(gè)代理,這個(gè)代理類不僅包含原來類方法的功能,而且還在原來的基礎(chǔ)上添加了額外處理的新類。這個(gè)代理類并不是定義好的,是動(dòng)態(tài)生成的。具有解耦合性。

動(dòng)態(tài)代理的應(yīng)用,非常典型就Spring 里的AOP,以及各種注解

60. 怎么實(shí)現(xiàn)動(dòng)態(tài)代理?

1 必須定義一個(gè)接口

2 定義InvocationHandler(將實(shí)現(xiàn)接口的類的對(duì)象傳遞給它)處理類。

3 定義一個(gè)代理類Proxy(因?yàn)檎{(diào)用newInstance()可以產(chǎn)生代理對(duì)象,其實(shí)他只是一個(gè)產(chǎn)生代理對(duì)象的工具類)。

4 利用到InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進(jìn)制碼,利用加載器加載,并將其實(shí)例化產(chǎn)生代理對(duì)象,最后返回。

五、對(duì)象拷貝

61. 為什么要使用克隆?

用一個(gè)對(duì)象得到了大量的數(shù)據(jù),需要對(duì)此處理,但同時(shí)又想保存原來的數(shù)據(jù),就需要對(duì)原數(shù)據(jù)進(jìn)行克隆操作。

62. 如何實(shí)現(xiàn)對(duì)象克隆?

有兩種方式:

1). 實(shí)現(xiàn)Cloneable接口,并重寫其中的clone()方法,在里面定義克隆動(dòng)作;

2). 實(shí)現(xiàn)Serializable接口,通過對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆,可以實(shí)現(xiàn)真正的深度克隆

請(qǐng)注意,基于序列化和反序列化實(shí)現(xiàn)的克隆不僅是深度克隆,更重要的是通過泛型限定,把對(duì)象里包含的子對(duì)象也克隆出來,同時(shí)檢查克隆出來的對(duì)象是否支持序列化,而這種檢查是編譯器完成的,不是在運(yùn)行時(shí)拋出異常,這種是方案明顯優(yōu)于使用Object類的clone方法克隆對(duì)象,畢竟讓問題在編譯的時(shí)候暴露出來總是好過把問題留到運(yùn)行時(shí)。

63. 深拷貝和淺拷貝區(qū)別是什么?

淺拷貝只是復(fù)制了對(duì)象的引用地址,兩個(gè)對(duì)象指向同一個(gè)內(nèi)存地址,所以修改其中任意的值,另一個(gè)值都會(huì)隨之變化,這就是淺拷貝,淺拷貝可能會(huì)引發(fā)潛在的數(shù)據(jù)修改問題

深拷貝是將對(duì)象及值復(fù)制過來,兩個(gè)對(duì)象修改其中任意的值另一個(gè)值不會(huì)改變,這就是深拷貝(例:JSON.parse()和JSON.stringify(),但是此方法無法復(fù)制函數(shù)類型)

學(xué)習(xí)更多JAVA知識(shí)與技巧,關(guān)注與私信博主(學(xué)習(xí))免費(fèi)學(xué)習(xí)領(lǐng)取JAVA 課件,源碼,安裝包,還有最新

大廠面試資料等等等

java高級(jí)經(jīng)典面試題及答案(Java面試題大全帶答案)(1)

歡迎分享轉(zhuǎn)載→http://www.avcorse.com/read-242745.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號(hào)-5 TXT地圖HTML地圖XML地圖