《互联网大厂Java面试:核心知识大考验》

57 阅读10分钟

互联网大厂Java面试:核心知识大考验

在一间明亮却透着紧张氛围的面试房间里,一场决定求职者命运的Java面试正在进行。面试官表情严肃,而求职者王铁牛表面镇定,内心实则忐忑不安。

面试官:“第一轮开始,先问几个基础问题。第一个,Java中ArrayList和HashMap的底层数据结构分别是什么?” 王铁牛:“ArrayList底层是数组,HashMap底层是数组加链表,JDK1.8之后引入了红黑树。” 面试官:“回答不错。第二个问题,HashMap在什么情况下会发生扩容?” 王铁牛:“当HashMap中的元素个数达到负载因子(默认0.75)乘以当前容量的时候,就会发生扩容。” 面试官:“很好。那Spring框架中IOC和AOP分别是什么?” 王铁牛:“IOC是控制反转,把对象创建和对象之间的依赖关系交给Spring容器管理。AOP是面向切面编程,在不修改原有代码的基础上,对方法进行增强。” 面试官:“第一轮回答得很不错,基础还是比较扎实的。接下来第二轮。多线程中线程池的核心参数有哪些,分别有什么作用?” 王铁牛:“有核心线程数、最大线程数、存活时间、时间单位,还有任务队列。核心线程数就是一直存活的线程数,最大线程数是线程池能容纳的最大线程数,存活时间是多余线程在空闲时的存活时长,时间单位就是存活时间的单位,任务队列就是存放任务的地方。” 面试官:“还行。那JVM的垃圾回收算法有哪些?” 王铁牛:“嗯……有标记 - 清除算法,标记存活对象,然后清除未标记的。还有复制算法,把内存分成两块,每次只用一块,用完了把存活对象复制到另一块。还有标记 - 整理算法,标记存活对象,然后把存活对象往一端移动,清除边界外的内存。” 面试官:“勉强可以。那Spring Boot自动配置原理是什么?” 王铁牛:“就是它会根据类路径下的依赖,自动配置一些Bean,好像是通过一些配置类和条件注解实现的。” 面试官:“回答得不太清晰。进入第三轮。Dubbo的服务调用流程是怎样的?” 王铁牛:“嗯……就是服务提供者把服务注册到注册中心,服务消费者从注册中心获取服务列表,然后调用服务,大概是这样。” 面试官:“那RabbitMQ的消息确认机制是怎么回事?” 王铁牛:“就是消息发送出去后,会有确认,确保消息被正确接收,好像有生产者确认和消费者确认。” 面试官:“最后一个问题,xxl - job的调度原理是什么?” 王铁牛:“这个……它好像是有个调度中心,然后去调度任务,具体不太清楚。” 面试官:“王铁牛,整体来看,你对一些基础知识点掌握得还可以,但对于一些稍微深入的问题,回答得不是很理想。今天的面试就到这里,你回去等通知吧。我们会综合评估所有候选人后,再做决定。感谢你今天来参加面试。”

