一、java基礎(chǔ)篇
1.接口和抽象類的區(qū)別
- 相似點(diǎn)
- 接口和抽象類都不能被實(shí)例化
- 實(shí)現(xiàn)接口或者抽象類的子類都必須實(shí)現(xiàn)這些抽象方法
- 不同點(diǎn)
- 抽象類可以包含普通方法和代碼塊, 接口里只能包含抽象方法, 靜態(tài)方法和默認(rèn)方法
- 抽象類可以有構(gòu)造方法, 而接口沒有
- 抽象類中的成員變量可以是各種類型, 接口中的成員變量只能是public static final類型, 并且必須輔助
2.重載和重寫的區(qū)別
- 重載發(fā)生在同一個(gè)類中, 方法名相同、參數(shù)列表、返回類型、權(quán)限修飾符可以不同.
- 重寫發(fā)生在子類中, 方法名、參數(shù)列表、返回類型都相同, 權(quán)限修飾符要大于父類方法, 聲明異常范圍要小于父類方法, 但是final和private修飾的方法不可重寫
3.==和equals的區(qū)別
- ==比較基本類型時(shí), 比較的是值, ==比較引用類型時(shí), 比較的是內(nèi)存地址
- equals是Object類的方法, 本質(zhì)上與==一樣, 但是有些類重寫了equals方法, 比如String的equals被重寫后, 比較的是字符值, 另外重寫了equals后, 也必須重寫hashcode方法
4.異常處理機(jī)制
- 使用try catch finaly捕獲異常, finaly中的代碼一定會(huì)執(zhí)行, 捕獲異常后程序會(huì)繼續(xù)執(zhí)行
- 使用throws聲明該方法可能會(huì)拋出的異常類型, 出現(xiàn)異常后, 程序終止
5.HashMap原理
HashMap在JDK1.8之后是基于數(shù)組+鏈表+紅黑樹來(lái)實(shí)現(xiàn)的, 特點(diǎn)是key不能重復(fù), 可以為null, 線程不安全
一、HashMap存取原理
- 計(jì)算key的hash值, 然后進(jìn)行二次hash, 根據(jù)二次hash結(jié)果找到對(duì)應(yīng)的索引位置
- 如果這個(gè)位置有值, 先進(jìn)去equals比較, 如果結(jié)果為true則取代這個(gè)元素, 如果結(jié)果為false, 就使用高低位平移法將節(jié)點(diǎn)插入鏈表
二、HashMap的擴(kuò)容機(jī)制
當(dāng)添加某個(gè)元素后,數(shù)組的總的添加元素?cái)?shù)大于了 數(shù)組長(zhǎng)度 * 0.75(默認(rèn),也可自己設(shè)定),數(shù)組長(zhǎng)度擴(kuò)容為兩倍。(如開始創(chuàng)建HashMap集合后,數(shù)組長(zhǎng)度為16,臨界值為16 * 0.75 = 12,當(dāng)加入元素后元素個(gè)數(shù)超過(guò)12,數(shù)組長(zhǎng)度擴(kuò)容為32,臨界值變?yōu)?4)
6.HashMap線程不安全的原因
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null) // 如果沒有hash碰撞則直接插入元素
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
其中第六行代碼是判斷是否出現(xiàn)Hash碰撞, 假設(shè)兩個(gè)線程A、B都在進(jìn)行put操作, 并且通過(guò)Hash函數(shù)計(jì)算出的插入下標(biāo)都是相同的, 當(dāng)線程A執(zhí)行完第六行代碼后, 由于時(shí)間片耗盡被掛起, 而線程B得到了時(shí)間片后在該下標(biāo)處插入了元素, 完成了正常的插入, 然后線程A獲得時(shí)間片,由于之前已經(jīng)進(jìn)行了hash碰撞的判斷,所有此時(shí)不會(huì)再進(jìn)行判斷,而是直接進(jìn)行插入,這就導(dǎo)致了線程B插入的數(shù)據(jù)被線程A覆蓋了,從而線程不安全。
簡(jiǎn)單來(lái)說(shuō):put要進(jìn)行兩個(gè)動(dòng)作
- 判斷是否有Hash碰撞
- 通過(guò)Hash函數(shù)計(jì)算的插入下標(biāo)處, 插入值
這兩個(gè)動(dòng)作是分開的, 不具備原子性(可以簡(jiǎn)單理解為put方法沒加鎖)
除此之前,還有就是代碼的第38行處有個(gè)++size,我們這樣想,還是線程A、B,這兩個(gè)線程同時(shí)進(jìn)行put操作時(shí),假設(shè)當(dāng)前HashMap的zise大小為10,當(dāng)線程A執(zhí)行到第38行代碼時(shí),從主內(nèi)存中獲得size的值為10后準(zhǔn)備進(jìn)行+1操作,但是由于時(shí)間片耗盡只好讓出CPU,線程B快樂(lè)的拿到CPU還是從主內(nèi)存中拿到size的值10進(jìn)行+1操作,完成了put操作并將size=11寫回主內(nèi)存,然后線程A再次拿到CPU并繼續(xù)執(zhí)行(此時(shí)size的值仍為10),當(dāng)執(zhí)行完put操作后,還是將size=11寫回內(nèi)存,此時(shí),線程A、B都執(zhí)行了一次put操作,但是size的值只增加了1,所有說(shuō)還是由于數(shù)據(jù)覆蓋又導(dǎo)致了線程不安全。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-422530.html
7.想要線程安全的HashMap怎么辦
- 使用ConcurrentHashMap(并發(fā)的HashMap)
- 使用HashTable
- Collections.synchronizedHashMap()方法
8.HashTable和HashMap的區(qū)別
- HashTable的每個(gè)方法都用synchronized修飾,因此是線程安全的,但同時(shí)讀寫效率很低
- HashTable的Key不允許為null
- HashTable只對(duì)key進(jìn)行一次hash,HashMap進(jìn)行了兩次Hash
- HashTable底層使用的數(shù)組加鏈表
9.ConcurrentHashMap原如何保證的線程安全
JDK1.8:采用CAS+Synchronized保證線程安全,每次插入數(shù)據(jù)時(shí)判斷在當(dāng)前數(shù)組下標(biāo)是否是第一次插入,是就通過(guò)CAS方式插入,然后判斷f.hash是否=-1,是的話就說(shuō)明其他線程正在進(jìn)行擴(kuò)容,當(dāng)前線程也會(huì)參與擴(kuò)容;刪除方法用了synchronized修飾,保證并發(fā)下移除元素安全
ConcurrentHashMap原理詳解(太細(xì)了)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-422530.html
10.ArrayList和LinkedList的區(qū)別
- ArrayList底層是使用動(dòng)態(tài)數(shù)組, 默認(rèn)容量為10, 當(dāng)元素?cái)?shù)量達(dá)到容量時(shí), 生成一個(gè)新的數(shù)組, 大小為前一次的1.5倍, 然后將原來(lái)的數(shù)組copy過(guò)來(lái), 因?yàn)閿?shù)組在內(nèi)存中是連續(xù)的地址, 所以ArrayList查找數(shù)據(jù)更快, 由于擴(kuò)容機(jī)制
到了這里,關(guān)于Java八股文的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!