行情動盪不安,看著同組曾經一塊並肩戰鬥過的開發同事們陸續被迫離職,我最近幾個月也多少的思維活躍了些,有些惴惴不安,不知何時會輪到自己。想著即使暫時輪不到自己,也要有著被裁後能立馬找到下家工作的能力,一直坐以待斃終究淪為案板上的羔羊。
據說華為社招技術崗基本都是OD了,正式的很少很少,但是OD的面試流程及用人標準和正式是一樣的,有不少難度,我也想試試,想試下華為大佬的技術盤問下我能抵擋幾個回合,於是隨機在Boss上選了一個華為OD崗投遞了下。
去年8月份,參加過一次華為演算法題機考,戰敗收尾,畢竟我不是985/211名校,及格線要比他們高很多很多;
今年3月份,再次參加了一次,還好,過了;
後來又參加了華為的性格測試、提交了所有的資面材料稽核,等著HR幫我推華為招聘的專案組;
4月份工作太忙了,加上五一放假的臨近不想給自己太大壓力,就和HR講把推專案組的事情安排到了節後;
後來5月16接到了來自於華為的HR的電話,簡單溝通了半小時,隨後她替我約了華為的面試官時間,技術一面放在5月18號週六上午10:00,技術二面放在了5月20號週一晚上7:30,完美避開了我的上班時間。兩位面試官風格迥異,第一個笑呵呵的比較隨和,問技術的同時摻雜著場景題,有來有往有交流,第二個比較高冷嚴肅,全場拷問技術,一問一答型,回答完後不做任何評價,接著下一題。至於我回答的怎麼樣,其實我也沒底,有些問題確實是我的技術盲區,但我會的也全答上來了,慶幸的是,兩位面試官出的現場程式設計題我全做上來了,每輪都進行了接近一小時左右。
5月21號,HR告訴我兩輪技術面透過了,接下來就是用人主管的綜面了,綜面一般問題都不大,過後就發offer了,從技術面定級的情況下每個月給到 xx K,每年大概是14-16薪的樣子,我粗略估算了下,綜合年收入能比我現在多10W左右,媽呀,這種破行情下還能讓我有這樣的好機遇,華為待遇方面果然大氣! 可另一個頭疼的問題來了,用人部門是其他城市的,開始是想去那個城市發展的,但家裡人還是不希望我走太遠,我諮詢了下可不可以入職後在北京的華為辦公,答案是否定的,沒轍,我不是一個人,我還有家人在北京上班,不再是那個初次步入社會闖蕩的少年了,天南地北皆可去得,只能放棄了這份來之不易的工作,拒絕了綜面。 感嘆我畢業後來北京已經工作五年,第一份工作呆了2年,第二份工作呆了3年了,我是一個求穩定的人,若不是公司各種資金吃緊,大面積的裁人,換調專案組導致晉升渺茫,停止調薪,我是萬不會萌發出去看看的想法,但是人總歸給自己個退路麼,等有一天裁員這把大刀真的架在了我的脖子上,又當如何?
廢話說了那麼多,開始迴歸主題了,我覆盤了下在技術面時答得不是很好的幾個問題,也接受大家的批評與指正:
1.你知道Spring有哪些釋出訊息、監聽訊息的元件麼?
初看這問題我以為是想問我訊息中介軟體,我回答了Kafka訊息釋出監聽,非同步解耦的知識點,其實不然,這裡面試官想考察我對Spring的事件機制瞭解多少,Spring事件機制名詞隱約在哪聽過,但工作中卻從未用過,算是被難到了,後來面試官也大致給我解釋了在Spring中這是個很好用的元件,使用觀察者模式啥的,能夠降低模組之間的耦合度,像一些發簡訊,發郵件的功能可以透過這種監聽機制自動去處理,和業務程式碼隔離開,不是透過呼叫方法的形式。後來我也大致去網上搜了下,大致整理了一些知識點:
(1)概述
Spring的事件機制是Spring框架中的一個重要特性,它基於觀察者模式實現,允許應用程式中的元件之間進行松耦合的通訊。這種機制允許開發者在應用程式中定義、釋出和監聽事件,從而實現模組間的解耦,提高程式碼的可維護性和可擴充套件性。
(2)核心元件與概念
- 事件(Event):表示應用程式中的某個動作或狀態的發生,通常透過繼承
ApplicationEvent
類來定義自定義事件。 - 事件源(Event Source):負責釋出事件的物件,通常使用
ApplicationEventPublisher
介面的實現類來發布事件。 - 事件監聽器(Event Listener):負責監聽事件並執行相應操作的物件,需要實現
ApplicationListener
介面。 也可以在方法上新增@EventListener註解實現。 - 事件廣播器(Event Broadcaster):在Spring中,這個角色通常由
ApplicationContext
擔任,它實現了ApplicationEventPublisher
介面,負責管理事件的釋出和監聽。
(3)工作流程
- 事件釋出:事件源透過
ApplicationEventPublisher
的publishEvent
方法釋出事件。 - 事件監聽:事件監聽器透過實現
ApplicationListener
介面並註冊到ApplicationContext
中,從而能夠監聽到感興趣的事件。 - 事件處理:當事件釋出後,
ApplicationContext
會找到所有訂閱了該事件的監聽器,並呼叫它們的onApplicationEvent
方法進行處理。
(4)使用方式
- 定義自定義事件:透過繼承
ApplicationEvent
類來定義自定義事件,可以新增與事件相關的資料和方法。 - 建立事件監聽器:建立一個實現了
ApplicationListener
介面的類,並指定需要監聽的事件型別。在onApplicationEvent
方法中編寫事件處理邏輯。 - 釋出事件:在需要釋出事件的地方,透過
ApplicationEventPublisher
的publishEvent
方法釋出自定義事件。
(5)優點
- 解耦:透過事件機制,可以實現模組間的解耦,使程式碼更加清晰、易於維護。
- 可擴充套件性:可以方便地新增新的事件和監聽器,以滿足不同的業務需求。
- 靈活性:事件監聽器可以處理多個事件,也可以只處理特定型別的事件。
(6)自己寫的簡單的Demo程式碼
// 定義一個事件類 import lombok.Getter; import org.springframework.context.ApplicationEvent; @Getter public class CustomEvent extends ApplicationEvent { private String phone; private String email; public CustomEvent(Object source) { super(source); } public CustomEvent(String phone,String email,Object source){ super(source); this.email = email; this.phone = phone; } } // 定義一個事件釋出器 import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; @Component public class CustomPublisher implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void publish(String phone,String email) { CustomEvent event = new CustomEvent(phone,email,"Event Publish"); publisher.publishEvent(event); } } import org.springframework.context.ApplicationListener; /** * / 定義一個事件監聽器 - 發簡訊 */ public class CustomListenerOne implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent customEvent) { System.out.println("I am ListenerOne,I need send message to phone:"+customEvent.getPhone()); } } import org.springframework.context.ApplicationListener; /** * 定義一個事件監聽器 發郵件 */ public class CustomListenerTwo implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent customEvent) { System.out.println("I am ListenerTwo,I need send email to "+customEvent.getEmail()); } } import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * 註解方式實現的事件監聽器 */ @Component public class CustomListenerThree { @EventListener public void onApplicationEvent(CustomEvent customEvent) { System.out.println("註解方案:I am ListenerTwo,I need send email to "+customEvent.getEmail()); } @EventListener public void onApplicationEvent2(CustomEvent customEvent) { System.out.println("註解方案:I am ListenerOne,I need send message to phone:"+customEvent.getPhone()); } } import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** 測試啟動類,註冊各個元件 */ public class MainEvent { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 註冊事件監聽器 context.addApplicationListener(new CustomListenerOne()); context.addApplicationListener(new CustomListenerTwo()); // 註冊釋出器的bean context.register(CustomPublisher.class); context.refresh(); // 獲取釋出器併發布事件 CustomPublisher publisher = context.getBean(CustomPublisher.class); publisher.publish("188****865","aaa_dduuuu@1663.com"); context.close(); } }
2.介紹下final、finally、finalize的特性與區別。
final和finally我比較熟悉,當時全都回答上來了,但是finalize我確實之前沒有深入瞭解和使用過,直接和麵試官說finalize不清楚了。
- final
final
是一個修飾符,它可以用來修飾類、方法和變數。- 當
final
修飾一個類時,這個類不能被繼承。 - 當
final
修飾一個方法時,這個方法不能被重寫(在子類中)。 - 當
final
修飾一個變數時,這個變數的值不能被改變(常量)。對於基本型別,值不可變;對於引用型別,引用本身不可變,但引用的物件內部狀態可以改變。
- finally
finally
是一個異常處理塊的關鍵字,用於定義在所有情況下都必須執行的程式碼,無論是否丟擲或捕獲到異常。- 通常,
finally
塊與try
和catch
塊一起使用,以確保資源(如檔案控制代碼、網路連線等)在異常發生時也能被正確釋放。 - 無論
try
塊中的程式碼是否成功執行,或者catch
塊是否捕獲到異常,finally
塊中的程式碼都會被執行。
- finalize
finalize
是Object
類的一個方法,它的設計初衷是在垃圾收集器準備釋放物件佔用的記憶體之前,被垃圾收集器呼叫,從而允許物件執行一些清理工作。- 但是,由於
finalize
方法的執行時間是不確定的,並且其執行可能會受到 JVM 實現的影響,因此在現代 Java 程式設計中,通常不建議依賴finalize
方法進行資源管理或執行其他重要的清理工作。 - 相反,應該使用
try-with-resources
語句(Java 7 引入)或顯式的close
方法(如果物件實現了AutoCloseable
或Closeable
介面)來管理資源。
總結:
final
用於宣告常量、不可繼承的類和不可重寫的方法。finally
用於定義無論是否發生異常都必須執行的程式碼塊。finalize
是Object
類的一個方法,用於在物件被垃圾收集之前執行清理工作,但通常不建議使用它。
3.執行緒的sleep和物件的wait方法都能讓執行緒等待,有什麼區別呢?
這個問題我當時回答了一些sleep
方法在指定的時間後自動喚醒,wait
方法只能被其他執行緒在同一物件上呼叫 notify
或 notifyAll
方法來喚醒。但是面試官還追問我有沒有其他的區別,可見我回答的並不全面,後來也整理了一版,分享給大家:
- 所屬類和方法簽名:
sleep
是Thread
類的一個靜態方法,因此可以在任何執行緒中呼叫。其方法簽名是public static void sleep(long millis) throws InterruptedException
。wait
是Object
類的一個方法,因此所有物件都可以呼叫它。其方法簽名是public final void wait() throws InterruptedException
(還有其他過載版本,如wait(long timeout)
和wait(long timeout, int nanos)
)。
- 鎖機制:
sleep
方法不會釋放當前執行緒持有的任何鎖。執行緒在呼叫sleep
後會進入 TIMED_WAITING 狀態,但會繼續持有鎖,直到睡眠時間結束。wait
方法會釋放當前執行緒持有的物件鎖(即呼叫wait
方法的物件上的鎖)。執行緒在呼叫wait
後會進入 WAITING 或 TIMED_WAITING 狀態,並且不會持有鎖,直到其他執行緒在同一物件上呼叫notify
或notifyAll
方法。
- 喚醒機制:
sleep
方法在指定的時間後自動喚醒,或者如果執行緒被中斷,也會提前喚醒。wait
方法只能被其他執行緒在同一物件上呼叫notify
或notifyAll
方法來喚醒。如果執行緒在等待時被中斷,它也會提前喚醒,並丟擲InterruptedException
。
- 用途:
sleep
通常用於暫停執行緒的執行一段時間,或者用於實現簡單的輪詢或延遲。wait
通常用於多執行緒之間的通訊和同步,特別是在實現生產者-消費者模式或其他需要執行緒間協作的場景時。
- 異常處理:使用注意事項:
- 如果執行緒在呼叫
sleep
時被中斷,它會清除中斷狀態(即將中斷狀態重置為false
)並丟擲InterruptedException
。 - 如果執行緒在呼叫
wait
時被中斷,它也會丟擲InterruptedException
,但不會清除中斷狀態。這意味著呼叫執行緒可以立即檢查中斷狀態,並決定是繼續等待還是處理中斷。
- 如果執行緒在呼叫
-
sleep
可以在任何情況下呼叫,但wait
必須在同步程式碼塊或同步方法中呼叫,因為它依賴於物件鎖。- 在呼叫
wait
或notify
之前,最好總是先檢查物件的鎖狀態,以避免潛在的併發問題。 wait
和notify
應該與synchronized
關鍵字一起使用,以確保執行緒安全。
其餘的回答的不好肯定也有,但是記不真切了,怪我沒好好背八股文,像一些弱引用強引用的區別記著也沒回答好,這沒什麼分享的價值;Spring AOP底層原理講了一些沒說很細;還有OOM的排查方案我分享了下工作中遇到時怎麼排查處理的,但面試官好像想聽我講Linux命令版的排查;其餘的也問了一些mysql、spring、併發程式設計、JVM、Kafka、SpringCould元件、快取相關、軟體設計的一些零零碎碎的知識點,感覺也都回答了七七八八吧。經過機考和兩輪技術面,收穫還是有的,看來我不能只關注技術的廣度,也得靜下心來好好看看Java語言本身的一些技術點和原理,還得注意定期覆盤,否則曾經掌握過的知識點也會在時間的洪流中被沖刷乾淨。