问题答案

  1. ArrayList和HashMap的底层数据结构
    • ArrayList:底层是数组结构。数组可以快速地通过索引访问元素,适合随机访问场景。但在插入和删除元素时,尤其是在数组中间位置操作,需要移动大量元素,时间复杂度较高。
    • HashMap:JDK1.8之前,底层是数组加链表。数组的每个位置是一个链表头,通过哈希算法计算键的哈希值,确定元素在数组中的位置。如果发生哈希冲突(不同键的哈希值相同),则以链表形式存储在该位置。JDK1.8之后,当链表长度大于8且数组容量大于64时,链表会转化为红黑树,以提高查找效率。红黑树是一种自平衡的二叉查找树,查找、插入和删除操作的时间复杂度平均为O(log n)。
  2. HashMap扩容机制:HashMap有两个重要参数,容量(capacity)和负载因子(loadFactor)。默认初始容量为16,负载因子默认是0.75。当HashMap中的元素个数(size)达到负载因子乘以当前容量(即size >= capacity * loadFactor)时,就会触发扩容。扩容时,会创建一个新的数组,容量为原来的2倍,然后将旧数组中的元素重新计算哈希值,放入新数组中。这是因为扩容后数组容量变化,哈希值对应的数组位置可能改变。
  3. Spring框架中IOC和AOP
    • IOC(控制反转):传统编程中,对象的创建和依赖关系由程序自身控制。而在Spring中,IOC将对象的创建和依赖关系的管理交给Spring容器。例如,一个Service类依赖另一个Dao类,在IOC模式下,Spring容器负责创建Dao对象,并注入到Service类中,而不是由Service类自己去创建Dao对象。这样可以降低代码的耦合度,提高可维护性和可测试性。
    • AOP(面向切面编程):它是对OOP(面向对象编程)的补充。OOP主要关注业务模块的划分,而AOP关注的是那些横跨多个业务模块的功能,如日志记录、事务管理、权限控制等。这些功能被称为切面(Aspect)。AOP通过动态代理等技术,在不修改原有业务代码的基础上,在方法执行前、执行后或异常时等位置织入切面逻辑,实现对业务方法的增强。
  4. 线程池核心参数
    • 核心线程数(corePoolSize):线程池中一直存活的线程数,即使这些线程处于空闲状态,也不会被销毁。当有新任务提交时,如果线程池中的线程数小于核心线程数,会优先创建新线程来处理任务。
    • 最大线程数(maximumPoolSize):线程池能容纳的最大线程数。当任务队列已满,且线程池中的线程数小于最大线程数时,会继续创建新线程来处理任务。但如果线程数达到最大线程数,且任务队列也已满,新任务会根据拒绝策略进行处理。
    • 存活时间(keepAliveTime):当线程池中的线程数大于核心线程数时,多余的空闲线程在存活时间内没有接到新任务,就会被销毁。这个参数可以控制线程池在任务量减少时,自动释放多余的线程资源。
    • 时间单位(unit):存活时间的单位,如TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)等,用于指定存活时间的度量单位。
    • 任务队列(workQueue):用于存放等待处理的任务。常见的任务队列有ArrayBlockingQueue(有界数组队列)、LinkedBlockingQueue(无界链表队列)、SynchronousQueue(同步队列)等。不同的任务队列特性不同,会影响线程池的性能和行为。
  5. JVM垃圾回收算法
    • 标记 - 清除算法:分为两个阶段,标记阶段和清除阶段。在标记阶段,垃圾回收器从根对象(如栈中的引用、静态变量等)开始遍历,标记所有存活的对象。然后在清除阶段,清除所有未被标记的对象。这种算法的缺点是会产生大量不连续的内存碎片,可能导致后续大对象无法分配内存。
    • 复制算法:将内存空间分为两块,每次只使用其中一块。当这块内存使用完后,将存活的对象复制到另一块空闲内存中,然后清除原来使用的那块内存。这种算法不会产生内存碎片,且复制过程中可以对对象进行整理,提高内存访问的局部性。但它的缺点是需要两倍的内存空间,并且在对象存活率较高时,复制操作会消耗大量性能。
    • 标记 - 整理算法:同样先进行标记阶段,标记存活对象。然后在整理阶段,将存活对象向内存的一端移动,最后清除边界外的内存。这种算法结合了标记 - 清除算法和复制算法的优点,既避免了内存碎片问题,又不需要额外的两倍内存空间。适用于对象存活率较高的场景。
  6. Spring Boot自动配置原理:Spring Boot通过大量的自动配置类(以AutoConfiguration结尾)实现自动配置。这些自动配置类会根据类路径下是否存在某些特定的依赖,以及一些配置属性(通过@ConfigurationProperties注解绑定)来决定是否生效。例如,当项目中引入了spring - boot - starter - jdbc依赖时,DataSourceAutoConfiguration类会生效,自动配置数据源。同时,Spring Boot使用了条件注解(如@ConditionalOnClass、@ConditionalOnProperty等),只有满足特定条件时,对应的自动配置类才会被加载和应用。这样就实现了根据项目依赖和配置自动配置Spring应用的各种组件。
  7. Dubbo服务调用流程
    • 服务注册:服务提供者启动时,将自己提供的服务注册到注册中心(如Zookeeper)。注册中心维护了服务提供者的地址、端口、服务接口等信息。
    • 服务订阅:服务消费者启动时,从注册中心订阅自己需要的服务。注册中心会将服务提供者的信息推送给服务消费者。
    • 服务调用:服务消费者根据从注册中心获取的服务提供者信息,通过网络通信(如Netty)调用服务提供者的接口。在调用过程中,Dubbo还支持负载均衡(如随机、轮询等策略),从多个服务提供者中选择一个进行调用。
  8. RabbitMQ消息确认机制
    • 生产者确认(Publisher Confirm):生产者将消息发送到RabbitMQ服务器后,服务器会返回一个确认消息给生产者,告知消息是否成功接收。生产者可以通过设置confirmCallback回调函数来处理这些确认消息。如果消息成功接收,回调函数会被触发并传递相关确认信息;如果消息接收失败,回调函数也会被触发并传递失败原因。这样生产者可以知道消息是否成功发送,以便进行相应的处理,如重试发送。
    • 消费者确认(Consumer Ack):消费者从RabbitMQ服务器获取消息并处理完成后,需要向服务器发送一个确认消息(Ack),告知服务器该消息已被成功处理。服务器在收到消费者的确认消息后,才会将该消息从队列中删除。如果消费者在处理消息过程中出现异常,没有发送确认消息,服务器会认为该消息处理失败,可能会将消息重新放入队列,以便其他消费者重新处理。这种机制确保了消息不会因为消费者处理异常而丢失。
  9. xxl - job调度原理:xxl - job有一个调度中心,它负责管理和调度任务。调度中心会按照配置的调度规则(如定时任务的cron表达式),在指定时间触发任务调度。当调度中心触发任务时,会向注册到它的执行器发送任务执行请求。执行器接收到请求后,会启动一个线程来执行具体的任务逻辑。任务执行完成后,执行器会将任务执行结果返回给调度中心。调度中心可以记录任务的执行状态、执行时间等信息,方便用户进行监控和管理。同时,执行器可以注册多个,实现分布式任务执行,提高任务处理能力。