互联网大厂面试:Java核心知识、框架与中间件大考验
在互联网大厂的一间明亮面试室内,严肃的面试官正襟危坐,对面坐着略显紧张的求职者王铁牛。一场对Java核心知识、JUC、JVM等多方面技术的考验即将展开。
第一轮提问 面试官:首先问几个基础问题。Java里基本数据类型有哪些? 王铁牛:这个我知道,有byte、short、int、long、float、double、char、boolean。 面试官:回答正确,不错。那说说HashMap的底层数据结构是什么? 王铁牛:HashMap底层是数组加链表,当链表长度达到一定阈值后会转成红黑树。 面试官:很好,理解得挺到位。那ArrayList和LinkedList有什么区别呢? 王铁牛:ArrayList是基于动态数组实现的,随机访问快;LinkedList是基于双向链表实现的,插入和删除操作快。 面试官:回答得很清晰,基础掌握得不错。
第二轮提问 面试官:进入JUC相关问题。说说CountDownLatch的作用和使用场景。 王铁牛:呃……这个……好像是和线程同步有关吧,具体场景我有点模糊了。 面试官:那说下线程池的几个重要参数。 王铁牛:有核心线程数、最大线程数,还有……其他的我不太确定了。 面试官:再问一个,JVM的垃圾回收算法有哪些? 王铁牛:有标记清除、标记整理,还有个复制算法,但具体原理我讲不太清楚。 面试官:这几个问题回答得不太理想,你对JUC和JVM的知识掌握得不够扎实。
第三轮提问 面试官:接下来问一些框架相关的。Spring的IOC和AOP是什么? 王铁牛:IOC是控制反转,把对象的创建和管理交给Spring容器;AOP是面向切面编程,用于实现日志记录、事务管理等功能。 面试官:回答得还行。那Spring Boot和Spring的关系是什么? 王铁牛:Spring Boot是Spring的扩展,简化了Spring的开发,提供了自动配置等功能。 面试官:不错。再问下MyBatis的工作原理。 王铁牛:这个……好像是通过配置文件或者注解来映射SQL语句,然后和数据库交互,但具体流程我不太清楚。 面试官:整体回答有好有坏,你对框架的理解有一定基础,但还存在不少欠缺。
面试官:今天的面试就到这里,你回家等通知吧。后续我们会综合评估后给你反馈。
问题答案详细解析
- Java基本数据类型:
- Java有8种基本数据类型,可分为4类。整数类型:byte(1字节)、short(2字节)、int(4字节)、long(8字节);浮点类型:float(4字节)、double(8字节);字符类型:char(2字节);布尔类型:boolean(1位)。基本数据类型直接存储值,而不是引用。
- HashMap底层数据结构:
- JDK 1.8之前,HashMap底层是数组 + 链表结构。数组是HashMap的主体,链表是为了解决哈希冲突而存在的。当两个不同的键通过哈希函数计算出相同的哈希值时,就会发生哈希冲突,这些冲突的元素会以链表的形式存储在数组的同一个位置。
- JDK 1.8及以后,为了提高查询效率,当链表长度达到8且数组长度大于64时,链表会转换为红黑树。红黑树是一种自平衡的二叉搜索树,在节点较多时,其查找、插入和删除操作的时间复杂度为O(log n),比链表的O(n)要快。
- ArrayList和LinkedList的区别:
- 数据结构:ArrayList基于动态数组实现,数组在内存中是连续存储的;LinkedList基于双向链表实现,链表由一个个节点组成,每个节点包含数据以及指向前一个和后一个节点的引用。
- 随机访问性能:ArrayList支持随机访问,通过索引可以直接访问数组中的元素,时间复杂度为O(1);LinkedList不支持高效的随机访问,需要从头节点或尾节点开始遍历链表,时间复杂度为O(n)。
- 插入和删除性能:ArrayList在中间或开头插入、删除元素时,需要移动后续元素,时间复杂度为O(n);LinkedList在任意位置插入、删除元素,只需要修改节点的引用,时间复杂度为O(1)。
- CountDownLatch的作用和使用场景:
- 作用:CountDownLatch是JUC包中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作。通过一个计数器来实现,计数器的初始值为线程的数量。每当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值为0时,表示所有线程都已经完成任务,等待的线程可以继续执行。
- 使用场景:例如,在一个大型任务中,有多个子任务需要并行执行,主线程需要等待所有子任务完成后才能继续执行后续操作,就可以使用CountDownLatch。
- 线程池的重要参数:
- corePoolSize:核心线程数,线程池在初始化时会创建的线程数量。当提交的任务数小于核心线程数时,线程池会创建新的线程来执行任务。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。当任务数超过核心线程数,且任务队列已满时,线程池会创建新的线程,直到达到最大线程数。
- keepAliveTime:线程空闲时间,当线程池中的线程数量超过核心线程数时,空闲线程在经过keepAliveTime时间后会被销毁。
- unit:keepAliveTime的时间单位,如TimeUnit.SECONDS等。
- workQueue:任务队列,用于存储等待执行的任务。常见的任务队列有ArrayBlockingQueue、LinkedBlockingQueue等。
- threadFactory:线程工厂,用于创建线程。可以通过自定义线程工厂来设置线程的名称、优先级等。
- handler:拒绝策略,当任务队列已满且线程池中的线程数量达到最大线程数时,新提交的任务会触发拒绝策略。常见的拒绝策略有AbortPolicy(直接抛出异常)、CallerRunsPolicy(让提交任务的线程执行该任务)等。
- JVM的垃圾回收算法:
- 标记 - 清除算法:首先标记出所有需要回收的对象,然后统一回收这些对象。缺点是会产生大量的内存碎片,导致后续大对象无法分配到连续的内存空间。
- 标记 - 整理算法:先标记出需要回收的对象,然后将存活的对象向一端移动,最后清理掉边界以外的内存。避免了内存碎片的问题,但移动对象的过程会带来一定的性能开销。
- 复制算法:将内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完后,将存活的对象复制到另一块内存中,然后清理掉原来的内存。优点是不会产生内存碎片,缺点是内存利用率较低,只能使用一半的内存。
- 分代收集算法:根据对象的存活周期将内存分为新生代和老年代。新生代对象存活时间短,采用复制算法;老年代对象存活时间长,采用标记 - 清除或标记 - 整理算法。
- Spring的IOC和AOP:
- IOC(控制反转):传统的程序开发中,对象的创建和管理由程序员手动完成。而在Spring中,IOC将对象的创建和管理交给Spring容器,由容器来负责对象的生命周期。通过依赖注入(DI)的方式,将对象之间的依赖关系注入到对象中,降低了代码的耦合度。
- AOP(面向切面编程):AOP是一种编程范式,它允许开发者在不修改原有代码的基础上,对程序进行增强。AOP通过将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,以切面的形式进行统一管理。Spring AOP基于代理模式实现,有JDK动态代理和CGLIB代理两种方式。
- Spring Boot和Spring的关系:
- Spring Boot是Spring的扩展,它简化了Spring应用的开发过程。Spring Boot提供了自动配置功能,通过约定大于配置的原则,减少了开发者的配置工作量。它还集成了各种常用的开发工具和框架,如嵌入式服务器、数据库连接池等,让开发者可以快速搭建和部署Spring应用。
- MyBatis的工作原理:
- 读取配置文件:MyBatis会读取配置文件(如mybatis-config.xml),其中包含了数据库连接信息、映射文件的位置等。
- 构建SqlSessionFactory:根据配置文件构建SqlSessionFactory,它是MyBatis的核心工厂类,用于创建SqlSession。
- 创建SqlSession:SqlSession是MyBatis执行数据库操作的会话,通过SqlSession可以执行SQL语句。
- 映射SQL语句:MyBatis通过映射文件(如Mapper.xml)或注解将Java方法与SQL语句进行映射。
- 执行SQL语句:SqlSession根据映射关系执行SQL语句,并将结果返回给调用者。
- 关闭SqlSession:操作完成后,需要关闭SqlSession,释放资源。