Java 是一種廣泛使用的面向?qū)ο缶幊陶Z(yǔ)言,在軟件開(kāi)發(fā)領(lǐng)域有著重要的地位。Java 提供了豐富的庫(kù)和強(qiáng)大的特性,適用于多種應(yīng)用場(chǎng)景,包括企業(yè)應(yīng)用、移動(dòng)應(yīng)用、嵌入式系統(tǒng)等。
????????
以下是幾個(gè)面試技巧:
? ?1. 復(fù)習(xí)核心概念:回顧 Java 的核心概念,如面向?qū)ο缶幊?、類和?duì)象、繼承和多態(tài)、異常處理、集合框架等。確保對(duì)這些基礎(chǔ)知識(shí)有清晰的理解。
? ?2. 熟悉常見(jiàn)問(wèn)題:預(yù)測(cè)并準(zhǔn)備常見(jiàn)的面試問(wèn)題,如 "什么是 Java 的封裝、繼承和多態(tài)?","什么是抽象類和接口?它們的區(qū)別是什么?" 等。熟悉這些問(wèn)題的答案,以便能夠流利、清晰地回答面試官的提問(wèn)。
? ?3. 編碼實(shí)踐:練習(xí)編寫(xiě)一些簡(jiǎn)單的 Java 代碼,以加深對(duì)基礎(chǔ)概念的理解。嘗試解決一些常見(jiàn)的編程問(wèn)題,如逆序字符串、查找數(shù)組中的最大值等。這將有助于您在面試中展示自己的編碼能力。
? ?4. 項(xiàng)目經(jīng)驗(yàn)準(zhǔn)備:復(fù)習(xí)您在 Java 開(kāi)發(fā)方面的項(xiàng)目經(jīng)驗(yàn)。準(zhǔn)備一些項(xiàng)目細(xì)節(jié)和亮點(diǎn),強(qiáng)調(diào)您在項(xiàng)目中所承擔(dān)的角色和技術(shù)貢獻(xiàn)。面試官通常會(huì)關(guān)注您的項(xiàng)目經(jīng)驗(yàn),因此務(wù)必能夠清晰而有條理地介紹您的項(xiàng)目經(jīng)歷。
? ?5. 注意優(yōu)缺點(diǎn):在回答問(wèn)題時(shí),盡量不僅停留在正確的答案上,還要深入思考并表達(dá)特定功能、概念或語(yǔ)言特性的優(yōu)缺點(diǎn)。面試官通常會(huì)更關(guān)注您的思考能力和對(duì)技術(shù)的全面理解。
? ?6. 積極參與:在面試中,積極與面試官互動(dòng)。表達(dá)自己的觀點(diǎn)和思考,提出問(wèn)題或?qū)で蟪吻?。這不僅能展示您的積極性和好奇心,還能促進(jìn)面試的互動(dòng)和對(duì)話。
? ?7. 準(zhǔn)備好問(wèn)題:在面試結(jié)束時(shí),通常會(huì)給您提供機(jī)會(huì)提問(wèn)。為了展示您對(duì)崗位和公司的興趣,準(zhǔn)備一些相關(guān)問(wèn)題,例如關(guān)于公司文化、技術(shù)棧、團(tuán)隊(duì)合作等方面的問(wèn)題。
????????
最重要的是保持自信和冷靜。提前準(zhǔn)備,并對(duì)自己的知識(shí)和經(jīng)驗(yàn)有自信,這樣您就能在面試中展現(xiàn)出最佳的表現(xiàn)。祝您面試順利!
目錄
一、Java Object類有哪些方法,分別作用
二、HashMap原理,是否存在線程安全問(wèn)題
三、Java如何進(jìn)?線程同步
四、CAS原理
五、JVM垃圾回收之GC算法有哪些
六、Mysql索引原理以及查詢優(yōu)化
七、TCP擁塞控制
八、算法:? 給定—棵二叉樹(shù),找到這棵樹(shù)最中最后—行中最左邊的值
九、知道什么設(shè)計(jì)模式,分別介紹
十、算法:求?序數(shù)組中第k?的數(shù)
十一、算法:求旋轉(zhuǎn)數(shù)組找最?值(?分法)
十二、算法:判斷?叉樹(shù)是否鏡像(遞歸)
十三、如何理解前后端分離
十四、有哪些后端開(kāi)發(fā)經(jīng)驗(yàn),做了什么
十五、介紹HashMap與TreeMap區(qū)別
十六、?HashMap實(shí)現(xiàn)?個(gè)有過(guò)期功能的緩存
十七、平時(shí)怎么學(xué)習(xí)新知識(shí)
十八、最近看了什么書(shū)
(一面題)
一、Java Object類有哪些方法,分別作用
????????Java中的
Object
類是所有類的超類(父類),任何類都直接或間接地繼承自Object
類。因此,Object
類中的方法對(duì)所有Java對(duì)象都是可用的。下面是一些最常用的Object
類方法及其作用:
?
1. public boolean equals(Object obj)
- 檢查調(diào)用該方法的對(duì)象是否等于參數(shù)傳遞的對(duì)象。默認(rèn)實(shí)現(xiàn)是比較兩個(gè)對(duì)象的內(nèi)存地址(即它們是否為同一對(duì)象),但很多類重寫(xiě)此方法以提供有意義的相等性比較。
- Object中的equals方法比較的是對(duì)象的地址是否相同;? equals方法可以被重寫(xiě),重寫(xiě)后equals方法比較的是兩個(gè)對(duì)象值是否相同。
- 在Java規(guī)范中,對(duì)equals方法的使用必須遵循以下幾個(gè)規(guī)則:
- 自反性:對(duì)于任何非空引用值X,X.equals(X)都應(yīng)返回true;
- 對(duì)稱性:對(duì)于任何非空引用值X和Y,當(dāng)且僅當(dāng) Y.equals(X)返回true時(shí), X.equals(Y)也應(yīng)該返回true;
- 傳遞性:對(duì)于任何非空引用值X,Y,Z,如果X.equals(Y)返回true,并且Y.equals(Z)返回true,那么X.equals(Z)應(yīng)返回true;
- 一致性:對(duì)于任何非空引用值X和Y,多次調(diào)用 X.equals(Y)始終返回true或始終返回false。
- equals和 ==的區(qū)別
- equals比較的是兩個(gè)對(duì)象值是否相等,如果沒(méi)有被重寫(xiě),比較的是對(duì)象的引用地址是否相同;
==用于比較基本數(shù)據(jù)類型的值是否相等,或比較兩個(gè)對(duì)象的引用地址是否相等;
String? hello? =? new? String("hello"); String? hello1? =? new? String("hello"); System.out .println(hello.equals(hello1)); //重寫(xiě)了 ,比較的是值 ,輸出結(jié)果為true System.out .println(hello?== hello1); //比較的是引用地址 ,輸出結(jié)果為false int? age? =? 10; int? age2? =? 10; //比較基本類型的值 System.out.println(age? ==? age2);?????? //輸出為true
?2. public int hashCode()
- 返回調(diào)用對(duì)象的哈希碼值。默認(rèn)情況下,這個(gè)方法返回對(duì)象的內(nèi)存地址轉(zhuǎn)換成的整數(shù)值。重寫(xiě)
equals()
時(shí)通常也需要重寫(xiě)hashCode()
,以便保持equals()
為true
的兩個(gè)對(duì)象具有相同的哈希碼。
?3. public String toString()
- 返回對(duì)象的字符串表示形式。
Object
類的默認(rèn)實(shí)現(xiàn)返回一個(gè)由類名,符號(hào)“@”以及對(duì)象哈希碼的無(wú)符號(hào)十六進(jìn)制表示組成的字符串。通常,類會(huì)重寫(xiě)這個(gè)方法,提供更有意義的信息。- 比如System.out.print(person)等價(jià)于System.out.print(person.toString());???? //默認(rèn)返回對(duì)象的地址
getClass().getName是返回對(duì)象的全類名,? Integer.toHexString(hashCode())是以16進(jìn)制無(wú)符號(hào)整數(shù)形式返回此哈希碼的字符串表示
形式。
?4. public final native Class<?> getClass()
- 返回運(yùn)行時(shí)類的
Class
對(duì)象。Class
類的實(shí)例表示正在運(yùn)行的Java應(yīng)用程序中的類和接口。
?5. protected native Object clone() throws CloneNotSupportedException
- 創(chuàng)建并返回調(diào)用該方法的對(duì)象的一個(gè)副本。
- 對(duì)象的類必須實(shí)現(xiàn)
Cloneable
接口,否則拋出CloneNotSupportedException
。clone生成的新對(duì)象與原對(duì)象的關(guān)系,區(qū)別在于兩個(gè)對(duì)象間是否存在相同的引用或?qū)?yīng)的內(nèi)存地址是否存在共用情況;若存在,則為 “淺復(fù)
制”?,否則為?“深復(fù)制”?,“深復(fù)制”時(shí)需要將共同關(guān)聯(lián)的引用也復(fù)制完全。
?6. public void finalize()
- 當(dāng)垃圾收集器確定不存在對(duì)該對(duì)象的更多引用時(shí),由垃圾收集器在垃圾回收前調(diào)用此方法。子類可以重寫(xiě)
finalize()
以進(jìn)行清理工作(諸如釋放資源等)。
?7. protected void finalize() throws Throwable
- 雖然標(biāo)記為
protected
,但這是對(duì)finalize()
方法的解釋。從Java 9開(kāi)始,已經(jīng)不再推薦使用finalize()
方法,取而代之的是使用Cleaner
和PhantomReference
這樣的替代方案。
?8. public final void wait()
?throws InterruptedException
- 導(dǎo)致當(dāng)前線程等待,直到另一個(gè)線程調(diào)用此對(duì)象的
notify()
方法或notifyAll()
方法。- 調(diào)用wait方法的當(dāng)前線程一定要擁有對(duì)象的監(jiān)視器鎖。
- wait方法會(huì)把當(dāng)前線程放在對(duì)應(yīng)的等待隊(duì)列中,在這個(gè)對(duì)象上的所有同步請(qǐng)求都不會(huì)得到響應(yīng)。線程調(diào)用將不會(huì)調(diào)用線程,線程一直處于休眠狀態(tài)。要注意的是,? wait方法把當(dāng)前線程放置到這個(gè)對(duì)象的等待隊(duì)列中,解鎖也僅僅是在這個(gè)對(duì)象上;當(dāng)前線程在等待過(guò)程中仍然持有其他對(duì)象的鎖。
- 如果當(dāng)前線程被其他線程在當(dāng)前線程等待之前或正在等待時(shí)調(diào)用了interrupt()中斷了,那么就會(huì)拋出InterruptException異常。
- 為什么wait方法一般要寫(xiě)在while循環(huán)里?
- 在某個(gè)線程調(diào)用notify到等待線程被喚醒的過(guò)程中,有可能出現(xiàn)另一個(gè)線程得到了鎖并修改了條件使得條件不再滿足;只有某些等待 線程的條件滿足了,但通知線程調(diào)用了notifyAll有可能出現(xiàn)“偽喚醒”。
- wait方法和sleep方法的區(qū)別?
- wait方法屬于object類,當(dāng)調(diào)用wait方法時(shí),線程會(huì)放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象調(diào)用notify方法后 本線程才會(huì)進(jìn)入對(duì)象鎖定池,準(zhǔn)備獲取對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。
- sleep方法屬于thread類,sleep方法導(dǎo)致程序暫停執(zhí)行指定的時(shí)間,讓出CPU給其他線程,但是它的監(jiān)控狀態(tài)依然保持,當(dāng)指定的時(shí)間到了又會(huì)恢復(fù)運(yùn)行狀態(tài)。在調(diào)用sleep方法過(guò)程中,線程不會(huì)釋放對(duì)象鎖。
?9. public final native void notify()
- 喚醒正在等待對(duì)象監(jiān)視器的單個(gè)線程。
喚醒可能等待該對(duì)象的對(duì)象鎖的其他線程。由JVM(與優(yōu)先級(jí)無(wú)關(guān))隨機(jī)挑選一個(gè)處于wait狀態(tài)的線程。
在調(diào)用notify()之前,線程必須獲取該對(duì)象的對(duì)象鎖,執(zhí)行完notify()方法后,不會(huì)馬上釋放鎖,直到退出synchronized代碼塊,當(dāng)前線程 才會(huì)釋放鎖;? notify一次只能隨機(jī)通知一個(gè)線程進(jìn)行喚醒。
?10. public final native void notifyAll()
- 喚醒正在等待對(duì)象監(jiān)視器的所有線程。
使所有正在等待池中等待同一個(gè)共享資源的全部線程從等待狀態(tài)退出,進(jìn)入可運(yùn)行狀態(tài),讓它們同時(shí)競(jìng)爭(zhēng)對(duì)象鎖,只有獲得鎖的線程才能進(jìn)
入就緒狀態(tài)。
?11. public final void wait(long timeout) throws InterruptedException
- 使當(dāng)前線程等待指定的毫秒數(shù),除非另一個(gè)線程調(diào)用
notify()
或notifyAll()
,或當(dāng)前線程被中斷。使用該方法時(shí),傳入的timeout
參數(shù)是最大等待時(shí)間。如果timeout
為0,則一直等待直到被通知或中斷。
?12. public final void wait(long timeout, int nanos) throws InterruptedException
- 使當(dāng)前線程等待至多
timeout
毫秒加nanos
納秒,除非另一個(gè)線程調(diào)用notify()
或notifyAll()
,或當(dāng)前線程被中斷。這個(gè)方法允許更精細(xì)的控制等待的時(shí)間。????????注意,在使用
wait()
、notify()
和notifyAll()
這幾個(gè)方法時(shí),必須在同步塊或同步方法中調(diào)用,這是因?yàn)樗鼈冃枰i定對(duì)象監(jiān)視器。雖然
Object
類提供了這些基本方法,通常在實(shí)際開(kāi)發(fā)中會(huì)通過(guò)各種并發(fā)工具類(如java.util.concurrent
包中的類)來(lái)處理線程同步和通知問(wèn)題,因?yàn)樗鼈兲峁┝烁痈呒?jí)、易于使用和更可靠的并發(fā)管理功能。最后需要提醒的是,
Object
類的某些方法如finalize()
已被標(biāo)記為過(guò)時(shí),因?yàn)樗赡軙?huì)導(dǎo)致程序性能問(wèn)題,并且不保證垃圾收集器會(huì)按時(shí)調(diào)用它。從Java 9開(kāi)始,finalize()
方法被明確標(biāo)記為過(guò)時(shí)(deprecated),并推薦使用其他資源釋放機(jī)制,如try-with-resources
語(yǔ)句來(lái)管理資源自動(dòng)關(guān)閉。
????????
二、HashMap原理,是否存在線程安全問(wèn)題
? ? HashMap
?是 Java 中一種基于哈希表的?Map
?接口的實(shí)現(xiàn)。它存儲(chǔ)的內(nèi)容是鍵值對(duì) (key-value
?對(duì)),每個(gè)鍵映射到一個(gè)值。HashMap
?允許使用 null 值和 null 鍵。????????HashMap簡(jiǎn)單說(shuō)就是它根據(jù)鍵的hashCode值存儲(chǔ)數(shù)據(jù),?多數(shù)情況下可以直接定位到它的值,因?具有很快的訪問(wèn)速 度,但遍歷順序卻是不確定的。
????????HashMap基于哈希表,底層結(jié)構(gòu)由數(shù)組來(lái)實(shí)現(xiàn),添加到集合中的元素以“key--value”形式保存到數(shù)組中,在數(shù)組中key- -value被包裝成?個(gè)實(shí)體來(lái)處理---也就是上?Map接?中的Entry。
????????在HashMap中, Entry[]保存了集合中所有的鍵值對(duì),當(dāng)我們需要快速存儲(chǔ)、獲取、刪除集合中的元素時(shí),?? HashMap會(huì) 根據(jù)hash算法來(lái)獲得“鍵值對(duì)”在數(shù)組中存在的位置,以來(lái)實(shí)現(xiàn)對(duì)應(yīng)的操作?法。
????????HashMap底層是采?數(shù)組來(lái)維護(hù)的.Entry靜態(tài)內(nèi)部類的數(shù)組
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } }
????????HashMap添加元素:將準(zhǔn)備增加到map中的對(duì)象與該位置上的對(duì)象進(jìn)??較(equals?法),如果相同,那么就將該位置上 的那個(gè)對(duì)象(Entry類型)的value值替換掉,否則沿著該Entry的鏈繼續(xù)重復(fù)上述過(guò)程,如果到鏈的最后任然沒(méi)有找到與此對(duì)象相同的對(duì)? 象,那么這個(gè)時(shí)候就會(huì)被增加到數(shù)組中,將數(shù)組中該位置上的那個(gè)Entry對(duì)象鏈到該對(duì)象的后?(先hashcode計(jì)算位置,如果找到相同 位置便替換值,找不到則重復(fù)hashcode計(jì)算,直到最后在添加到hashmap最后?;? )?
????????HashMap是基于哈希表的Map接?的?同步實(shí)現(xiàn),允許null鍵值,但不保證映射的順序 ;底層使?數(shù)組實(shí)現(xiàn),數(shù)組中的 每項(xiàng)是?個(gè)鏈表 ;存儲(chǔ)時(shí)根據(jù)key的hash算法來(lái)決定其存儲(chǔ)位置;數(shù)組擴(kuò)容需要重新計(jì)算擴(kuò)容后每個(gè)元素在數(shù)組中的位置? 很耗性能;
????????ConcurrentHashMap是HashMap的線程安全實(shí)現(xiàn),允許多個(gè)修改操作同時(shí)進(jìn)?(使?了鎖分離技術(shù)),它使?了多個(gè)鎖來(lái)
控制對(duì)hash表的不同段進(jìn)?的修改,每個(gè)段其實(shí)就是?個(gè)?的hashtable,它們有??的鎖。使?了多個(gè)?hash表(段 Segment),允許多個(gè)讀操作并發(fā)進(jìn)?,讀操作并不需要鎖,因?yàn)樗腍ashEntry?乎是不可變的
????????
這是?
HashMap
?的一些主要原理和工作方式:
存儲(chǔ)結(jié)構(gòu):
HashMap
?在內(nèi)部使用一個(gè)數(shù)組來(lái)存儲(chǔ)數(shù)據(jù),這個(gè)數(shù)組又被稱為“桶”(Bucket)。每個(gè)桶是一個(gè)鏈表,鏈表的每一個(gè)節(jié)點(diǎn)是一個(gè)?Entry
?對(duì)象,該對(duì)象包含鍵、值以及指向下一個(gè)?Entry
?節(jié)點(diǎn)的引用。哈希函數(shù):
當(dāng)我們向?HashMap
?中插入一個(gè)?key-value
?對(duì)時(shí),它首先會(huì)使用哈希函數(shù)計(jì)算出鍵對(duì)象的哈希碼。HashMap
?通過(guò)使用?key.hashCode()
?方法來(lái)獲取哈希碼,然后通過(guò)內(nèi)部的哈希函數(shù)來(lái)轉(zhuǎn)換成數(shù)組索引。沖突解決:
由于桶的數(shù)量有限,會(huì)發(fā)生不同鍵的哈希碼產(chǎn)生相同數(shù)組索引的情況,這稱為“哈希沖突”。HashMap
?使用鏈表來(lái)解決沖突,所有哈希值相同的元素會(huì)被存儲(chǔ)在同一個(gè)桶的鏈表中。從 Java 8 開(kāi)始,當(dāng)同一個(gè)桶中的元素個(gè)數(shù)超過(guò)一定的閾值(默認(rèn)是鏈表長(zhǎng)度大于 8),鏈表會(huì)被轉(zhuǎn)換成紅黑樹(shù)以提高性能。查找元素:
在需要獲取元素時(shí),HashMap
?使用鍵對(duì)象的哈希碼來(lái)找到其在數(shù)組中的桶位,然后遍歷鏈表或紅黑樹(shù)(如果轉(zhuǎn)換成紅黑樹(shù)的話)來(lái)找到對(duì)應(yīng)的節(jié)點(diǎn)。擴(kuò)容:
當(dāng)?HashMap
?中的元素?cái)?shù)量達(dá)到數(shù)組大小和加載因子(load factor,默認(rèn)是 0.75)的乘積時(shí),HashMap
?會(huì)進(jìn)行擴(kuò)容操作,即創(chuàng)建一個(gè)新的更大的數(shù)組,并將舊數(shù)組中的所有元素重新插入到新數(shù)組中。這個(gè)過(guò)程叫做“rehash”。迭代:
HashMap
?的迭代器(Iterator)遍歷時(shí)按照哈希桶的順序進(jìn)行,而不是按照鍵或值的排序順序。若在迭代過(guò)程中對(duì)?HashMap
?結(jié)構(gòu)進(jìn)行修改,很可能會(huì)拋出?ConcurrentModificationException
(快速失敗行為)。
HashMap
?的這些特性使它成為一個(gè)在大多數(shù)情況下都有良好性能的鍵值存儲(chǔ)結(jié)構(gòu)。但是正確地了解和使用?HashMap
?的原理對(duì)于避免性能問(wèn)題和正確地進(jìn)行內(nèi)存使用仍然非常重要。????????
由于?
HashMap
?是非線程安全的,當(dāng)多個(gè)線程同時(shí)對(duì)其進(jìn)行修改時(shí),可能會(huì)出現(xiàn)幾種問(wèn)題。這些問(wèn)題不只限制于數(shù)據(jù)的不一致性,還可能引發(fā)程序的崩潰。以下是一些可能出現(xiàn)的具體問(wèn)題:
數(shù)據(jù)丟失:
當(dāng)兩個(gè)線程同時(shí)執(zhí)行?put
?操作,它們可能計(jì)算出相同的存儲(chǔ)位置從而覆蓋對(duì)方的數(shù)據(jù),這將導(dǎo)致其中一個(gè)鍵值對(duì)丟失。無(wú)限循環(huán):
在 JDK 7 及之前版本的?HashMap
?中,多線程環(huán)境下擴(kuò)容(rehashing)可以導(dǎo)致循環(huán)鏈表的出現(xiàn),這會(huì)導(dǎo)致?get
?方法陷入無(wú)限循環(huán)。數(shù)據(jù)不一致:
如果一個(gè)線程正在讀取,而另一個(gè)線程同時(shí)修改了數(shù)據(jù)結(jié)構(gòu),讀線程可能會(huì)看到部分更新的數(shù)據(jù),從而導(dǎo)致不可預(yù)料的結(jié)果。
ConcurrentModificationException
?異常:
當(dāng)一個(gè)線程迭代?HashMap
?時(shí),如果另外一個(gè)線程修改了?HashMap
?的結(jié)構(gòu)(添加或刪除任何元素),那么迭代器將快速失敗并拋出?ConcurrentModificationException
。內(nèi)存泄漏:
在并發(fā)環(huán)境下,由于線程修改的不同步可能導(dǎo)致某些?Entry
?節(jié)點(diǎn)從未正確刪除,致使垃圾收集器無(wú)法回收這部分內(nèi)存,隨之產(chǎn)生內(nèi)存泄漏。如果需要在多線程環(huán)境下使用?
Map
?結(jié)構(gòu)而又不想處理上述問(wèn)題,可以使用一些線程安全的替代方案,例如:
Collections.synchronizedMap(new HashMap<>()
:使用 Collections 工具為?HashMap
?提供同步的包裝器,但是每個(gè)方法調(diào)用都是同步的,可能會(huì)導(dǎo)致不必要的性能損耗。ConcurrentHashMap
:一種線程安全且高效的?HashMap
?替代實(shí)現(xiàn)。它利用分鎖機(jī)制提供更高的并發(fā)性,通常是多線程環(huán)境下?HashMap
?的最佳選擇。了解問(wèn)題和可能的解決方案,可以確保在多線程環(huán)境中有效地使用?
HashMap
,避免競(jìng)態(tài)條件和其他同步相關(guān)問(wèn)題。
????????
三、Java如何進(jìn)?線程同步
在 Java 中,線程同步是指多個(gè)線程訪問(wèn)共享資源時(shí),確保每個(gè)線程看到一致的內(nèi)存狀態(tài)且不會(huì)相互干擾的機(jī)制。為了防止線程間出現(xiàn)沖突,Java 提供了多種線程同步的機(jī)制。
同步方法?(Synchronized Methods):
在方法聲明中加入?synchronized
?關(guān)鍵字可以使該方法在同一時(shí)間內(nèi)只能被一個(gè)線程訪問(wèn)。當(dāng)一個(gè)線程訪問(wèn)一個(gè)對(duì)象的?synchronized
?方法時(shí),其他試圖訪問(wèn)該對(duì)象的?synchronized
?方法的線程將會(huì)阻塞。public synchronized void method() { // 同步代碼 }
同步塊?(Synchronized Blocks):
如果只有方法中的某個(gè)代碼塊需要同步,可以使用?synchronized
?關(guān)鍵字來(lái)同步一個(gè)代碼塊,這比同步整個(gè)方法更加細(xì)粒度,可以減少等待時(shí)間,從而提高性能。public void method() { // ... 非同步代碼 synchronized(this) { // this 是鎖定的對(duì)象,也可以是其他對(duì)象 // 同步代碼 } // ... 非同步代碼 }
鎖?(Locks):
Java?java.util.concurrent.locks
?包提供了更加靈活的鎖定機(jī)制。其中?ReentrantLock
?是常用的實(shí)現(xiàn),它具有與使用關(guān)鍵字?synchronized
?類似的基本行為和語(yǔ)義,但擁有額外的功能。import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Counter { private final Lock lock = new ReentrantLock(); private int count = 0; public void increment() { lock.lock(); try { count++; } finally { // 確保在發(fā)生異常時(shí)鎖能被釋放 lock.unlock(); } } }
原子變量:
java.util.concurrent.atomic
?包提供了一組原子類,例如?AtomicInteger
,?AtomicLong
,?AtomicReference
?等。這些類利用底層硬件的原子指令來(lái)實(shí)現(xiàn)同步,而無(wú)需使用傳統(tǒng)的鎖機(jī)制。這些操作是非阻塞的,并且往往是性能更高的選項(xiàng)。import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getValue() { return count.get(); } }
volatile 關(guān)鍵字:
當(dāng)一個(gè)變量被聲明為?volatile
,那么線程每次讀取變量時(shí)都會(huì)從內(nèi)存中讀,每次寫(xiě)回變量時(shí)都會(huì)寫(xiě)入到內(nèi)存中。這確保了該變量的可見(jiàn)性,即當(dāng)一個(gè)線程更新了該變量時(shí),其他線程能立刻看到修改后的值。public class SharedObject { private volatile int sharedVariable = 0; public void increment() { sharedVariable++; } public int getSharedVariable() { return sharedVariable; } }
????????請(qǐng)注意,
volatile
?不能保證復(fù)合操作(如自增)的原子性,它僅保證變量的讀寫(xiě)操作的可見(jiàn)性。使用哪種同步機(jī)制取決于具體的情況,正確的選擇可以提供良好的性能同時(shí)確保線程安全。對(duì)于一些高級(jí)的同步需要,我們可能還需要使用到?
Semaphore
,?CountDownLatch
,?CyclicBarrier
,?Phaser
?等同步工具,它們?cè)?java.util.concurrent
?包下提供了更為復(fù)雜的并發(fā)控制。
????????
四、CAS原理
CAS(Compare-And-Swap 或 Compare-And-Set)是一種用于實(shí)現(xiàn)多線程同步的技術(shù)。它涉及對(duì)內(nèi)存中的某些值進(jìn)行原子地比較和更新。"原子"指的是操作作為一個(gè)不可分割的單元執(zhí)行,要么完全執(zhí)行,要么完全不執(zhí)行,不會(huì)出現(xiàn)中間狀態(tài)。
CAS 操作通常包括以下三個(gè)操作數(shù):
- 內(nèi)存位置(V):要操作的內(nèi)存地址。
- 期望原值(A):期望讀到的值。
- 新值(B):若期望原值驗(yàn)證為真,即內(nèi)存位置的值與期望原值相等,則更新為此新值。
這個(gè)操作的偽代碼可以表示為:
if memory_value == expected_value memory_value = new_value else handle the failure (do nothing or retry or abort, etc.)
執(zhí)行 CAS 操作的基本步驟是:
- 系統(tǒng)從內(nèi)存地址 V 讀取當(dāng)前值。
- 檢查當(dāng)前值是否與期望原值 A 相等。
- 如果相等,則將新值 B 更新到內(nèi)存地址 V。
- 如果不等,則不做任何操作或者采取其他補(bǔ)救措施(比如重試或回滾)。
CAS 操作是無(wú)鎖編程中常用的技術(shù),有助于在不使用傳統(tǒng)鎖機(jī)制的情況下實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的安全操作。在多處理器系統(tǒng)中,CAS 是通過(guò)硬件指令直接支持的,因此可以高效地執(zhí)行。
CAS 有以下優(yōu)點(diǎn):
- 性能:由于不需要鎖定,CAS 可以減少線程上下文切換的成本,通常比使用鎖更高效。
- 死鎖避免:既然沒(méi)有使用傳統(tǒng)的鎖,也就不可能出現(xiàn)死鎖的情況。
不過(guò),CAS 也存在一些問(wèn)題:
- ABA 問(wèn)題:如果值從 A 變成 B,然后又變回 A,CAS 會(huì)認(rèn)為什么都沒(méi)有改變,然而實(shí)際上可能發(fā)生了重要的變動(dòng)。
- 循環(huán)時(shí)間長(zhǎng):如果很多線程同時(shí)嘗試進(jìn)行 CAS,那么只有一個(gè)能成功,其他線程可能不得不多次嘗試。
- 只能保證一個(gè)共享變量的原子操作:如果需要同時(shí)更新多個(gè)共享變量,則不能直接使用 CAS。
總的來(lái)說(shuō),CAS 是一種基于硬件實(shí)現(xiàn)的輕量級(jí)同步機(jī)制,適用于某些不需要嚴(yán)格的鎖定機(jī)制就能解決的線程安全問(wèn)題。它是 Java 中?
java.util.concurrent.atomic
?包中的原子變量類的關(guān)鍵實(shí)現(xiàn)技術(shù)。
????????
五、JVM垃圾回收之GC算法有哪些
Java 虛擬機(jī)(JVM)使用垃圾回收(GC)算法來(lái)管理和回收不再使用的內(nèi)存。主要的垃圾回收算法包括:
標(biāo)記-清除(Mark-Sweep)算法:
- 標(biāo)記:該階段標(biāo)記出所有從根集合開(kāi)始可達(dá)的對(duì)象。
- 清除:回收所有未被標(biāo)記的對(duì)象占用的內(nèi)存。
缺點(diǎn)是兩個(gè)主要方面:標(biāo)記和清除過(guò)程的效率不高,以及清除后容易產(chǎn)生內(nèi)存碎片。復(fù)制(Copy or Scavenge)算法:
- 將內(nèi)存分為兩個(gè)相等的區(qū)域,每次只使用其中的一個(gè)。
- 當(dāng)進(jìn)行垃圾回收時(shí),將正在使用的內(nèi)存區(qū)域中存活的對(duì)象復(fù)制到未使用的區(qū)域,然后清除正在使用的內(nèi)存區(qū)域中的所有對(duì)象。
- 優(yōu)點(diǎn)是避免了內(nèi)存碎片,缺點(diǎn)是內(nèi)存利用效率為 50%。
標(biāo)記-整理(Mark-Compact)算法:
- 結(jié)合了“標(biāo)記-清除”和“復(fù)制”算法的優(yōu)點(diǎn)。
- 標(biāo)記:和標(biāo)記-清除算法一樣進(jìn)行標(biāo)記。
- 整理:將所有存活的對(duì)象壓縮到內(nèi)存的一端,清理掉端邊界外的內(nèi)存。
優(yōu)點(diǎn)是解決內(nèi)存碎片問(wèn)題而不需要犧牲過(guò)多的內(nèi)存。增量(Incremental)垃圾回收:
- 讓垃圾回收分批進(jìn)行,不是一次性清理完所有垃圾。
- 目的是減少每次垃圾回收的時(shí)間,使得垃圾回收對(duì)系統(tǒng)的影響更為平滑。
分代收集(Generational Collection)算法:
- 根據(jù)對(duì)象存活周期的不同,將內(nèi)存劃分為新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation,但在 Java 8 中已經(jīng)被移除,改成了元空間(MetaSpace))。
- 新生代使用復(fù)制算法,老年代一般采用標(biāo)記-清除或標(biāo)記-整理算法。
并行(Parallel)垃圾回收:
- 使用多個(gè)垃圾回收線程并行執(zhí)行垃圾回收,以提高垃圾回收的效率。
- 適用于多核處理器,能夠更高效地進(jìn)行垃圾回收。
并發(fā)(Concurrent)垃圾回收:
- 允許垃圾回收線程與應(yīng)用程序線程同時(shí)工作。
- 旨在減少應(yīng)用程序的停頓時(shí)間,適合需要響應(yīng)時(shí)間快的應(yīng)用。
JVM 中有不同的垃圾回收器實(shí)現(xiàn)上述算法。常見(jiàn)的垃圾回收器有:
- Serial GC:?jiǎn)尉€程運(yùn)行,采用標(biāo)記-復(fù)制算法,適合單核處理器或小內(nèi)存環(huán)境。
- Parallel GC (也稱為吞吐量收集器):多線程執(zhí)行,側(cè)重增加吞吐量。
- CMS (Concurrent Mark-Sweep) GC:以獲取最短停頓時(shí)間為目標(biāo),使用并發(fā)標(biāo)記清除算法。
- G1 (Garbage-First) GC:旨在為多核機(jī)器提供高吞吐量和低延遲的性能表現(xiàn),適用于大內(nèi)存。
與這些收集器相關(guān)的具體細(xì)節(jié)和性能特征,取決于 JVM 的實(shí)現(xiàn)和版本。在 OpenJDK 和 Oracle JDK 9 及以后的版本中,還提供了新的垃圾回收器(如 ZGC 和 Shenandoah),旨在降低停頓時(shí)間,同時(shí)適應(yīng)大內(nèi)存和多處理器環(huán)境。
????????
六、Mysql索引原理以及查詢優(yōu)化
MySQL索引是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。以下是一些基本的MySQL索引類型和它們的工作原理:
B+Tree索引:最常用的索引類型,適合用于全鍵值、鍵值范圍或鍵值前綴查找。B-Tree索引能夠加速數(shù)據(jù)的讀取,但會(huì)額外消耗一些空間來(lái)存儲(chǔ)索引,并且在插入、更新和刪除時(shí)需要同步更新索引。
哈希索引:基于哈希表的實(shí)現(xiàn),適合等值查詢,不適合范圍查詢和部分鍵匹配查詢。主要優(yōu)點(diǎn)是查找速度快,缺點(diǎn)是不支持排序和分組。
全文索引?(Fulltext Indexes):特別適用于對(duì)文本內(nèi)容進(jìn)行搜索的應(yīng)用,可以快速定位包含某個(gè)詞或短語(yǔ)的記錄。
空間數(shù)據(jù)索引?(R-Tree):用于空間數(shù)據(jù)類型,如GIS數(shù)據(jù)。
工作原理:
- B+Tree索引:通過(guò)維護(hù)一個(gè)平衡搜索樹(shù)來(lái)進(jìn)行優(yōu)化檢索,數(shù)據(jù)按照順序存儲(chǔ),可用于查找、排序和分組操作。
查詢優(yōu)化:
- 選擇正確的索引:了解不同類型的查詢和哪些情況下最適合使用索引。找出查詢中常用的列并為它們建立索引。
- 索引列上進(jìn)行操作:避免在索引列上做運(yùn)算或使用函數(shù),這將導(dǎo)致無(wú)法使用索引。
- 使用最左前綴法則:對(duì)于復(fù)合索引,確保查詢條件與索引中列的順序一致。
- 避免全表掃描:盡量使用索引來(lái)避免全表掃描,適當(dāng)?shù)叵拗撇樵兎祷氐男袛?shù)。
- 索引的選擇性:選擇性是不重復(fù)的索引值與數(shù)據(jù)表中的記錄數(shù)的比例,選擇性越高的索引性能越好。
- 使用Explain來(lái)分析查詢:使用EXPLAIN語(yǔ)句來(lái)分析你的查詢以及索引使用情況,可以幫助發(fā)現(xiàn)性能瓶頸。
- 適當(dāng)?shù)腖ike語(yǔ)句:當(dāng)必須使用LIKE查詢時(shí),如果模式以通配符開(kāi)頭,索引將不會(huì)被使用。盡可能避免
'%value%'
這種模式,而使用'value%'
。????????確保定期維護(hù)索引以防止索引碎片,導(dǎo)致查詢效率下降。通過(guò)定期運(yùn)行
OPTIMIZE TABLE
命令,可以幫助重新組織存儲(chǔ)和重新構(gòu)建數(shù)據(jù)庫(kù)索引,進(jìn)而提高查詢效率。
????????
七、TCP擁塞控制
TCP擁塞控制是一種網(wǎng)絡(luò)機(jī)制,它旨在防止過(guò)多的數(shù)據(jù)同時(shí)注入網(wǎng)絡(luò),從而避免網(wǎng)絡(luò)中的路由器或鏈路過(guò)載,導(dǎo)致過(guò)度的延遲或丟包。TCP的擁塞控制通過(guò)動(dòng)態(tài)調(diào)整各TCP連接的發(fā)送窗口大小來(lái)控制它們各自的發(fā)送速率。
以下是TCP擁塞控制的主要算法:
?1. 慢啟動(dòng)(Slow Start):
- TCP連接開(kāi)始時(shí),擁塞窗口(cwnd)從一個(gè)很小的值(通常是一個(gè)MSS,即最大報(bào)文段大?。╅_(kāi)始增長(zhǎng),每當(dāng)收到一個(gè)ACK,cwnd就增加一個(gè)MSS,這樣,cwnd每個(gè)RTT(往返時(shí)間)就翻倍增長(zhǎng),呈指數(shù)增長(zhǎng)。
- 當(dāng)cwnd達(dá)到慢啟動(dòng)閾值(ssthresh)時(shí),TCP切換到擁塞避免算法。
?2. 擁塞避免(Congestion Avoidance):
- 這個(gè)階段TCP轉(zhuǎn)變?yōu)楦€(wěn)健的增長(zhǎng)方式,每個(gè)RTT只增加一個(gè)MSS,呈線性增長(zhǎng),以穩(wěn)步增加網(wǎng)絡(luò)負(fù)載。
- 如果出現(xiàn)丟包(如超時(shí)或接收到重復(fù)ACKs),TCP認(rèn)為網(wǎng)絡(luò)出現(xiàn)擁塞,并且將ssthresh設(shè)置為當(dāng)前cwnd的一半,并進(jìn)入快速恢復(fù)或慢啟動(dòng)。
?3. 快速重傳(Fast Retransmit):
- 如果發(fā)送端收到三個(gè)重復(fù)的ACK,它就知道該段后面的報(bào)文段一定是丟失了,而不是需要等待定時(shí)器超時(shí)。
- 它會(huì)立即重傳這個(gè)失序的報(bào)文段而不是等待超時(shí),同時(shí)不減小cwnd的大小以避免降低傳輸速率。
?4. 快速恢復(fù)(Fast Recovery):
- 在執(zhí)行快速重傳之后,TCP進(jìn)入快速恢復(fù)階段,ssthresh被設(shè)置為當(dāng)前cwnd的一半,cwnd被設(shè)置為ssthresh加3倍MSS(對(duì)于之前接收的三個(gè)重復(fù)ACK),然后每接收一個(gè)重復(fù)的ACK就增加一個(gè)MSS。
- 當(dāng)終于收到了一個(gè)新的ACK,認(rèn)為網(wǎng)絡(luò)恢復(fù)了,cwnd被重新設(shè)置為ssthresh的值,TCP進(jìn)入擁塞避免階段。
?5. 超時(shí)處理:
- 如果等待ACK的時(shí)間超過(guò)了預(yù)定的超時(shí)時(shí)間,TCP認(rèn)為發(fā)生了嚴(yán)重的擁塞,并將ssthresh設(shè)置為當(dāng)前cwnd的一半,并且將cwnd重新設(shè)置為1MSS,然后進(jìn)入慢啟動(dòng)階段。
????????隨著時(shí)間的推移,TCP的擁塞控制算法也不斷發(fā)展和完善,例如引入了更為復(fù)雜的算法如BBR(Bottleneck Bandwidth and RTT)等,旨在進(jìn)一步優(yōu)化性能。
????????TCP擁塞控制的工作原理是一個(gè)動(dòng)態(tài)的過(guò)程,它需要根據(jù)網(wǎng)絡(luò)的實(shí)時(shí)狀態(tài)不斷地調(diào)整發(fā)送速率。這些機(jī)制使得TCP能夠動(dòng)態(tài)適應(yīng)不同的網(wǎng)絡(luò)擁塞情況,能夠在各種類型的網(wǎng)絡(luò)中有效地傳輸數(shù)據(jù),同時(shí)又不會(huì)因?yàn)檫^(guò)多的數(shù)據(jù)流量而導(dǎo)致網(wǎng)絡(luò)崩潰。
????????
八、算法:? 給定—棵二叉樹(shù),找到這棵樹(shù)最中最后—行中最左邊的值
????????為了在Java中實(shí)現(xiàn)這個(gè)算法,您可以創(chuàng)建一個(gè)
TreeNode
類表示二叉樹(shù)節(jié)點(diǎn),然后使用廣度優(yōu)先搜索(BFS)遍歷整棵樹(shù)。通過(guò)隊(duì)列來(lái)實(shí)現(xiàn)BFS算法,您可以輕松地按層遍歷二叉樹(shù),并記錄下每層的第一個(gè)節(jié)點(diǎn)。????????以下是一個(gè)實(shí)現(xiàn)這一功能的Java代碼示例:
import java.util.Queue; import java.util.LinkedList; class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class BinaryTreeBottomLeftValue { public int findBottomLeftValue(TreeNode root) { if (root == null) return -1; Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); int result = root.val; // 初始化結(jié)果為根節(jié)點(diǎn)的值 while (!queue.isEmpty()) { int size = queue.size(); // 當(dāng)前層的節(jié)點(diǎn)數(shù)量 for (int i = 0; i < size; i++) { TreeNode node = queue.poll(); // 如果是當(dāng)前層的第一個(gè)節(jié)點(diǎn),更新結(jié)果值 if (i == 0) result = node.val; // 添加子節(jié)點(diǎn)到隊(duì)列 if (node.left != null) queue.add(node.left); if (node.right != null) queue.add(node.right); } } return result; // 返回最后一行最左邊的值 } public static void main(String[] args) { // 示例二叉樹(shù) // 1 // / \ // 2 3 // / / \ // 4 5 6 // / // 7 // 示例二叉樹(shù)構(gòu)建 TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.left.left = new TreeNode(4); root.right.left = new TreeNode(5); root.right.right = new TreeNode(6); root.right.left.left = new TreeNode(7); BinaryTreeBottomLeftValue solution = new BinaryTreeBottomLeftValue(); int bottomLeft = solution.findBottomLeftValue(root); System.out.println("Bottom left value: " + bottomLeft); // 應(yīng)輸出 7 } }
在這個(gè)Java程序中,我們定義了
TreeNode
類來(lái)表示樹(shù)的節(jié)點(diǎn),然后在BinaryTreeBottomLeftValue
類中添加了findBottomLeftValue
方法。這個(gè)方法使用隊(duì)列來(lái)追蹤需要訪問(wèn)的節(jié)點(diǎn),遍歷所有層,并記錄下每一層中的第一個(gè)節(jié)點(diǎn)值。最終,返回的就是最后一行中最左邊的值。在
main
方法中,我們建立了一個(gè)示例二叉樹(shù),并調(diào)用findBottomLeftValue
方法來(lái)獲取最后一行最左邊的值。
????????
(二面題,考察代碼能力。60分鐘)
九、知道什么設(shè)計(jì)模式,分別介紹
????????設(shè)計(jì)模式是解決軟件設(shè)計(jì)中常見(jiàn)問(wèn)題的通用、可重用的解決方案。設(shè)計(jì)模式可以分為三個(gè)主要類別:創(chuàng)建型、結(jié)構(gòu)型和行為型。下面是每一類中一些常用的設(shè)計(jì)模式及其簡(jiǎn)要介紹:
????????
創(chuàng)建型模式
創(chuàng)建型模式專注于如何創(chuàng)建對(duì)象或類的實(shí)例。
工廠方法模式(Factory Method):
允許接口定義創(chuàng)建對(duì)象的方法,但由子類決定要實(shí)例化的類的類型。工廠方法將對(duì)象的創(chuàng)建延遲到子類。抽象工廠模式(Abstract Factory):
提供一個(gè)接口用于創(chuàng)建相關(guān)或依賴對(duì)象的家族,而不需要明確指定具體類。建造者模式(Builder):
分離復(fù)雜對(duì)象的構(gòu)建和表示,同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。原型模式(Prototype):
通過(guò)拷貝一個(gè)現(xiàn)有對(duì)象的方式來(lái)創(chuàng)建對(duì)象,而不是通過(guò)實(shí)例化。單例模式(Singleton):
確保一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn)。????????
結(jié)構(gòu)型模式
結(jié)構(gòu)型模式與類和對(duì)象的組織有關(guān),它們定義了類之間的關(guān)系來(lái)實(shí)現(xiàn)更大的功能。
適配器模式(Adapter):
允許將不兼容的接口轉(zhuǎn)換為其他類可以工作的接口。橋接模式(Bridge):
分離抽象部分和實(shí)現(xiàn)部分,使它們可以獨(dú)立變化。組合模式(Composite):
允許將對(duì)象組成樹(shù)形結(jié)構(gòu)來(lái)表示“部分-整體”的層次結(jié)構(gòu)。使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。裝飾器模式(Decorator):
向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。外觀模式(Facade):
提供了一個(gè)統(tǒng)一的接口,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口。外觀定義了一個(gè)高層接口,讓子系統(tǒng)更容易使用。享元模式(Flyweight):
運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象。代理模式(Proxy):
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。????????
行為型模式
行為型模式專注于算法和對(duì)象間職責(zé)的分配。
責(zé)任鏈模式(Chain of Responsibility):
為請(qǐng)求創(chuàng)建一個(gè)接收者對(duì)象的鏈。這種模式給更多的對(duì)象一個(gè)機(jī)會(huì)處理請(qǐng)求。命令模式(Command):
將請(qǐng)求封裝為一個(gè)對(duì)象,從而允許我們使用不同的請(qǐng)求、隊(duì)列請(qǐng)求、記錄日志等來(lái)參數(shù)化其他對(duì)象。解釋器模式(Interpreter):
定義一種語(yǔ)法用于一個(gè)某個(gè)特定類型的問(wèn)題,并提供該語(yǔ)法的解釋器。迭代器模式(Iterator):
提供一種方法順序訪問(wèn)一個(gè)聚合對(duì)象中各個(gè)元素,而又不暴露該對(duì)象的內(nèi)部表示。中介者模式(Mediator):
封裝多個(gè)對(duì)象之間復(fù)雜的交互和協(xié)作關(guān)系,中介者通過(guò)減少類之間的通信線條數(shù)量,來(lái)減少依賴性。備忘錄模式(Memento):
在不破壞封裝的前提下,捕獲并保存一個(gè)對(duì)象的內(nèi)部狀態(tài),以便在以后可以恢復(fù)到這個(gè)狀態(tài)。觀察者模式(Observer):
一種訂閱機(jī)制,當(dāng)一個(gè)對(duì)象狀態(tài)變化時(shí),所有依賴它的對(duì)象都會(huì)收到通知并自動(dòng)更新。狀態(tài)模式(State):
允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。策略模式(Strategy):
定義一系列算法,并將每一個(gè)算法封裝起來(lái),使他們可以互相替換。模板方法模式(Template Method):
定義算法的框架,允許子類在不改變算法結(jié)構(gòu)的情況下,重寫(xiě)算法的某些步驟。訪問(wèn)者模式(Visitor):
對(duì)于一個(gè)對(duì)象結(jié)構(gòu)中的元素,允許一個(gè)外部的訪問(wèn)者來(lái)訪問(wèn),無(wú)需改變對(duì)象結(jié)構(gòu)的具體元素類。這些設(shè)計(jì)模式在軟件開(kāi)發(fā)中被廣泛應(yīng)用,能夠幫助開(kāi)發(fā)者通過(guò)事先定義好的、經(jīng)過(guò)實(shí)戰(zhàn)考驗(yàn)的方法來(lái)解決各種設(shè)計(jì)問(wèn)題,從而編寫(xiě)出更加可維護(hù)、更加清晰、更加可靠的代碼。
? ? ? ? 詳解設(shè)計(jì)模式專欄:(后續(xù)慢慢更新)
????????
十、算法:求?序數(shù)組中第k?的數(shù)
????????要在無(wú)序數(shù)組中找到第k大的數(shù),可以使用快速選擇(Quickselect)算法。該算法的思想是基于快速排序的分治思想,但是只執(zhí)行必要的分區(qū)操作,從而更高效地找到第k大的數(shù)。
????????下面是使用Java實(shí)現(xiàn)快速選擇算法來(lái)找到無(wú)序數(shù)組中第k大的數(shù)的示例代碼:
import java.util.Random; public class QuickSelect { private static Random random = new Random(); public static int findKthLargest(int[] nums, int k) { return quickSelect(nums, 0, nums.length - 1, nums.length - k); } private static int quickSelect(int[] nums, int start, int end, int k) { if (start == end) { return nums[start]; } int pivotIndex = partition(nums, start, end); if (pivotIndex == k) { return nums[pivotIndex]; } else if (pivotIndex < k) { return quickSelect(nums, pivotIndex + 1, end, k); } else { return quickSelect(nums, start, pivotIndex - 1, k); } } private static int partition(int[] nums, int start, int end) { // 隨機(jī)選擇一個(gè)pivot int randomIndex = start + random.nextInt(end - start + 1); swap(nums, randomIndex, end); int pivot = nums[end]; int pivotIndex = start; for (int i = start; i < end; i++) { if (nums[i] < pivot) { swap(nums, i, pivotIndex); pivotIndex++; } } swap(nums, pivotIndex, end); return pivotIndex; } private static void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } public static void main(String[] args) { int[] nums = {3, 5, 2, 1, 6, 4}; int k = 2; int result = findKthLargest(nums, k); System.out.println("第" + k + "大的數(shù)是: " + result); } }
????????在示例代碼中,
findKthLargest
方法接受一個(gè)無(wú)序數(shù)組和一個(gè)整數(shù)k作為輸入,返回第k大的數(shù)。quickSelect
方法是實(shí)現(xiàn)快速選擇的遞歸函數(shù),用于分區(qū)和選擇。partition
方法根據(jù)選定的pivot元素將數(shù)組分成兩部分,并返回pivot的索引。????????在示例代碼的
main
方法中,我們提供了一個(gè)無(wú)序數(shù)組和一個(gè)k值進(jìn)行測(cè)試,并打印出結(jié)果。????????需要注意的是,上述代碼的示例是找無(wú)序數(shù)組中第k大的數(shù)。如果要找第k小的數(shù),只需修改遞歸調(diào)用的條件和相應(yīng)返回的數(shù)值即可。
????????快速選擇算法的時(shí)間復(fù)雜度為O(N),其中N是數(shù)組的長(zhǎng)度。
????????
十一、算法:求旋轉(zhuǎn)數(shù)組找最?值(?分法)
????????求解旋轉(zhuǎn)數(shù)組中最小值的問(wèn)題可以使用二分法進(jìn)行高效求解。旋轉(zhuǎn)數(shù)組是指將一個(gè)升序排列的數(shù)組的前若干個(gè)元素搬到數(shù)組末尾而得到的數(shù)組。
????????下面是使用Java實(shí)現(xiàn)二分法求解旋轉(zhuǎn)數(shù)組中最小值的示例代碼:
public class MinimumInRotatedArray { public static int findMin(int[] nums) { int left = 0; int right = nums.length - 1; while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] > nums[right]) { left = mid + 1; } else if (nums[mid] < nums[right]) { right = mid; } else { right--; } } return nums[left]; } public static void main(String[] args) { int[] nums = {4, 5, 6, 7, 0, 1, 2}; int min = findMin(nums); System.out.println("旋轉(zhuǎn)數(shù)組中的最小值是: " + min); } }
????????在示例代碼中,
findMin
方法接受一個(gè)旋轉(zhuǎn)數(shù)組作為輸入,并使用二分法來(lái)搜索旋轉(zhuǎn)數(shù)組中的最小值。算法首先初始化左右兩個(gè)指針,指向數(shù)組的第一個(gè)和最后一個(gè)元素。然后,通過(guò)計(jì)算中間元素的索引,將問(wèn)題的規(guī)??s小為子數(shù)組。在每一次迭代中,比較中間元素和最右側(cè)元素的大小關(guān)系,根據(jù)比較結(jié)果調(diào)整左右指針的位置,直到最小值被找到。????????在示例代碼的
main
方法中,我們提供了一個(gè)旋轉(zhuǎn)數(shù)組進(jìn)行測(cè)試,并打印出最小值。????????該算法的時(shí)間復(fù)雜度為O(logN),其中N是旋轉(zhuǎn)數(shù)組的長(zhǎng)度。二分法的每一次迭代都將問(wèn)題的規(guī)模減半,因此算法的時(shí)間復(fù)雜度是對(duì)數(shù)級(jí)別的。
????????
十二、算法:判斷?叉樹(shù)是否鏡像(遞歸)
????????判斷二叉樹(shù)是否鏡像可以使用遞歸的方式來(lái)解決。對(duì)于兩棵樹(shù)是鏡像的,意味著它們的根節(jié)點(diǎn)的值相同,并且每個(gè)樹(shù)的右子樹(shù)都與另一個(gè)樹(shù)的左子樹(shù)鏡像對(duì)稱。
????????下面是使用Java遞歸實(shí)現(xiàn)判斷二叉樹(shù)是否鏡像的示例代碼:
class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int val) { this.val = val; } } public class MirrorBinaryTree { public static boolean isMirror(TreeNode root) { if (root == null) { return true; } return isSymmetric(root.left, root.right); } private static boolean isSymmetric(TreeNode left, TreeNode right) { if (left == null && right == null) { return true; } if (left == null || right == null || left.val != right.val) { return false; } return isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left); } public static void main(String[] args) { TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(2); root.left.left = new TreeNode(3); root.left.right = new TreeNode(4); root.right.left = new TreeNode(4); root.right.right = new TreeNode(3); boolean result = isMirror(root); System.out.println("二叉樹(shù)是否鏡像: " + result); } }
????????在示例代碼中,
isMirror
方法接受一個(gè)二叉樹(shù)的根節(jié)點(diǎn)作為輸入,使用遞歸判斷二叉樹(shù)是否鏡像。在isSymmetric
方法中,首先判斷左右子樹(shù)是否都為空,如果是,則表示當(dāng)前節(jié)點(diǎn)及其子樹(shù)鏡像對(duì)稱。其次,判斷左右子樹(shù)是否都不為空,并且當(dāng)前節(jié)點(diǎn)的值相同,如果是,則遞歸判斷左子樹(shù)的左子樹(shù)和右子樹(shù)的右子樹(shù),以及左子樹(shù)的右子樹(shù)和右子樹(shù)的左子樹(shù)是否鏡像對(duì)稱。最后,如果上述條件都不滿足,則表示二叉樹(shù)不是鏡像。????????在示例代碼的
main
方法中,我們構(gòu)建了一個(gè)鏡像二叉樹(shù)進(jìn)行測(cè)試,并打印出結(jié)果。????????如果二叉樹(shù)為空,也可視為空鏡像,因此在
isMirror
方法中增加了對(duì)空樹(shù)的處理。????????該算法的時(shí)間復(fù)雜度為O(N),其中N是二叉樹(shù)中的節(jié)點(diǎn)數(shù)量。因?yàn)樗惴ㄐ枰闅v每個(gè)節(jié)點(diǎn)且只訪問(wèn)一次,所以時(shí)間復(fù)雜度與節(jié)點(diǎn)數(shù)量成線性關(guān)系。
????????
(三面,開(kāi)放式問(wèn)題,40分鐘)
十三、如何理解前后端分離
????????前后端分離是一種Web應(yīng)用開(kāi)發(fā)模式,它將用戶界面(UI)及前端業(yè)務(wù)邏輯與后端服務(wù)及數(shù)據(jù)處理邏輯分開(kāi),使得前端和后端可獨(dú)立開(kāi)發(fā)與部署。這種模式帶來(lái)了一系列的優(yōu)勢(shì),同時(shí)也對(duì)開(kāi)發(fā)流程和架構(gòu)提出了新的要求。
????????以下是前后端分離的幾個(gè)主要方面和好處:
?1. 角色和技術(shù)棧分離:
- 前端(Front-end):負(fù)責(zé)展示用戶界面和用戶交互,通常使用HTML、CSS和JavaScript等技術(shù)構(gòu)建,使用現(xiàn)代框架和庫(kù)如React、Vue、Angular,進(jìn)行豐富的交互式體驗(yàn)的開(kāi)發(fā)。
- 后端(Back-end):負(fù)責(zé)處理業(yè)務(wù)邏輯、數(shù)據(jù)庫(kù)交云、認(rèn)證授權(quán)等服務(wù)器端功能,可以使用各種編程語(yǔ)言和框架,如Java Spring Boot、Python Django、Node.js Express等。
?2. 開(kāi)發(fā)和部署獨(dú)立:
- 開(kāi)發(fā)人員可以專注于各自擅長(zhǎng)的領(lǐng)域,前端開(kāi)發(fā)者專注于用戶體驗(yàn)和界面構(gòu)建,后端開(kāi)發(fā)者專注于數(shù)據(jù)處理和業(yè)務(wù)規(guī)則實(shí)現(xiàn)。
- 前后端代碼可以分別部署在不同的服務(wù)器上,提供更靈活的擴(kuò)展和管理選項(xiàng)。
?3. 通訊通過(guò)API:
- 前后端之間通過(guò)定義良好的API接口交互,通常是RESTful API或GraphQL等形式。
- 使用JSON或XML等數(shù)據(jù)交換格式,前端通過(guò)HTTP請(qǐng)求與后端通訊。
?4. 增強(qiáng)用戶體驗(yàn):
- 由于前端可獨(dú)立更新,所以能夠快速響應(yīng)市場(chǎng)變化,提高用戶體驗(yàn)。
- 前端可實(shí)現(xiàn)單頁(yè)面應(yīng)用(SPA),動(dòng)態(tài)加載內(nèi)容而無(wú)需重新加載整個(gè)頁(yè)面。
?5. 提高開(kāi)發(fā)效率:
- 前后端分離允許前后端團(tuán)隊(duì)并行工作,只要API契約定義好,雙方即可獨(dú)立進(jìn)行開(kāi)發(fā)。
- 可利用各種前端開(kāi)發(fā)和調(diào)試工具,加快前端開(kāi)發(fā)進(jìn)度。
?6. 便于擴(kuò)展和維護(hù):
- 各自的更新和維護(hù)不會(huì)互相影響,減少了開(kāi)發(fā)和部署的復(fù)雜性。
- 由于前后端的解耦,更容易對(duì)系統(tǒng)進(jìn)行擴(kuò)展和整合新技術(shù)。
????????盡管前后端分離帶來(lái)了上述優(yōu)點(diǎn),但同時(shí)也帶來(lái)了一些挑戰(zhàn),例如跨域資源共享(CORS)問(wèn)題、API版本管理、前后端接口聯(lián)調(diào)需要更好的溝通和文檔支持等。解決這些挑戰(zhàn)需要團(tuán)隊(duì)之間良好的溝通,以及合適的工具和流程來(lái)協(xié)調(diào)工作。
????????
十四、有哪些后端開(kāi)發(fā)經(jīng)驗(yàn),做了什么
????????應(yīng)該準(zhǔn)備一個(gè)簡(jiǎn)潔而詳細(xì)的回答來(lái)展示你的后端開(kāi)發(fā)技能、所使用的技術(shù)棧以及你在以往項(xiàng)目中的具體貢獻(xiàn)。以下是幾個(gè)方面參考:
?1. 概述后端技術(shù)棧:
????????開(kāi)始時(shí)簡(jiǎn)要介紹你使用過(guò)的后端語(yǔ)言和框架,如Java/Spring Boot、C#/ASP.NET、Python/Django、Node.js/Express等。?2. 描述特定的項(xiàng)目經(jīng)驗(yàn):
????????提供一個(gè)或幾個(gè)具體的項(xiàng)目例子,這些例子應(yīng)該能夠反映出你在后端開(kāi)發(fā)方面的專業(yè)水平和經(jīng)驗(yàn)。對(duì)每個(gè)項(xiàng)目,介紹以下信息:
- 項(xiàng)目的目標(biāo)和你的角色。
- 使用的技術(shù)棧和工具。
- 你主要負(fù)責(zé)的功能和任務(wù)。
- 所解決的關(guān)鍵問(wèn)題和挑戰(zhàn)。
- 項(xiàng)目的成果和你對(duì)成功所做的貢獻(xiàn)。
?3. 展示解決問(wèn)題的能力:
????????談?wù)勀阍诤蠖碎_(kāi)發(fā)中遇到的最有挑戰(zhàn)性的問(wèn)題以及你是如何解決這些問(wèn)題的。?4. 強(qiáng)調(diào)團(tuán)隊(duì)合作:
????????如果適用,討論你如何與前端開(kāi)發(fā)者、設(shè)計(jì)師和其他團(tuán)隊(duì)成員合作,以確保項(xiàng)目的順利進(jìn)行。?5. 討論性能和安全:
????????如果你有在項(xiàng)目中針對(duì)性能優(yōu)化或安全措施做出貢獻(xiàn)的經(jīng)驗(yàn),一定要提到。?6. 提供維護(hù)和擴(kuò)展的經(jīng)驗(yàn):
????????如果你參與了現(xiàn)有項(xiàng)目的維護(hù)或是為系統(tǒng)提供了擴(kuò)展功能,說(shuō)明你的工作如何提高了代碼質(zhì)量、系統(tǒng)可靠性或用戶體驗(yàn)。?7. 量化成果:
????????如果可能,提供一些量化的結(jié)果,比如性能提升的百分比、處理的請(qǐng)求量、減少的加載時(shí)間等。
?????????示例:
????????“我在后端開(kāi)發(fā)方面有5年的經(jīng)驗(yàn),主要使用Java和Spring Boot框架。我參與過(guò)多個(gè)企業(yè)級(jí)項(xiàng)目,例如開(kāi)發(fā)了一個(gè)支持?jǐn)?shù)百萬(wàn)并發(fā)用戶的電子商務(wù)平臺(tái),其中我負(fù)責(zé)實(shí)現(xiàn)訂單處理系統(tǒng)和用戶身份驗(yàn)證模塊。
????????除了Java,我也使用過(guò)Node.js開(kāi)發(fā)API服務(wù),并且熟悉數(shù)據(jù)庫(kù)技術(shù),比如MySQL和MongoDB。在最近的一個(gè)項(xiàng)目中,我優(yōu)化了數(shù)據(jù)庫(kù)查詢,使關(guān)鍵操作的速度提高了30%以上。
????????我還處理過(guò)對(duì)系統(tǒng)安全的改進(jìn)。我曾經(jīng)實(shí)現(xiàn)了一套基于OAuth 2.0的權(quán)限管理系統(tǒng),確保了我們的用戶數(shù)據(jù)的安全性。團(tuán)隊(duì)合作方面,我通常與前端開(kāi)發(fā)人員密切協(xié)作,確定API規(guī)格,并通過(guò)持續(xù)集成和代碼評(píng)審來(lái)維護(hù)代碼質(zhì)量。我們的協(xié)作導(dǎo)致了項(xiàng)目按時(shí)交付,客戶反饋表明用戶滿意度顯著提高?!?/p>
????????根據(jù)自己的經(jīng)驗(yàn)調(diào)整這個(gè)回答,保持真實(shí)性,并根據(jù)應(yīng)聘的崗位特點(diǎn)強(qiáng)調(diào)最相關(guān)的部分。
????????
十五、介紹HashMap與TreeMap區(qū)別
? ? HashMap
?和?TreeMap
?是 Java 的兩種常用的?Map
?實(shí)現(xiàn),它們都提供了鍵-值對(duì)存儲(chǔ)機(jī)制,但在內(nèi)部工作原理和特性上有所不同。以下是它們之間的主要區(qū)別:?1. 內(nèi)部結(jié)構(gòu):
HashMap
?基于散列表(哈希表)實(shí)現(xiàn),使用哈希函數(shù)來(lái)確定每個(gè)鍵值對(duì)(節(jié)點(diǎn))的存儲(chǔ)位置。TreeMap
?基于紅黑樹(shù)實(shí)現(xiàn),紅黑樹(shù)是一種自平衡的排序二叉樹(shù)。?2. 排序:
HashMap
?不保證任何排序,鍵值對(duì)的存儲(chǔ)是根據(jù)哈希值來(lái)決定的,迭代它時(shí)得到的順序是無(wú)序的。TreeMap
?根據(jù)鍵的自然順序或者構(gòu)造時(shí)所指定的?Comparator
?進(jìn)行排序。這意味著鍵會(huì)按升序排列,或者按?Comparator
?實(shí)現(xiàn)的順序排列。?3. 時(shí)間復(fù)雜度:
HashMap
?提供了常數(shù)時(shí)間的性能,即?O(1)
,對(duì)于?get
、put
?和?remove
?操作,理想情況下是這樣。但是,在最壞的情況下(例如當(dāng)所有元素都映射到同一個(gè)桶中時(shí)),性能可能會(huì)退化到?O(n)
。TreeMap
?保證了?get
、put
?和?remove
?操作的時(shí)間復(fù)雜度為?O(log n)
,因?yàn)樗腔跇?shù)的。?4. null 值:
HashMap
?允許鍵和值為?null
,意味著你可以將?null
?作為一個(gè)鍵或值插入到?HashMap
?中。TreeMap
?不允許鍵為?null
(因?yàn)樗枰凑漳撤N順序?qū)︽I進(jìn)行排序),但允許值為?null
。?5. 線程安全:
- 兩者都不是線程安全的。在多線程環(huán)境下,如果沒(méi)有正確的同步,任何結(jié)構(gòu)性修改都可能引發(fā)并發(fā)問(wèn)題。
- 如果需要線程安全,可以通過(guò)?
Collections.synchronizedMap
?來(lái)包裝?HashMap
,或者使用?ConcurrentHashMap
?代替?HashMap
。而?TreeMap
?沒(méi)有直接的線程安全對(duì)應(yīng),但可以考慮使用?ConcurrentSkipListMap
。?6. 性能:
HashMap
?通常在大部分場(chǎng)景下提供更好的性能,尤其是在添加和查詢?cè)貢r(shí)。而?TreeMap
?在維持映射的有序狀態(tài)方面表現(xiàn)更好,尤其適用于需要有序遍歷鍵時(shí)。????????選擇?
HashMap
?還是?TreeMap
?取決于應(yīng)用程序的具體需求。如果不需要排序,并且想要最快的訪問(wèn)速度,HashMap
?是較好的選項(xiàng)。如果需要一個(gè)總是處于排序狀態(tài)的鍵集合,TreeMap
?是更加適合的選擇。
????????
十六、?HashMap實(shí)現(xiàn)?個(gè)有過(guò)期功能的緩存
要使用 HashMap 實(shí)現(xiàn)一個(gè)具有過(guò)期功能的緩存,可以創(chuàng)建一個(gè)包裝類,這個(gè)類將包含每個(gè)緩存項(xiàng)的值和過(guò)期時(shí)間。我們可以在每次訪問(wèn)緩存時(shí)檢查該項(xiàng)是否過(guò)期。如果過(guò)期,我們就從緩存中移除這個(gè)項(xiàng)。另外,我們還可以創(chuàng)建一個(gè)維護(hù)過(guò)期項(xiàng)的線程或定時(shí)任務(wù),來(lái)定期清理過(guò)期的緩存項(xiàng)。
下面是一個(gè)簡(jiǎn)單的緩存實(shí)現(xiàn),使用了 HashMap:
import java.util.concurrent.*; public class ExpiringCache<K, V> { // 緩存項(xiàng)類包含值和過(guò)期時(shí)間 private class CacheItem { V value; long expiryTime; public CacheItem(V value, long expiryTime) { this.value = value; this.expiryTime = expiryTime; } } private final ConcurrentHashMap<K, CacheItem> cacheMap; private final ScheduledExecutorService executorService; public ExpiringCache() { cacheMap = new ConcurrentHashMap<>(); executorService = Executors.newSingleThreadScheduledExecutor(); // 定期執(zhí)行過(guò)期緩存清理任務(wù) executorService.scheduleAtFixedRate(() -> { long currentTime = System.currentTimeMillis(); cacheMap.entrySet().removeIf(entry -> currentTime > entry.getValue().expiryTime); }, 1, 1, TimeUnit.SECONDS); } // 向緩存添加項(xiàng),并設(shè)置過(guò)期時(shí)間 public void put(K key, V value, long expiryDurationInMillis) { long expiryTime = System.currentTimeMillis() + expiryDurationInMillis; cacheMap.put(key, new CacheItem(value, expiryTime)); } // 從緩存中獲取項(xiàng),如果不存在或已過(guò)期,則返回 null public V get(K key) { CacheItem item = cacheMap.get(key); if (item != null && System.currentTimeMillis() < item.expiryTime) { return item.value; } cacheMap.remove(key); // 如果已過(guò)期,移除 return null; } public void shutdown() { executorService.shutdownNow(); } }
????????在以上代碼中,我們創(chuàng)建了一個(gè)內(nèi)部類?
CacheItem
?來(lái)存儲(chǔ)緩存的值和該項(xiàng)的過(guò)期時(shí)間。通過(guò)?ScheduledExecutorService
, 我們安排了定期執(zhí)行的任務(wù),以清理所有過(guò)期的緩存項(xiàng)。put
?方法添加緩存項(xiàng)時(shí)需要指定一個(gè)過(guò)期時(shí)間。get
?方法只返回未過(guò)期的緩存項(xiàng),如果檢測(cè)到緩存項(xiàng)已過(guò)期,則將其移除。????????這個(gè)實(shí)現(xiàn)是線程安全的,因?yàn)槲覀兪褂昧?
ConcurrentHashMap
,它是一個(gè)支持完全并發(fā)的哈希表。我們也使用了?ScheduledExecutorService
?來(lái)定期檢查和清除過(guò)期項(xiàng)。????????請(qǐng)記得,在停止使用緩存或者程序結(jié)束時(shí)調(diào)用?
shutdown()
?方法來(lái)關(guān)閉?ScheduledExecutorService
。這很重要,因?yàn)槲覀儾幌M笈_(tái)線程在不需要時(shí)繼續(xù)運(yùn)行。????????
????????Google 的 Guava 庫(kù)中包含了一個(gè)功能強(qiáng)大的緩存實(shí)現(xiàn)?
Cache
?類(你可以自己用Cache來(lái)實(shí)現(xiàn)緩存過(guò)期功能試試)。????????下面是使用 Guava 緩存的一個(gè)簡(jiǎn)單例子:
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.concurrent.TimeUnit; public class GuavaCacheExample { public static void main(String[] args) throws Exception { // 創(chuàng)建緩存 LoadingCache<String, String> cache = CacheBuilder.newBuilder() .maximumSize(100) // 最多緩存項(xiàng) .expireAfterWrite(10, TimeUnit.MINUTES) // 寫(xiě)入10分鐘后過(guò)期 .build( new CacheLoader<String, String>() { public String load(String key) { return fetchData(key); } }); // 從緩存中取值,可能直接取緩存,或者加載數(shù)據(jù)后緩存 String value = cache.get("myKey"); System.out.println("Value for 'myKey': " + value); // 直接向緩存里添加一個(gè)鍵值對(duì) cache.put("anotherKey", "anotherValue"); // 得到某個(gè)鍵對(duì)應(yīng)的值(如果存在) System.out.println("Value for 'anotherKey': " + cache.getIfPresent("anotherKey")); } // 示范的數(shù)據(jù)加載方法,實(shí)際應(yīng)用中應(yīng)更復(fù)雜,例如數(shù)據(jù)庫(kù)查詢操作 private static String fetchData(String key) { // Here you should implement actual data fetching logic return "Data for " + key; } }
????????在這個(gè)例子中,我們使用 Guava 來(lái)創(chuàng)建一個(gè)緩存,它有一個(gè)最大項(xiàng)數(shù)限制,并且每個(gè)緩存項(xiàng)在寫(xiě)入10分鐘后會(huì)自動(dòng)過(guò)期。
CacheLoader
?被用來(lái)定義如何加載數(shù)據(jù)到緩存中。當(dāng)你調(diào)用?get()
?方法時(shí),Guava 自動(dòng)使用定義好的加載機(jī)制來(lái)提供值。如果緩存中已經(jīng)有這個(gè)值了,就會(huì)立刻返回。????????Guava 的?
Cache
?類也提供了很多其他功能,比如監(jiān)聽(tīng)器,手動(dòng)移除,緩存統(tǒng)計(jì)等。????????如果需要在商業(yè)項(xiàng)目中使用緩存,推薦使用成熟的緩存框架,比如 Guava,Caffeine,Ehcache 或者 Hazelcast 等。這些框架提供了更復(fù)雜的緩存策略,性能監(jiān)控,以及和其他技術(shù)的集成。
? ? ? ??
十七、平時(shí)怎么學(xué)習(xí)新知識(shí)
? ? ? ? 下面是一些常見(jiàn)的學(xué)習(xí)方法和技巧:
設(shè)定學(xué)習(xí)目標(biāo):開(kāi)始學(xué)習(xí)前,先設(shè)定清晰的、具體的學(xué)習(xí)目標(biāo),這可以幫助你保持專注并衡量自己的進(jìn)步。
按計(jì)劃學(xué)習(xí):建立一個(gè)學(xué)習(xí)計(jì)劃,為每個(gè)學(xué)習(xí)階段設(shè)定時(shí)間表和里程碑,按計(jì)劃執(zhí)行可以提高學(xué)習(xí)效率。
多樣化學(xué)習(xí)渠道:使用書(shū)籍、在線課程、視頻教學(xué)、博客文章、技術(shù)文檔等不同的學(xué)習(xí)資源可以幫助你全面理解新知識(shí)。
實(shí)踐操作:通過(guò)實(shí)際編程、設(shè)計(jì)、寫(xiě)作或其他實(shí)踐活動(dòng)將所學(xué)知識(shí)運(yùn)用到實(shí)際中去,實(shí)踐是檢驗(yàn)學(xué)習(xí)成果的最佳方式。
參與討論:加入學(xué)習(xí)小組或在線社區(qū),參與討論可以讓你從不同的角度理解新知識(shí),同時(shí),解答他人問(wèn)題也是一種很好的學(xué)習(xí)方式。
建立聯(lián)系:嘗試將新學(xué)的知識(shí)和你已經(jīng)知道的知識(shí)聯(lián)系起來(lái),這樣有助于記憶和深化理解。
定期復(fù)習(xí):周期性地檢查和復(fù)習(xí)你所學(xué)的內(nèi)容,避免遺忘,并確保知識(shí)點(diǎn)能夠牢固記在腦中。
靈活調(diào)整:在學(xué)習(xí)過(guò)程中,根據(jù)自己的學(xué)習(xí)進(jìn)展和理解情況,適當(dāng)調(diào)整學(xué)習(xí)目標(biāo)和計(jì)劃。
保持好奇心和耐心:學(xué)習(xí)新知識(shí)可能會(huì)遇到困難和挑戰(zhàn),保持開(kāi)放和有好奇心的態(tài)度是很重要的,耐心地對(duì)待每一步學(xué)習(xí)過(guò)程。
運(yùn)用現(xiàn)代技術(shù)輔助學(xué)習(xí):可以利用各種軟件和Apps來(lái)輔助學(xué)習(xí),比如使用筆記軟件整理學(xué)習(xí)筆記,用時(shí)間管理工具追蹤學(xué)習(xí)時(shí)間等。
????????
十八、最近看了什么書(shū)
? ? ? ? 這個(gè)就?由發(fā)揮了。
????????要回答好這個(gè)問(wèn)題,一定要保持看書(shū)學(xué)習(xí)的狀態(tài),否則肯定回答不好這個(gè)問(wèn)題,至少不能瞎編,多問(wèn)兩個(gè)問(wèn)題就被看穿。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-802337.html
? ? ? ? 所以,隨時(shí)保持學(xué)習(xí)充電狀態(tài)。一起加油!未來(lái)可期。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-802337.html
到了這里,關(guān)于Java入門高頻考查基礎(chǔ)知識(shí)4(字節(jié)跳動(dòng)面試題18題2.5萬(wàn)字參考答案)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!