《揭秘互联网大厂 Java 面试:从基础到进阶的核心考点大挑战》

50 阅读9分钟

在一间明亮却略显严肃的面试房间里,一位求职者正襟危坐,对面的面试官表情沉稳,一场决定命运的互联网大厂Java面试即将拉开帷幕。

面试官:“第一轮提问开始。先说说Java中ArrayList和HashMap的底层数据结构分别是什么?” 求职者:“ArrayList底层是数组,HashMap底层是数组加链表,JDK1.8后引入了红黑树。” 面试官:“回答得不错。那ArrayList在扩容时具体是怎么操作的?” 求职者:“当元素个数达到容量阈值时,会创建一个新的更大的数组,然后将原数组内容复制过去。” 面试官:“很好。HashMap在什么情况下会发生哈希冲突,又是如何解决的?” 求职者:“当不同的键计算出相同的哈希值时会发生哈希冲突,通过链地址法解决,JDK1.8后链表长度大于8且数组长度大于64时会转化为红黑树。”

面试官:“第二轮提问。谈谈Spring框架中IOC和AOP的概念及作用。” 求职者:“IOC是控制反转,把对象创建和管理交给Spring容器,AOP是面向切面编程,能在不修改原有代码基础上增加功能。” 面试官:“那Spring Boot相较于Spring,最大的优势是什么?” 求职者:“Spring Boot简化了Spring应用的搭建和开发,自动配置很多东西,开箱即用。” 面试官:“MyBatis中#{}和{}有什么区别?” **求职者**:“#{}是预编译处理,{}是字符串替换,#{}能防止SQL注入。”

面试官:“第三轮提问。Dubbo在分布式系统中有哪些核心功能?” 求职者:“嗯……好像是服务治理,还有……还有负载均衡吧。” 面试官:“RabbitMQ的消息确认机制是怎样的?” 求职者:“就是……消息发出去,然后……对方收到确认。” 面试官:“xxl - job在分布式任务调度中有什么特点?” 求职者:“它……它能调度任务,好像还挺简单易用的。”

面试官微微皱眉,思索片刻后说道:“今天的面试就到这里,你回去等通知吧。我们会综合评估各位候选人的情况,有结果会尽快联系你。感谢你今天来参加面试。”

