复盘下华为大佬的技术拷问

  行情动荡不安,看着同组曾经一块并肩战斗过的开发同事们陆续被迫离职,我最近几个月也多少的思维活跃了些,有些惴惴不安,不知何时会轮到自己。想着即使暂时轮不到自己,也要有着被裁后能立马找到下家工作的能力,一直坐以待毙终究沦为案板上的羔羊。

  据说华为社招技术岗基本都是OD了,正式的很少很少,但是OD的面试流程及用人标准和正式是一样的,有不少难度,我也想试试,想试下华为大佬的技术盘问下我能抵挡几个回合,于是选了一个华为OD岗投递了下。

  去年8月份,参加过一次华为算法题机考,战败收尾,毕竟我不是985/211名校,及格线要比他们高很多很多;

  今年3月份,再次参加了一次,还好,过了;

  后来又参加了华为的性格测试、提交了所有的资面材料审核,等着HR帮我推华为招聘的项目组;

  4月份工作太忙了,加上五一放假的临近不想给自己太大压力,就和HR讲把推项目组的事情安排到了节后;

  后来5月16接到了来自于华为的HR的电话,简单沟通了半小时,随后她替我约了华为的面试官时间,技术一面放在5月18号周六上午10:00,技术二面放在了5月20号周一晚上7:30,完美避开了我的上班时间。两位面试官风格迥异,第一个笑呵呵的比较随和,问技术的同时掺杂着场景题,有来有往有交流,第二个比较高冷严肃,全场拷问技术,一问一答型,回答完后不做任何评价,接着下一题。至于我回答的怎么样,其实我也没底,有些问题确实是我的技术盲区,但我会的也全答上来了,庆幸的是,两位面试官出的现场编程题我全做上来了,每轮都进行了接近一小时左右。

  5月21号,HR告诉我两轮技术面通过了,接下来就是用人主管的综面了,综面过后就能发offer了,华为是我梦寐以求想要进的企业,是学生时代的梦想,此时感觉离他好近!   可另一个头疼的问题来了,用人部门是其他城市的,开始是想去那个城市发展的,但家里人还是不希望我走太远,我咨询了下可不可以入职后在北京的华为办公,答案是否定的,没辙,我不是一个人,我还有家人在北京上班,不再是那个初次步入社会闯荡的少年了,天南地北皆可去得,只能放弃了这份来之不易的工作,拒绝了综面。 感叹我毕业后来北京已经工作五年,第一份工作呆了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 块与 try 和 catch 块一起使用,以确保资源(如文件句柄、网络连接等)在异常发生时也能被正确释放。
    • 无论 try 块中的代码是否成功执行,或者 catch 块是否捕获到异常,finally 块中的代码都会被执行。
  3. 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 方法来唤醒。但是面试官还追问我有没有其他的区别,可见我回答的并不全面,后来也整理了一版,分享给大家:

  1. 所属类和方法签名:
    • 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))。
  2. 锁机制:
    • sleep 方法不会释放当前线程持有的任何锁。线程在调用 sleep 后会进入 TIMED_WAITING 状态,但会继续持有锁,直到睡眠时间结束。
    • wait 方法会释放当前线程持有的对象锁(即调用 wait 方法的对象上的锁)。线程在调用 wait 后会进入 WAITING 或 TIMED_WAITING 状态,并且不会持有锁,直到其他线程在同一对象上调用 notify 或 notifyAll 方法。
  3. 唤醒机制:
    • sleep 方法在指定的时间后自动唤醒,或者如果线程被中断,也会提前唤醒。
    • wait 方法只能被其他线程在同一对象上调用 notify 或 notifyAll 方法来唤醒。如果线程在等待时被中断,它也会提前唤醒,并抛出 InterruptedException
  4. 用途:
    • sleep 通常用于暂停线程的执行一段时间,或者用于实现简单的轮询或延迟。
    • wait 通常用于多线程之间的通信和同步,特别是在实现生产者-消费者模式或其他需要线程间协作的场景时。
  5. 异常处理:使用注意事项:
    • 如果线程在调用 sleep 时被中断,它会清除中断状态(即将中断状态重置为 false)并抛出 InterruptedException
    • 如果线程在调用 wait 时被中断,它也会抛出 InterruptedException,但不会清除中断状态。这意味着调用线程可以立即检查中断状态,并决定是继续等待还是处理中断。
    • sleep 可以在任何情况下调用,但 wait 必须在同步代码块或同步方法中调用,因为它依赖于对象锁。
    • 在调用 wait 或 notify 之前,最好总是先检查对象的锁状态,以避免潜在的并发问题。
    • wait 和 notify 应该与 synchronized 关键字一起使用,以确保线程安全。

 

  其余的回答的不好肯定也有,但是记不真切了,怪我没好好背八股文,像一些弱引用强引用的区别记着也没回答好,这没什么分享的价值;Spring AOP底层原理讲了一些没说很细;还有OOM的排查方案我分享了下工作中遇到时怎么排查处理的,但面试官好像想听我讲Linux命令版的排查;其余的也问了一些mysql、spring、并发编程、JVM、Kafka、SpringCould组件、缓存相关、软件设计的一些零零碎碎的知识点,感觉也都回答了七七八八吧。经过机考和两轮技术面,收获还是有的,看来我不能只关注技术的广度,也得静下心来好好看看Java语言本身的一些技术点和原理,还得注意定期复盘,否则曾经掌握过的知识点也会在时间的洪流中被冲刷干净。

 

posted @ 2024-05-31 17:57  帝莘  阅读(658)  评论(0编辑  收藏  举报