互联网大厂 Java 求职者面试场景

35 阅读6分钟

互联网大厂 Java 求职者面试场景

第一轮提问

面试官:铁牛,先说说 ArrayList 和 HashMap 的底层数据结构分别是什么? 王铁牛:ArrayList 底层是数组,HashMap 底层是数组和链表(JDK 1.8 之后引入了红黑树)。 面试官:回答得不错。那 ArrayList 在扩容时是怎么操作的? 王铁牛:当 ArrayList 元素数量超过容量时,会创建一个新的更大的数组,然后把原数组的元素复制到新数组中。 面试官:很好。那 HashMap 在什么情况下会发生扩容,扩容的过程是怎样的? 王铁牛:当 HashMap 的元素数量达到负载因子(默认 0.75)乘以容量时就会扩容。扩容时会创建一个新的更大的数组,然后重新计算每个元素在新数组中的位置并迁移过去。

第二轮提问

面试官:接下来聊聊多线程,说说线程池的作用是什么? 王铁牛:线程池可以管理和复用线程,避免频繁创建和销毁线程带来的开销。 面试官:不错,那常见的线程池类型有哪些? 王铁牛:有 FixedThreadPool、CachedThreadPool、SingleThreadExecutor 等。 面试官:那线程池中的拒绝策略有哪些? 王铁牛:呃……好像有 AbortPolicy、CallerRunsPolicy 等,具体不太记得了。

第三轮提问

面试官:再谈谈 Spring,Spring 的 IOC 是什么? 王铁牛:IOC 就是控制反转,把对象的创建和管理交给 Spring 容器来处理。 面试官:很好,那 Spring 的 AOP 呢? 王铁牛:AOP 是面向切面编程,就是可以在不修改原有代码的基础上,对一些横切关注点进行统一处理,比如日志、事务等。 面试官:那 Spring Boot 相对于 Spring 有什么优势? 王铁牛:Spring Boot 简化了 Spring 项目的配置,提供了很多开箱即用的功能。

最后,面试官说:铁牛,今天的面试就到这里了,你回去等通知吧。

答案详解

  1. ArrayList 和 HashMap 的底层数据结构:ArrayList 底层是 Object 类型的数组,它可以动态扩容来存储元素。HashMap 底层在 JDK 1.8 之前是数组和链表的组合,当链表长度达到一定阈值(默认 8)且数组容量达到一定条件时,链表会转化为红黑树,这样可以提高查找效率。
  2. ArrayList 扩容操作:当向 ArrayList 中添加元素时,如果当前元素数量超过了 ArrayList 的容量,就会触发扩容。扩容时,会创建一个新的数组,其容量为原数组容量的 1.5 倍(通过位运算实现:oldCapacity + (oldCapacity >> 1)),然后通过 System.arraycopy 方法将原数组的元素复制到新数组中。
  3. HashMap 扩容:HashMap 有一个负载因子(默认 0.75),当 HashMap 中的元素数量(size)达到负载因子乘以容量(threshold = loadFactor * capacity)时,就会进行扩容。扩容时会创建一个新的更大的数组,其容量为原来的 2 倍。然后遍历原数组中的每个链表(或红黑树),重新计算每个元素在新数组中的位置并迁移过去。在 JDK 1.8 中,为了减少链表过长导致的查找效率低下问题,当链表长度达到 8 且数组容量大于等于 64 时,链表会转化为红黑树;当红黑树节点数量小于 6 时,又会退化为链表。
  4. 线程池的作用:线程池可以管理和复用线程,避免频繁创建和销毁线程带来的开销。它可以控制线程的数量,提高系统的性能和稳定性。比如在一个 Web 应用中,有很多用户请求需要处理,如果每次请求都创建一个新线程,会消耗大量的系统资源,而使用线程池可以复用已有的线程来处理请求。
  5. 常见的线程池类型
    • FixedThreadPool:固定大小的线程池,线程数量固定,当有任务提交时,如果线程池中有空闲线程,则立即执行;如果没有空闲线程,则将任务放入队列中等待。
    • CachedThreadPool:可缓存的线程池,线程数量不固定,当有任务提交时,如果线程池中有空闲线程,则立即执行;如果没有空闲线程,则创建一个新线程来执行任务。如果线程空闲时间超过 60 秒,则会被回收。
    • SingleThreadExecutor:单线程的线程池,只有一个线程来执行任务,所有任务按照顺序依次执行。
  6. 线程池中的拒绝策略
    • AbortPolicy:默认的拒绝策略,当线程池无法处理新任务时,会抛出 RejectedExecutionException 异常。
    • CallerRunsPolicy:当线程池无法处理新任务时,会将任务交给调用者线程(提交任务的线程)来执行。
    • DiscardPolicy:当线程池无法处理新任务时,会直接丢弃该任务,不做任何处理。
    • DiscardOldestPolicy:当线程池无法处理新任务时,会丢弃队列中最老的任务(最早进入队列的任务),然后尝试提交新任务。
  7. Spring 的 IOC(控制反转):IOC 是一种设计思想,它把对象的创建和管理交给 Spring 容器来处理。在传统的编程中,我们需要自己创建对象并管理它们之间的依赖关系,而在 Spring 中,我们只需要在配置文件(或使用注解)中声明对象和它们之间的依赖关系,Spring 容器会自动创建和管理这些对象。比如我们有一个 UserService 类,它依赖于 UserDao 类,在使用 IOC 时,我们不需要手动 new UserDao 对象并注入到 UserService 中,而是由 Spring 容器来完成这个过程。
  8. Spring 的 AOP(面向切面编程):AOP 是一种编程范式,它可以在不修改原有代码的基础上,对一些横切关注点进行统一处理。横切关注点是指那些与业务逻辑无关,但又在多个地方都需要用到的功能,比如日志记录、事务管理、权限校验等。在 Spring 中,通过定义切面(Aspect)、切点(Pointcut)和通知(Advice)来实现 AOP。切面是一个包含切点和通知的类,切点定义了哪些方法会被拦截,通知定义了在切点方法执行前后、异常时等不同时机要执行的代码。
  9. Spring Boot 相对于 Spring 的优势:Spring Boot 简化了 Spring 项目的配置,提供了很多开箱即用的功能。它内置了 Tomcat 等 Web 服务器,不需要手动配置复杂的 XML 文件,只需要通过简单的注解和配置就可以快速搭建一个 Web 应用。同时,Spring Boot 还提供了自动配置功能,它会根据项目中引入的依赖自动配置相关的组件,大大提高了开发效率。