主動放棄了華為的offer,等會兒再遺憾,先覆盤下華為大佬的技術拷問

帝莘發表於2024-05-31

  行情動盪不安,看著同組曾經一塊並肩戰鬥過的開發同事們陸續被迫離職,我最近幾個月也多少的思維活躍了些,有些惴惴不安,不知何時會輪到自己。想著即使暫時輪不到自己,也要有著被裁後能立馬找到下家工作的能力,一直坐以待斃終究淪為案板上的羔羊。

  據說華為社招技術崗基本都是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)工作流程

  • 事件釋出:事件源透過ApplicationEventPublisherpublishEvent方法釋出事件。
  • 事件監聽:事件監聽器透過實現ApplicationListener介面並註冊到ApplicationContext中,從而能夠監聽到感興趣的事件。
  • 事件處理:當事件釋出後,ApplicationContext會找到所有訂閱了該事件的監聽器,並呼叫它們的onApplicationEvent方法進行處理。

(4)使用方式

  • 定義自定義事件:透過繼承ApplicationEvent類來定義自定義事件,可以新增與事件相關的資料和方法。
  • 建立事件監聽器:建立一個實現了ApplicationListener介面的類,並指定需要監聽的事件型別。在onApplicationEvent方法中編寫事件處理邏輯。
  • 釋出事件:在需要釋出事件的地方,透過ApplicationEventPublisherpublishEvent方法釋出自定義事件。

(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不清楚了。

  1. final
    • final 是一個修飾符,它可以用來修飾類、方法和變數。
    • final 修飾一個類時,這個類不能被繼承。
    • final 修飾一個方法時,這個方法不能被重寫(在子類中)。
    • final 修飾一個變數時,這個變數的值不能被改變(常量)。對於基本型別,值不可變;對於引用型別,引用本身不可變,但引用的物件內部狀態可以改變。
  2. finally
    • finally 是一個異常處理塊的關鍵字,用於定義在所有情況下都必須執行的程式碼,無論是否丟擲或捕獲到異常。
    • 通常,finally 塊與 trycatch 塊一起使用,以確保資源(如檔案控制代碼、網路連線等)在異常發生時也能被正確釋放。
    • 無論 try 塊中的程式碼是否成功執行,或者 catch 塊是否捕獲到異常,finally 塊中的程式碼都會被執行。
  3. finalize
    • finalizeObject 類的一個方法,它的設計初衷是在垃圾收集器準備釋放物件佔用的記憶體之前,被垃圾收集器呼叫,從而允許物件執行一些清理工作。
    • 但是,由於 finalize 方法的執行時間是不確定的,並且其執行可能會受到 JVM 實現的影響,因此在現代 Java 程式設計中,通常不建議依賴 finalize 方法進行資源管理或執行其他重要的清理工作。
    • 相反,應該使用 try-with-resources 語句(Java 7 引入)或顯式的 close 方法(如果物件實現了 AutoCloseableCloseable 介面)來管理資源。

總結:

  • final 用於宣告常量、不可繼承的類和不可重寫的方法。
  • finally 用於定義無論是否發生異常都必須執行的程式碼塊。
  • finalizeObject 類的一個方法,用於在物件被垃圾收集之前執行清理工作,但通常不建議使用它。

3.執行緒的sleep和物件的wait方法都能讓執行緒等待,有什麼區別呢?

這個問題我當時回答了一些sleep 方法在指定的時間後自動喚醒,wait 方法只能被其他執行緒在同一物件上呼叫 notifynotifyAll 方法來喚醒。但是面試官還追問我有沒有其他的區別,可見我回答的並不全面,後來也整理了一版,分享給大家:

  1. 所屬類和方法簽名:
    • sleepThread 類的一個靜態方法,因此可以在任何執行緒中呼叫。其方法簽名是 public static void sleep(long millis) throws InterruptedException
    • waitObject 類的一個方法,因此所有物件都可以呼叫它。其方法簽名是 public final void wait() throws InterruptedException(還有其他過載版本,如 wait(long timeout)wait(long timeout, int nanos))。
  2. 鎖機制:
    • sleep 方法不會釋放當前執行緒持有的任何鎖。執行緒在呼叫 sleep 後會進入 TIMED_WAITING 狀態,但會繼續持有鎖,直到睡眠時間結束。
    • wait 方法會釋放當前執行緒持有的物件鎖(即呼叫 wait 方法的物件上的鎖)。執行緒在呼叫 wait 後會進入 WAITING 或 TIMED_WAITING 狀態,並且不會持有鎖,直到其他執行緒在同一物件上呼叫 notifynotifyAll 方法。
  3. 喚醒機制:
    • sleep 方法在指定的時間後自動喚醒,或者如果執行緒被中斷,也會提前喚醒。
    • wait 方法只能被其他執行緒在同一物件上呼叫 notifynotifyAll 方法來喚醒。如果執行緒在等待時被中斷,它也會提前喚醒,並丟擲 InterruptedException
  4. 用途:
    • sleep 通常用於暫停執行緒的執行一段時間,或者用於實現簡單的輪詢或延遲。
    • wait 通常用於多執行緒之間的通訊和同步,特別是在實現生產者-消費者模式或其他需要執行緒間協作的場景時。
  5. 異常處理:使用注意事項:
    • 如果執行緒在呼叫 sleep 時被中斷,它會清除中斷狀態(即將中斷狀態重置為 false)並丟擲 InterruptedException
    • 如果執行緒在呼叫 wait 時被中斷,它也會丟擲 InterruptedException,但不會清除中斷狀態。這意味著呼叫執行緒可以立即檢查中斷狀態,並決定是繼續等待還是處理中斷。
    • sleep 可以在任何情況下呼叫,但 wait 必須在同步程式碼塊或同步方法中呼叫,因為它依賴於物件鎖。
    • 在呼叫 waitnotify 之前,最好總是先檢查物件的鎖狀態,以避免潛在的併發問題。
    • waitnotify 應該與 synchronized 關鍵字一起使用,以確保執行緒安全。

  其餘的回答的不好肯定也有,但是記不真切了,怪我沒好好背八股文,像一些弱引用強引用的區別記著也沒回答好,這沒什麼分享的價值;Spring AOP底層原理講了一些沒說很細;還有OOM的排查方案我分享了下工作中遇到時怎麼排查處理的,但面試官好像想聽我講Linux命令版的排查;其餘的也問了一些mysql、spring、併發程式設計、JVM、Kafka、SpringCould元件、快取相關、軟體設計的一些零零碎碎的知識點,感覺也都回答了七七八八吧。經過機考和兩輪技術面,收穫還是有的,看來我不能只關注技術的廣度,也得靜下心來好好看看Java語言本身的一些技術點和原理,還得注意定期覆盤,否則曾經掌握過的知識點也會在時間的洪流中被沖刷乾淨。

相關文章