答案

  1. ArrayList和HashMap底层数据结构
    • ArrayList:底层是数组结构,它允许以数组下标的方式快速访问元素。例如list.get(0)可以直接获取第一个元素。数组的特点是连续内存存储,所以查询效率高,但插入和删除非尾部元素时,需要移动大量元素,效率较低。
    • HashMap:JDK1.8之前底层是数组加链表结构,数组的每个位置是一个链表头节点。当发生哈希冲突时,新的键值对会以链表节点的形式插入到对应数组位置的链表中。JDK1.8后,当链表长度大于8且数组长度大于64时,链表会转化为红黑树,以提高查询效率。红黑树是一种自平衡的二叉查找树,它保证了在最坏情况下,查询、插入和删除操作的时间复杂度为O(log n)。
  2. ArrayList扩容操作
    • ArrayList有一个容量(capacity)和实际元素个数(size)。当size达到capacity时,会触发扩容。扩容时,新的容量是原容量的1.5倍(newCapacity = oldCapacity + (oldCapacity >> 1))。然后创建一个新的更大的数组,通过System.arraycopy方法将原数组内容复制到新数组。例如,原数组容量为10,当添加第11个元素时,会扩容到15。
  3. HashMap哈希冲突及解决
    • 哈希冲突:由于哈希函数的局限性,不同的键可能计算出相同的哈希值,这就产生了哈希冲突。例如,两个不同的字符串“abc”和“cba”,经过哈希函数计算可能得到相同的哈希值。
    • 解决方法:采用链地址法,即数组的每个位置存储一个链表。当发生哈希冲突时,新的键值对以链表节点的形式插入到对应数组位置的链表中。在JDK1.8后,为了优化哈希冲突时的查询性能,当链表长度大于8且数组长度大于64时,链表会转化为红黑树。
  4. Spring中IOC和AOP
    • IOC(控制反转):传统开发中,对象的创建和管理由应用程序自身负责,而在Spring框架中,IOC将对象的创建和管理交给Spring容器。例如,在一个业务逻辑类中,如果需要依赖另一个类的实例,传统方式是在业务逻辑类中自己new出依赖类的实例。而使用IOC,Spring容器会创建并管理这些依赖对象,业务逻辑类只需要声明依赖,由Spring容器注入。这样可以降低组件之间的耦合度,提高代码的可维护性和可测试性。
    • AOP(面向切面编程):AOP是将一些与业务逻辑无关但又贯穿于多个业务模块的功能(如日志记录、事务管理、权限控制等)抽取出来,形成一个独立的切面。这些切面可以在不修改原有业务代码的基础上,在特定的切入点(如方法调用前后)织入到业务逻辑中。例如,在一个电商系统中,订单处理方法前后可能需要记录日志,使用AOP可以将日志记录功能封装成切面,在订单处理方法执行前后自动执行日志记录逻辑。
  5. Spring Boot相较于Spring的优势
    • 简化配置:Spring Boot采用自动配置机制,很多Spring框架中繁琐的配置都可以省略。例如,在Spring中配置数据库连接需要编写大量的XML配置或者Java配置类,而在Spring Boot中,只需要在application.propertiesapplication.yml文件中简单配置数据库连接信息,Spring Boot就能自动配置好相关的数据源、JdbcTemplate等。
    • 快速开发:Spring Boot提供了大量的Starter依赖,开发者只需要引入相关的Starter,就可以快速搭建起一个包含特定功能的Spring应用。比如引入spring - boot - starter - web就可以快速搭建一个Web应用,包含了Tomcat服务器、Spring MVC等相关依赖和配置。
    • 开箱即用:Spring Boot内置了很多常用的中间件和服务器,如Tomcat、Jetty等,不需要额外安装和配置。同时,它还提供了健康检查、指标监控等功能,方便应用的运维和管理。
  6. MyBatis中#{}和${}的区别
    • #{}:是预编译处理,MyBatis在处理#{}时,会将SQL中的#{}替换为?,然后使用PreparedStatement进行参数设置。这样可以有效防止SQL注入攻击。例如,SQL语句select * from user where username = #{username},在执行时会变为select * from user where username =?,然后通过PreparedStatement.setString(1, usernameValue)设置参数值。
    • **:是字符串替换,MyBatis在处理{}**:是字符串替换,MyBatis在处理`{}时,会直接将中的内容替换为实际的值。例如,SQL语句selectfromuserwhereusername={}`中的内容替换为实际的值。例如,SQL语句`select * from user where username = '{username}',如果username的值为“admin' or '1'='1”,那么最终执行的SQL语句就是select * from user where username = 'admin' or '1'='1',这就导致了SQL注入漏洞。所以,在使用${}`时要特别小心,一般用于传入表名、列名等非用户输入的参数。
  7. Dubbo核心功能
    • 服务治理:Dubbo提供了服务注册与发现功能,服务提供者将自己的服务注册到注册中心(如Zookeeper),服务消费者从注册中心获取服务提供者的地址信息。同时,Dubbo还支持服务的动态配置,如服务的权重调整、流量控制等。例如,在一个电商系统中,有多个商品服务提供者,Dubbo可以根据配置动态调整每个服务提供者的权重,以实现更合理的负载均衡。
    • 负载均衡:Dubbo支持多种负载均衡策略,如随机、轮询、最少活跃调用数等。当服务消费者调用服务时,Dubbo会根据配置的负载均衡策略从多个服务提供者中选择一个进行调用。例如,随机策略会随机选择一个服务提供者,轮询策略会按照顺序依次选择服务提供者。
    • 远程调用:Dubbo采用高性能的网络通信框架(如Netty)实现远程服务调用,支持多种协议(如Dubbo协议、HTTP协议等)。服务消费者可以像调用本地方法一样调用远程服务,Dubbo会负责底层的网络通信和数据序列化/反序列化。
  8. RabbitMQ消息确认机制
    • 生产者确认:RabbitMQ提供了两种生产者确认模式,即普通确认模式和批量确认模式。
      • 普通确认模式:生产者发送一条消息后,调用channel.waitForConfirms()方法等待RabbitMQ的确认。如果消息成功到达RabbitMQ服务器,该方法返回true,否则返回false。这种模式下,每发送一条消息都需要等待确认,性能较低。
      • 批量确认模式:生产者可以批量发送多条消息,然后调用channel.waitForConfirms()方法等待确认。这种模式下,只要有一条消息未成功到达服务器,该方法就会返回false,生产者需要重新发送这批消息。批量确认模式相较于普通确认模式,减少了等待确认的次数,提高了性能。
    • 消费者确认:消费者从RabbitMQ获取消息后,需要向RabbitMQ发送确认消息,告知RabbitMQ该消息已被成功处理。RabbitMQ有两种消费者确认方式,即自动确认和手动确认。
      • 自动确认:消费者一旦接收到消息,RabbitMQ就认为该消息已被成功处理,会立即从队列中删除。这种方式简单,但如果消费者在处理消息过程中出现异常,消息就会丢失。
      • 手动确认:消费者接收到消息后,调用channel.basicAck(deliveryTag, multiple)方法手动确认消息。deliveryTag是消息的唯一标识,multiple表示是否批量确认。如果设置为true,则表示确认该deliveryTag及之前的所有消息。手动确认可以确保消息不会丢失,但需要开发者在代码中正确处理确认逻辑。
  9. xxl - job特点
    • 简单易用:xxl - job提供了简洁的Web控制台,用户可以在控制台中方便地管理任务,包括任务的新增、修改、删除、启动、停止等操作。同时,任务的配置也很简单,只需要填写任务的执行地址、执行参数等基本信息。
    • 分布式调度:支持将任务分布式部署在多个节点上执行,提高任务处理能力。例如,在一个大数据处理系统中,有大量的数据需要处理,可以将任务分发到多个节点上并行处理,加快处理速度。
    • 任务依赖:支持任务之间的依赖关系配置,即一个任务的执行可以依赖于另一个任务的执行结果。例如,在一个ETL任务流程中,数据抽取任务完成后才能执行数据转换任务,通过配置任务依赖可以确保任务按顺序执行。
    • 故障转移:当某个执行节点出现故障时,xxl - job会自动将任务调度到其他正常节点上执行,保证任务的可靠性。