互联网大厂 Java 求职者面试场景
第一轮提问
面试官:王铁牛,首先说说 ArrayList 和 HashMap 的底层数据结构分别是什么? 王铁牛:ArrayList 底层是数组,HashMap 底层是数组 + 链表(JDK1.8 后引入红黑树优化)。 面试官:回答得不错。那 HashMap 在什么情况下链表会转化为红黑树? 王铁牛:好像是当链表长度达到 8 并且数组长度大于 64 的时候。 面试官:对的。那 Spring 中 Bean 的作用域有哪些? 王铁牛:有 singleton、prototype、request、session 等。
第二轮提问
面试官:好,那说说 JVM 的内存区域划分? 王铁牛:有堆、栈、方法区、程序计数器、本地方法栈。 面试官:不错。那多线程中,线程池的核心参数有哪些? 王铁牛:有 corePoolSize、maximumPoolSize、keepAliveTime 等。 面试官:那线程池是如何处理提交的任务的? 王铁牛:嗯……就是先看核心线程池满没满,满了就放队列里,队列满了再看最大线程池。
第三轮提问
面试官:那说说 MyBatis 的动态 SQL 是怎么实现的? 王铁牛:好像是通过一些标签,比如 if、where、foreach 等。 面试官:那 Dubbo 的服务注册与发现是怎么回事? 王铁牛:就是服务提供者把服务注册到注册中心,服务消费者从注册中心获取服务地址。 面试官:RabbitMQ 中的死信队列了解吗? 王铁牛:知道,就是消息变成死信后会被放到死信队列里,比如消息被拒绝、过期等情况。
面试官:好了,今天的面试就到这里,你回家等通知吧。
问题答案
- ArrayList 和 HashMap 的底层数据结构:ArrayList 底层基于数组实现,它可以动态扩容,通过维护一个 Object 类型的数组 elementData 来存储元素。HashMap 底层在 JDK1.8 之前是数组 + 链表,JDK1.8 及之后引入了红黑树优化,当链表长度达到 8 且数组长度大于 64 时,链表会转化为红黑树,这样在查找元素时时间复杂度从 O(n) 优化到 O(logn)。
- HashMap 链表转化为红黑树的条件:当链表长度达到 8 并且数组长度大于 64 的时候,会将链表转化为红黑树,目的是为了提高查询效率,因为链表的查询时间复杂度是 O(n),而红黑树的查询时间复杂度是 O(logn)。
- Spring 中 Bean 的作用域:
- singleton:单例模式,在 Spring IoC 容器中只会存在一个共享的 bean 实例,所有对该 bean 的请求都会返回这个唯一的实例。
- prototype:原型模式,每次对该 bean 的请求都会创建一个新的实例。
- request:在一次 HTTP 请求中,一个 bean 定义对应一个实例。
- session:在一个 HTTP Session 中,一个 bean 定义对应一个实例。
- JVM 的内存区域划分:
- 堆:是 JVM 中最大的一块内存区域,被所有线程共享,几乎所有的对象实例都在这里分配内存,是垃圾回收的主要区域。
- 栈:即虚拟机栈,每个线程都有一个私有的栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,它的生命周期与线程相同。
- 方法区:被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 程序计数器:是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,每个线程都有一个独立的程序计数器。
- 本地方法栈:与虚拟机栈的作用类似,只不过它是为虚拟机使用到的本地方法服务。
- 线程池的核心参数:
- corePoolSize:核心线程数,线程池创建后初始化的线程数量,即使这些线程空闲,也不会被销毁。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。
- keepAliveTime:线程空闲后的存活时间,当线程数大于 corePoolSize 时,多余的空闲线程会在 keepAliveTime 时间后被销毁。
- unit:keepAliveTime 的时间单位。
- workQueue:任务队列,用于保存等待执行的任务。
- threadFactory:线程工厂,用于创建线程。
- handler:拒绝策略,当任务队列和最大线程数都满了之后,对新任务的处理策略。
- 线程池处理提交任务的流程:当提交一个任务到线程池时,首先会判断核心线程池是否已满,如果未满,则创建一个新的核心线程来执行任务;如果核心线程池已满,则将任务放入任务队列中;如果任务队列也已满,再判断最大线程池是否已满,如果未满,则创建一个非核心线程来执行任务;如果最大线程池也已满,则根据拒绝策略来处理该任务。
- MyBatis 的动态 SQL 实现:MyBatis 通过提供一系列的动态 SQL 标签来实现动态 SQL,比如
<if>标签用于条件判断,<where>标签会自动处理 SQL 语句中的第一个AND或OR,<foreach>标签用于循环遍历集合等。通过这些标签,可以根据不同的条件动态生成 SQL 语句,提高 SQL 的复用性和灵活性。 - Dubbo 的服务注册与发现:Dubbo 采用注册中心来实现服务的注册与发现。服务提供者启动时,会将自己的服务信息注册到注册中心(如 Zookeeper 等);服务消费者启动时,会从注册中心获取服务提供者的地址信息,并根据一定的负载均衡策略选择一个服务提供者进行调用。这样就实现了服务的动态发现和调用。
- RabbitMQ 中的死信队列:死信队列是一种特殊的队列,当消息变成死信(Dead - Letter)时,会被发送到死信队列。消息变成死信的情况有:消息被消费者拒绝(basic.reject 或 basic.nack 且 requeue = false)、消息过期、队列达到最大长度等。死信队列可以用于处理异常情况的消息,或者实现延迟队列等功能。