核心内容摘要
91在线视精品如何席卷亚洲
Java实习模拟面试集合、并发、JVM与Spring核心高频题深度解析附连环追问适合人群准备Java后端实习/校招的同学关键词HashMap原理、ConcurrentHashMap、线程池、JVM内存模型、Spring事务、AQS、红黑树建议阅读时间
分钟在Java后端开发的面试中集合框架、并发编程、JVM原理和Spring框架是四大核心模块几乎每场面试都会涉及。
尤其是对于实习生岗位面试官更看重你对基础原理的理解深度而非项目经验。
本文以真实模拟面试对话的形式带你沉浸式体验一场高质量Java实习面试并附带面试官连环追问帮助你查漏补缺、构建系统性知识体系。
集合框架篇面试官提问请说说 HashMap 的底层
实现原理JDK
7 和 JDK
8 有什么区别候选人回答好的。
HashMap 是基于哈希表实现的 Map 接口底层使用数组 链表/红黑树结构。
在 JDK
7 中HashMap 使用数组 单向链表当发生哈希冲突时采用头插法插入新节点。
而 JDK
8 改为尾插法并引入了红黑树优化当链表长度 ≥ 8 且数组长度 ≥ 64 时链表会转换为红黑树提升查找效率从 O(n) 降到 O(log n)。
另外JDK
8 的扩容机制也做了优化采用高位运算代替取模减少哈希计算开销。
✅关键点头插 → 尾插、链表 → 红黑树、扩容优化。
面试官追问为什么 JDK
7 头插法在多线程下会导致死循环JDK
8 解决了吗候选人回答是的JDK
7 多线程并发扩容时由于头插法会反转链表顺序如果两个线程同时操作同一个桶可能形成环形链表导致 get() 方法死循环。
JDK
8 虽然改用尾插法避免了环形链表问题但HashMap 本身仍不是线程安全的只是不会死循环但仍可能出现数据覆盖或丢失。
所以多线程场景应使用ConcurrentHashMap或加锁。
面试官提问那 ConcurrentHashMap 在 JDK
7 和
8 的实现有什么不同候选人回答这是个经典对比JDK
7采用分段锁Segment机制。
整个 Map 被分成多个 Segment每个 Segment 相当于一个小型的 HashTable锁粒度是 Segment 级别最多支持 16 个并发写默认。
JDK
8取消 Segment直接使用synchronized CAS实现。
对每个桶Node加锁锁粒度细化到单个链表/红黑树头节点并发性能更高。
同时利用 CAS 操作无锁化更新 sizeCtl 等字段。
所以 JDK
8 的 ConcurrentHashMap 并发度更高、内存占用更少。
面试官提问ArrayList 和 LinkedList 有什么区别什么场景下用哪个候选人回答两者底层结构完全不同特性ArrayListLinkedList底层动态数组双向链表随机访问O(
O(n)插入/删除首尾O(
尾部O(n)中间O(
首尾O(n)中间需遍历内存占用连续内存缓存友好节点多出 prev/next 指针内存开销大使用建议需要频繁随机访问如 for 循环遍历→ArrayList频繁在首尾增删如队列、栈→LinkedList一般业务开发中ArrayList 更常用因为 CPU 缓存局部性好实际性能往往优于 LinkedList。
面试官追问为什么 HashMap 要用红黑树而不是 AVL 树候选人回答这是个很好的问题红黑树和 AVL 树都是自平衡二叉搜索树但AVL 树严格平衡查询更快O(log n)但插入/删除时旋转次数多维护成本高。
红黑树近似平衡插入/删除效率更高牺牲少量查询性能换取整体操作的稳定性。
HashMap 更关注综合性能尤其在频繁 put/remove 的场景下红黑树的“写快读稍慢”特性更合适。
而且 JDK 作者实测表明红黑树在 hash 冲突较多时表现更优。
并发编程篇面试官提问synchronized 和 ReentrantLock 有什么区别候选人回答两者都能实现可重入锁但有本质差异对比项synchronizedReentrantLock实现方式JVM 内置关键字monitorJDK API基于 AQS锁释放自动代码块结束必须手动 unlock()公平性非公平可选公平/非公平中断响应不支持支持 lockInterruptibly()条件变量1 个wait/notify多个 Condition性能JDK
6差距极小偏向锁优化略灵活建议简单同步用synchronized需要高级功能超时、中断、多条件用ReentrantLock。
面试官提问volatile 关键字的作用和
实现原理候选人回答volatile主要解决可见性和有序性问题不保证原子性。
可见性写 volatile 变量时强制将值刷回主内存读时从主内存加载最新值。
有序性禁止指令重排序通过插入内存屏障实现。
底层原理在生成汇编指令时JVM 会在 volatile 读写前后插入LoadLoad、StoreStore、LoadStore、StoreLoad等内存屏障确保 CPU 缓存一致性MESI 协议和指令顺序。
⚠️ 注意i这种操作即使加 volatile 也不安全因为包含“读-改-写”三步需用 AtomicInteger。
面试官提问ThreadLocal 的原理如何避免内存泄漏候选人回答ThreadLocal 为每个线程提供独立的变量副本底层通过ThreadLocalMap实现key 是 ThreadLocal 对象弱引用value 是实际值。
内存泄漏风险如果 ThreadLocal 对象被回收强引用消失但 Thread 还在运行那么 key 为 null 的 entry 无法被自动清理value 一直占用内存。
解决方案每次用完调用remove()最重要将 ThreadLocal 声明为static final避免频繁创建在线程池中尤其要注意因为线程复用残留数据会污染后续任务面试官提问线程池的核心参数和工作原理候选人回答ThreadPoolExecutor有 5 个核心参数newThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 空闲线程存活时间TimeUnitunit,BlockingQueueRunnableworkQueue,// 任务队列ThreadFactorythreadFactory,RejectedExecutionHandlerhandler// 拒绝策略);工作流程提交任务 → 若线程数 corePoolSize创建新线程执行否则放入 workQueue若队列满且线程数 maxPoolSize继续创建线程若队列满且线程数 maxPoolSize触发拒绝策略AbortPolicy 等线程池大小设置建议CPU 密集型corePoolSize CPU 核数IO 密集型corePoolSize CPU 核数 * (1 平均等待时间 / 平均CPU时间)面试官追问AQS 是什么怎么实现的候选人回答AQSAbstractQueuedSynchronizer是Java 并发包的基石如 ReentrantLock、CountDownLatch 都基于它。
核心思想维护一个stateint 类型表示同步状态通过CAS 修改 state线程获取不到锁时会被封装成Node加入CLH 队列双向链表支持独占模式如 ReentrantLock和共享模式如 Semaphore子类只需实现tryAcquire()/tryRelease()等方法AQS 负责排队、唤醒等复杂逻辑。
面试官提问Java 中有哪些锁分别适用什么场景候选人回答这个问题可以从多个维度理解
乐观锁 vs 悲观锁悲观锁认为总会冲突直接加锁如 synchronized乐观锁认为很少冲突用版本号/CAS 检查如 AtomicInteger
JVM 内置锁优化synchronized 升级过程偏向锁无竞争时偏向第一个线程减少 CAS轻量级锁有竞争但线程交替执行自旋等待重量级锁竞争激烈挂起线程OS 互斥量JDK 15 默认关闭偏向锁因现代应用并发度高。
自旋锁线程不挂起循环检查锁是否可用适合锁持有时间短JDK 中轻量级锁就用了自旋
JVM 篇面试官提问JVM 内存模型运行时数据区怎么划分候选人回答JVM 内存分为线程私有和线程共享两部分线程私有程序计数器PC Register虚拟机栈Stack存储局部变量、方法调用本地方法栈Native 方法线程共享堆Heap对象实例、数组GC 主战场方法区Metaspace in JDK 8类信息、常量、静态变量注意JDK 8 移除了永久代PermGen用 Metaspace本地内存替代避免 OOM。
面试官提问CMS 和 G1 垃圾回收器的区别候选人回答特性CMSG1目标低停顿可预测停顿 高吞吐算法标记-清除标记-整理Region 级内存布局老年代连续堆划分为多个 Region碎片问题有需 Full GC 整理无整理时移动对象适用场景老年代大、响应敏感大堆4G、停顿时间可控 G1 是未来主流支持-XX:MaxGCPauseMillis设置最大停顿目标。
面试官提问如何打破双亲委派模型候选人回答双亲委派指类加载时先委托父加载器父无法加载才自己加载。
打破方式重写ClassLoader.loadClass()方法不调用 parent.loadClass()而是自己先尝试加载。
典型场景TomcatWebAppClassLoader 优先加载 WEB-INF/lib 下的类避免不同应用类冲突SPI 机制如 JDBC通过Thread.currentThread().getContextClassLoader()加载第三方驱动面试官提问如何排查 OOM 问题候选人回答步骤如下添加 JVM 参数-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmp/heap.hprof用工具分析 dump 文件MATMemory Analyzer ToolJVisualVM查看内存占用最大的对象分析是否缓存未清理如 static MapThreadLocal 未 remove大对象频繁创建结合日志和代码定位泄漏点
Spring 框架篇面试官提问Spring IOC 和 AOP 原理Bean 生命周期候选人回答IOC控制反转通过BeanFactory/ApplicationContext容器管理对象依赖核心是反射 工厂模式。
AOP面向切面基于动态代理JDK Proxy 或 CGLIB在运行时织入增强逻辑。
Bean 生命周期关键步骤实例化构造函数属性填充AutowiredAware 接口回调BeanNameAware 等BeanPostProcessor.postProcessBeforeInitializationPostConstruct / InitializingBean.afterPropertiesSetBeanPostProcessor.postProcessAfterInitialization使用PreDestroy / DisposableBean.destroy面试官提问Transactional 什么时候会失效候选人回答常见失效场景自调用同类中方法 A 调用加了 Transactional 的方法 B代理失效非 public 方法Spring AOP 默认只代理 public 方法异常被捕获未抛出默认只回滚 RuntimeException 和 Error错误的传播行为如 REQUIRED_NEW 在嵌套事务中未正确使用数据库不支持事务如 MyISAM 引擎✅ 解决方案注入 self 代理、确保异常抛出、使用 AspectJ 编译时织入等。
面试官提问SpringBoot 自动配置原理候选人回答核心是EnableAutoConfiguration它会扫描META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports旧版是 spring.factories加载所有自动配置类如DataSourceAutoConfiguration通过ConditionalOnClass、ConditionalOnMissingBean等条件注解按需生效Starter 机制starter 是“依赖描述符”如spring-boot-starter-web引入 Tomcat Spring MVC Jackson 等配合自动配置实现“开箱即用”。
结语这场模拟面试覆盖了 Java 后端实习的高频核心考点。
记住不要死记硬背理解原理才能应对追问结合源码和场景比如“为什么用红黑树”、“线程池参数怎么设”面试是双向交流遇到不会的可以说“这部分我了解不深但我的理解是…”。
最后提醒实习面试更看重学习能力 基础扎实度把本文提到的知识点吃透你已经超越 80% 的竞争者欢迎点赞、收藏、评论交流更多 Java 面试干货关注我持续更新