22道Java面试题,看看你会了多少?
1)集合類:List和Set比較,各自的子類比較(ArrayList,Vector,LinkedList;HashSet,TreeSet)
List:元素是有順序的,元素可以重復(fù)因?yàn)槊總€(gè)元素有自己的角標(biāo)(索引)
?|-- ArrayList:底層是數(shù)組結(jié)構(gòu),特點(diǎn)是:查詢很快,增刪稍微慢點(diǎn),線程不同步:A線程將元素放在索引0位置,CPU調(diào)度線程A停止,B運(yùn)行,也將元素放在索引0位置,當(dāng)A和B同時(shí)運(yùn)行的時(shí)候Size就編程了2.
?|-- LinkedList:底層使用的是鏈表數(shù)據(jù)結(jié)構(gòu),特點(diǎn)是:增刪很快,查詢慢。線程不安全,線程安全問題是由多個(gè)線程同時(shí)寫或同時(shí)讀寫同一個(gè)資源造成的。
?|--Vector:底層是數(shù)組數(shù)據(jù)結(jié)構(gòu),線程同步,Vector的方法前面加了synchronized關(guān)鍵字,被ArrayList代替了,現(xiàn)在用的只有他的枚舉。
Set:元素是無序的,且不可以重復(fù)(存入和取出的順序不一定一致),線程不同步。set底層是使用Map實(shí)現(xiàn)的,故可以通過ConcurrentHashMap的方式變通實(shí)現(xiàn)線程安全的Set。
|--HashSet:底層是哈希表數(shù)據(jù)結(jié)構(gòu)。根據(jù)hashCode和equals方法來確定元素的唯一性。
? ?hashCode和equals:作用一樣,都是用來比較兩個(gè)對(duì)象是否相等一致。
? ?equals比較的比較全面,而利用hashCode()進(jìn)行對(duì)比,則只要生成一個(gè)hash值進(jìn)行比較久可以了,效率高。
? ?equal()相等的兩個(gè)對(duì)象他們的hashCode()肯定相等,也就是equal()是絕對(duì)可靠的。
? ?hashCode()相等的兩個(gè)對(duì)象他們的equal()不一定相等,hashCode()不是絕對(duì)可靠的。
Map:這個(gè)集合是存儲(chǔ)鍵值對(duì)的,一對(duì)一對(duì)往里存,而且要確保鍵的唯一性(01,張三)這樣的形式打印出來就是 ?01=張三
? ?|--HashTable:底層是哈希表數(shù)據(jù)結(jié)構(gòu),不可以存入null鍵和null值,該集合線程是同步的,效率比較低。出現(xiàn)于JDK1.0。線程安全,使用synchronized鎖住整張Hash表實(shí)現(xiàn)線程安全,即每次鎖住整張表讓線程獨(dú)占。
? ?|--HashMap:底層是哈希表數(shù)據(jù)結(jié)構(gòu),可以存入null鍵和null值,線程不同步,效率較高,代替了HashTable,出現(xiàn)于JDK 1.2
? ?|--TreeMap:底層是二叉樹數(shù)據(jù)結(jié)構(gòu),線程不同步,可以用于對(duì)map集合中的鍵進(jìn)行排序
? ?ConcurrentHashMap:線程安全,允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù),它使用了多個(gè)鎖來控制對(duì)hash表的不同部分進(jìn)行的修改。ConcurrentHashMap內(nèi)部使用段(Segment)來表示這些不同的部分,每個(gè)段其實(shí)就是一個(gè)小的Hashtable,它們有自己的鎖。只要多個(gè)修改操作發(fā)生在不同的段上,它們就可以并發(fā)進(jìn)行。
當(dāng)兩個(gè)對(duì)象需要對(duì)比的時(shí)候,首先用hashCode()去對(duì)比,如果不一樣,則表示這兩個(gè)對(duì)象肯定不相等(也就不用再比較equal(0)了),如果hashCode()相同,再比較equal(),如果equal()相同,那兩個(gè)對(duì)象就是相同的。
? ?|--TreeSet:可以對(duì)Set集合中的元素進(jìn)行排序(自然循序),底層的數(shù)據(jù)結(jié)構(gòu)是二叉樹,
2)HashMap的底層實(shí)現(xiàn),之后會(huì)問ConcurrentHashMap的底層實(shí)現(xiàn)
HashMap實(shí)際上是一個(gè)“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體。允許使用null值和null鍵。
HashMap底層就是一個(gè)數(shù)組結(jié)構(gòu),數(shù)組中的每一項(xiàng)又是一個(gè)鏈表。當(dāng)新建一個(gè)HashMap的時(shí)候,就會(huì)初始化一個(gè)數(shù)組。
HashMap是基于hash算法實(shí)現(xiàn)的,通過put(key,value)存儲(chǔ)對(duì)象到HashMap中,也可以通過get(key)從HashMap中獲取對(duì)象。
當(dāng)我們使用put的時(shí)候,首先HashMap會(huì)對(duì)key的hashCode()的值進(jìn)行hash計(jì)算,根據(jù)hash值得到這個(gè)元素在數(shù)組中的位置,將元素存儲(chǔ)在該位置的鏈表上。
當(dāng)我們使用get的時(shí)候,首先HashMap會(huì)對(duì)key的hashCode()的值進(jìn)行hash計(jì)算,根據(jù)hash值得到這個(gè)元素在數(shù)組中的位置,將元素從該位置上的鏈表中取出
當(dāng)多線程的情況下,可能產(chǎn)生條件競爭。當(dāng)重新調(diào)整HashMap大小的時(shí)候,確實(shí)存在條件競爭,如果兩個(gè)線程都發(fā)現(xiàn)HashMap需要重新調(diào)整大小了,
它們會(huì)同時(shí)試著調(diào)整大小。在調(diào)整大小的過程中,存儲(chǔ)在鏈表中的元素的次序會(huì)反過來,因?yàn)橐苿?dòng)到新的數(shù)組位置的時(shí)候,
HashMap并不會(huì)將元素放在LinkedList的尾部,而是放在頭部,這是為了避免尾部遍歷(tail traversing)。如果條件競爭發(fā)生了,那么就死循環(huán)了
ConcurrentHashMap基于雙數(shù)組和鏈表的Map接口的同步實(shí)現(xiàn)
ConcurrentHashMap中元素的key是唯一的、value值可重復(fù)
ConcurrentHashMap不允許使用null值和null鍵
ConcurrentHashMap是無序的
為什么使用ConcurrentHashMap:
我們都知道HashMap是非線程安全的,當(dāng)我們只有一個(gè)線程在使用HashMap的時(shí)候,自然不會(huì)有問題,但如果涉及到多個(gè)線程,并且有讀有寫的過程中,HashMap就會(huì)fail-fast。要解決HashMap同步的問題,我們的解決方案有:Hashtable 、Collections.synchronizedMap(hashMap)
這兩種方式基本都是對(duì)整個(gè)hash表結(jié)構(gòu)加上同步鎖,這樣在鎖表的期間,別的線程就需要等待了,無疑性能不高,所以我們引入ConcurrentHashMap,既能同步又能多線程訪問
ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu):
ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu)為一個(gè)Segment數(shù)組,Segment的數(shù)據(jù)結(jié)構(gòu)為HashEntry的數(shù)組,而HashEntry存的是我們的鍵值對(duì),可以構(gòu)成鏈表。可以簡單的理解為數(shù)組里裝的是HashMap
3)如何實(shí)現(xiàn)HashMap順序存儲(chǔ):
可以參考LinkedHashMap的底層實(shí)現(xiàn)LinkedHashMap底層使用哈希表與雙向鏈表來保存所有元素,它維護(hù)著一個(gè)運(yùn)行于所有條目的雙向鏈表(如果學(xué)過雙向鏈表的同學(xué)會(huì)更好的理解它的源代碼),此鏈表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序
1.按插入順序的鏈表:在LinkedHashMap調(diào)用get方法后,輸出的順序和輸入時(shí)的相同,這就是按插入順序的鏈表,默認(rèn)是按插入順序排序
2.按訪問順序的鏈表:在LinkedHashMap調(diào)用get方法后,會(huì)將這次訪問的元素移至鏈表尾部,不斷訪問可以形成按訪問順序排序的鏈表。簡單的說,按最近最少訪問的元素進(jìn)行排序(類似LRU算法)
4)String,StringBuffer和StringBuilder的區(qū)別
運(yùn)行速度快慢為:StringBuilder > StringBuffer > String
String最慢的原因:
String為字符串常量,而StringBuilder和StringBuffer均為字符串變量,即String對(duì)象一旦創(chuàng)建之后該對(duì)象是不可更改的,但后兩者的對(duì)象是變量,是可以更改的。
String:適用于少量的字符串操作的情況,
StringBuilder:適用于單線程下在字符緩沖區(qū)進(jìn)行大量操作的情況(線程不安全)
StringBuffer:適用多線程下在字符緩沖區(qū)進(jìn)行大量操作的情況(線程安全)
5)Object的方法有哪些:比如有wait方法,為什么會(huì)有
wait、notify、notifuAll
1.使用wait()、notify()和notifyAll()時(shí)需要首先對(duì)調(diào)用對(duì)象加鎖
2.調(diào)用wait()方法后,線程狀態(tài)會(huì)從RUNNING變?yōu)閃AITING,并將當(dāng)線程加入到lock對(duì)象的等待隊(duì)列中
3.調(diào)用notify()或者notifyAll()方法后,等待在lock對(duì)象的等待隊(duì)列的線程不會(huì)馬上從wait()方法返回,必須要等到調(diào)用notify()或者notifyAll()方法的線程將lock鎖釋放,等待線程才有機(jī)會(huì)從等待隊(duì)列返回。這里只是有機(jī)會(huì),因?yàn)殒i釋放后,等待線程會(huì)出現(xiàn)競爭,只有競爭到該鎖的線程才會(huì)從wait()方法返回,其他的線程只能繼續(xù)等待
4.notify()方法將等待隊(duì)列中的一個(gè)線程移到lock對(duì)象的同步隊(duì)列,notifyAll()方法則是將等待隊(duì)列中所有線程移到lock對(duì)象的同步隊(duì)列,被移動(dòng)的線程的狀態(tài)由WAITING變?yōu)锽LOCKED
5.wait()方法上等待鎖,可以通過wait(long timeout)設(shè)置等待的超時(shí)時(shí)間
6)wait和sleep的區(qū)別,必須理解
sleep方法屬于線程,wait方法屬于對(duì)象
sleep休眠當(dāng)前線程,不會(huì)釋放對(duì)象鎖,wait使當(dāng)前線程進(jìn)入等待狀態(tài),釋放對(duì)象鎖,只有針對(duì)此對(duì)象調(diào)用notify()方法(且共享對(duì)象資源釋放)后本線程才會(huì)繼續(xù)執(zhí)行
參考:https://www.cnblogs.com/hongten/p/hongten_java_sleep_wait.html
7)JVM的內(nèi)存結(jié)構(gòu),JVM的算法
JVM內(nèi)存結(jié)構(gòu)主要有三大塊:堆內(nèi)存、方法區(qū)和棧,幾乎所有的對(duì)象實(shí)例都存放在堆里,如果在堆中沒有內(nèi)存完成實(shí)例分配,并且堆也無法再擴(kuò)展時(shí),將會(huì)拋出OutOfMemoryError異常。
方法區(qū)用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),將拋出OutOfMemoryError異常。
每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等信息。
每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。
如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError異常。
8)強(qiáng)引用,軟引用和弱引用的區(qū)別
強(qiáng)引用:
以前我們使用的大部分引用實(shí)際上都是強(qiáng)引用,這是使用最普遍的引用。如果一個(gè)對(duì)象具有強(qiáng)引用,那就類似于必不可少的生活用品,垃圾回收器絕不會(huì)回收它。
當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來解決內(nèi)存不足問題。
軟引用:
如果一個(gè)對(duì)象只具有軟引用,那就類似于可有可物的生活用品。如果內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存
弱引用:
弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。
在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存
總結(jié):
強(qiáng)引用:String str = “abc”; list.add(str);
軟引用:如果弱引用對(duì)象回收完之后,內(nèi)存還是報(bào)警,繼續(xù)回收軟引用對(duì)象
弱引用:如果虛引用對(duì)象回收完之后,內(nèi)存還是報(bào)警,繼續(xù)回收弱引用對(duì)象
虛引用:虛擬機(jī)的內(nèi)存不夠使用,開始報(bào)警,這時(shí)候垃圾回收機(jī)制開始執(zhí)行System.gc(); String s = “abc”;如果沒有對(duì)象回收了, 就回收沒虛引用的對(duì)象
9)數(shù)組在內(nèi)存中如何分配
當(dāng)一個(gè)對(duì)象使用關(guān)鍵字“new”創(chuàng)建時(shí),會(huì)在堆上分配內(nèi)存空間,然后返回對(duì)象的引用,這對(duì)數(shù)組來說也是一樣的,因?yàn)閿?shù)組也是一個(gè)對(duì)象
簡單的值類型的數(shù)組,每個(gè)數(shù)組成員是一個(gè)引用(指針),引用到棧上的空間
10)用過哪些設(shè)計(jì)模式,手寫一個(gè)(除單例)
1.懶漢模式
????public?class?SingletonDemo?{private?static?SingletonDemo?instance;private?SingletonDemo(){}public?static?SingletonDemo?getInstance(){if(instance==null){instance=new?SingletonDemo();}return?instance;}}2.餓漢模式
????public?class?SingletonDemo?{private?static?SingletonDemo?instance=new?SingletonDemo();private?SingletonDemo(){}public?static?SingletonDemo?getInstance(){return?instance;}}3.簡單工廠模式
面條工廠:
先來一份蘭州拉面(具體的產(chǎn)品類):
????public?class?LzNoodles?extends?INoodles?{@Overridepublic?void?desc()?{System.out.println("蘭州拉面?上海的好貴?家里才5?6塊錢一碗");}}程序員加班必備也要吃泡面(具體的產(chǎn)品類):
????public?class?PaoNoodles?extends?INoodles?{@Overridepublic?void?desc()?{System.out.println("泡面好吃?可不要貪杯");}}準(zhǔn)備工作做完了,我們來到一家“簡單面館”(簡單工廠類),菜單如下:
????public?class?SimpleNoodlesFactory?{public?static?final?int?TYPE_LZ?=?1;//蘭州拉面public?static?final?int?TYPE_PM?=?2;//泡面public?static?INoodles?createNoodles(int?type)?{switch?(type)?{case?TYPE_LZ:return?new?LzNoodles();case?TYPE_PM:return?new?PaoNoodles();default:return?new?PaoNoodles();}}/***?簡單工廠模式*/void?creat(){INoodles?noodles?=?SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_PM);noodles.desc();}}?11)springmvc的核心是什么,請(qǐng)求的流程是怎么處理的,控制反轉(zhuǎn)怎么實(shí)現(xiàn)的
aop和ioc
流程:用戶發(fā)送請(qǐng)求給服務(wù)器。url:user.do--->Dispatchservlet處理-->DispatchServlet通過HandleMapping調(diào)用這個(gè)url對(duì)應(yīng)的Controller
Controller執(zhí)行完畢后,如果返回字符串,則ViewResolver將字符串轉(zhuǎn)化成相應(yīng)的視圖對(duì)象;如果返回ModelAndView對(duì)象,該對(duì)象本身就包含了視圖對(duì)象信息。
DispatchServlet將執(zhí)視圖對(duì)象中的數(shù)據(jù),輸出給服務(wù)器并呈現(xiàn)給客戶
IOC控制反轉(zhuǎn):典型的工廠模式,就是具有依賴注入功能的容器,是可以創(chuàng)建對(duì)象的容器,IOC容器負(fù)責(zé)實(shí)例化、定位、配置應(yīng)用程序中的對(duì)象及建立這些對(duì)象間的依賴。
通常new一個(gè)實(shí)例,控制權(quán)由程序員控制,而"控制反轉(zhuǎn)"是指new實(shí)例工作不由程序員來做而是交給Spring容器來做。。在Spring中BeanFactory是IOC容器的實(shí)際代表者
AOP依賴注入:典型的代理模式,面向切面編程將程序中的交叉業(yè)務(wù)邏輯(比如安全,日志,事務(wù)),封裝成一個(gè)切面,然后注入到目標(biāo)業(yè)務(wù)邏輯中去。
aop框架具有的兩個(gè)特征: 1.各個(gè)步驟之間的良好隔離性 2.源代碼無關(guān)性
12)mybatis如何處理結(jié)果集:
反射,建議看看源碼
通過在mapper配置文件里配置的屬性對(duì)照反射進(jìn)對(duì)象里
13)java的多態(tài)表現(xiàn)在哪里
多態(tài)是同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力。
多態(tài)就是同一個(gè)接口,使用不同的實(shí)例而執(zhí)行不同操作
比如同一個(gè)打印機(jī),可以打印黑白的紙張也可以打印彩色的,同樣是人,卻有黑人白人之分
14)接口有什么用
接口是一種規(guī)范,在這里舉兩個(gè)例子
1.接口就比如KFC,你一聽KFC就知道是賣炸雞薯?xiàng)l的,他可以有不同的分店,也可以有自己的創(chuàng)新食品(多態(tài)),但是招牌炸雞、雞肉卷、全家桶什么的肯定會(huì)有,
你不用進(jìn)店看菜單就知道他有,但如果不叫KFC換成炸雞店你也可以吃到炸雞,但是你不進(jìn)店看菜單你不知道他具體都賣的有哪些食品,這就是接口的好處
2.比如電插孔,多是兩孔和三孔的那種,如果沒有這種規(guī)范那每家電器公司都來做一種插孔的話,試想一下插頭換了怎么辦?是不是只能買原裝的來替換了
15)說說http,https協(xié)議
http是一種超文本協(xié)議,默認(rèn)端口80,以明文傳輸。
https是http協(xié)議的安全版,安全基礎(chǔ)是SSL,以密文傳輸
16)osi五層網(wǎng)絡(luò)協(xié)議
應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層
17)用過哪些加密算法
對(duì)稱加密,非對(duì)稱加密算法,Base64加密算法,MD5加密算法,SHA1加密算法
18)說說tcp三次握手,四次揮手
1.客戶端向服務(wù)器發(fā)送一個(gè)syn包,進(jìn)入發(fā)送狀態(tài)
2.服務(wù)器收到syn包,確認(rèn)客戶的syn,并向客戶端發(fā)送syn+ack包,進(jìn)入接受狀態(tài)
3.客戶端接受的來自服務(wù)的的syn包信息,向服務(wù)的發(fā)出ack包,次數(shù)兩者進(jìn)入tcp連接成功狀態(tài)
19)cookie和session的區(qū)別,分布式環(huán)境怎么保存用戶狀態(tài)
cookie存在客戶端,session存在服務(wù)端
分布式Session的幾種實(shí)現(xiàn)方式
1.基于數(shù)據(jù)庫的Session共享
2.基于NFS共享文件系統(tǒng)
3.基于memcached 的session,如何保證 memcached 本身的高可用性?
4.基于resin/tomcat web容器本身的session復(fù)制機(jī)制
5.基于TT/Redis 或 jbosscache 進(jìn)行 session 共享。
6.基于cookie 進(jìn)行session共享(唯一值token)
20)git,svn區(qū)別
Git是分布式的,而Svn不是分布的
Git下載下來后,在OffLine狀態(tài)下可以看到所有的Log,SVN不可以
SVN的特點(diǎn)是簡單,只是需要一個(gè)放代碼的地方時(shí)用是OK的,Git的特點(diǎn)版本控制可以不依賴網(wǎng)絡(luò)做任何事情,對(duì)分支和合并有更好的支持
21)請(qǐng)寫一段棧溢出、堆溢出的代碼
堆溢出,死循環(huán)存值,JVM就會(huì)拋出OutOfMemoryError:java heap space異常
????public?static?void?main(String[]?args)?{List<byte[]>?list?=?new?ArrayList<>();int?i=0;while(true){list.add(new?byte[5*1024*1024]);System.out.println("分配次數(shù):"+(++i));}}棧溢出,棧空間不足——StackOverflowError實(shí)例
????public?class?StackSOFTest?{int?depth?=?0;public?void?sofMethod(){depth?++?;sofMethod();}public?static?void?main(String[]?args)?{StackSOFTest?test?=?null;try?{test?=?new?StackSOFTest();test.sofMethod();}?finally?{System.out.println("遞歸次數(shù):"+test.depth);}}}22)ThreadLocal可以用來共享數(shù)據(jù)嗎
可以
ThreadLocal使用場合主要解決多線程中數(shù)據(jù)數(shù)據(jù)因并發(fā)產(chǎn)生不一致問題。ThreadLocal為每個(gè)線程的中并發(fā)訪問的數(shù)據(jù)提供一個(gè)副本,通過訪問副本來運(yùn)行業(yè)務(wù),這樣的結(jié)果是耗費(fèi)了內(nèi)存,單大大減少了線程同步所帶來性能消耗,也減少了線程并發(fā)控制的復(fù)雜度。
ThreadLocal和Synchonized都用于解決多線程并發(fā)訪問。但是ThreadLocal與synchronized有本質(zhì)的區(qū)別。synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)該只能被一個(gè)線程訪問。而ThreadLocal為每一個(gè)線程都提供了變量的副本,使得每個(gè)線程在某一時(shí)間訪問到的并不是同一個(gè)對(duì)象,這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個(gè)線程間通信時(shí)能夠獲得數(shù)據(jù)共享。
Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。
添加我私人微信
置頂或星標(biāo)公眾號(hào),第一時(shí)間接收小海熱文
方法如下
總結(jié)
以上是生活随笔為你收集整理的22道Java面试题,看看你会了多少?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 更新代码到gitlab上
- 下一篇: Java8 之 lambda 表达式、方