2021 Java面试真题深度解析:技术要点与实战指南

一、JVM与内存管理:从原理到实践

1. JVM内存模型与垃圾回收机制
2021年面试中,JVM相关问题占比超30%,重点考察内存分区与GC算法。例如:

  • 问题:JVM堆内存如何划分?各区域作用是什么?
    解析:堆内存分为新生代(Eden、Survivor)、老年代。Eden区存放新对象,Survivor区通过复制算法(From→To)减少内存碎片,老年代存储长期存活对象。Full GC触发条件包括老年代空间不足或System.gc()调用。
    代码示例
    1. // 模拟对象晋升至老年代
    2. public class GCDemo {
    3. public static void main(String[] args) {
    4. byte[] hugeArray = new byte[10 * 1024 * 1024]; // 分配10MB对象
    5. System.out.println("对象分配在Eden区");
    6. // 多次GC后,对象可能晋升至老年代
    7. }
    8. }

    建议:掌握-Xms-Xmx等JVM参数调优,结合VisualVM或JProfiler分析GC日志。

2. 类加载机制与双亲委派模型

  • 问题:双亲委派模型的作用及破坏场景?
    解析:双亲委派通过从下至上检查类是否已加载,避免类冲突(如避免用户自定义java.lang.String覆盖JDK类)。破坏场景包括线程上下文类加载器(如JDBC驱动加载)。
    代码示例
    1. // 自定义类加载器(需继承ClassLoader)
    2. public class CustomClassLoader extends ClassLoader {
    3. @Override
    4. protected Class<?> findClass(String name) {
    5. // 自定义类加载逻辑
    6. return super.findClass(name);
    7. }
    8. }

二、多线程与并发编程:核心机制与陷阱

1. synchronized与Lock对比

  • 问题:ReentrantLock相比synchronized的优势?
    解析:ReentrantLock支持公平锁、可中断锁、超时获取锁等特性,适用于高并发场景。
    代码示例
    1. Lock lock = new ReentrantLock();
    2. lock.lock(); // 获取锁
    3. try {
    4. // 临界区代码
    5. } finally {
    6. lock.unlock(); // 必须释放锁
    7. }

    建议:优先使用synchronized简化代码,复杂场景(如读写分离)选择ReentrantReadWriteLock

2. 线程池参数配置

  • 问题:如何合理设置线程池核心参数?
    解析:核心参数包括corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(空闲线程存活时间)。任务队列选择需考虑吞吐量(ArrayBlockingQueue)与拒绝策略(如AbortPolicy抛出异常)。
    代码示例
    1. ExecutorService executor = new ThreadPoolExecutor(
    2. 5, // corePoolSize
    3. 10, // maximumPoolSize
    4. 60, TimeUnit.SECONDS, // keepAliveTime
    5. new LinkedBlockingQueue<>(100), // 任务队列
    6. new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
    7. );

三、Spring框架:核心机制与源码解析

1. Spring IoC与DI实现原理

  • 问题:Spring如何通过反射实现依赖注入?
    解析:Spring通过BeanFactoryApplicationContext加载Bean定义,利用反射调用构造方法或setter方法注入依赖。
    代码示例
    1. // 用户类
    2. public class UserService {
    3. private UserRepository repository;
    4. public void setRepository(UserRepository repository) {
    5. this.repository = repository;
    6. }
    7. }
    8. // XML配置
    9. <bean id="userService" class="com.example.UserService">
    10. <property name="repository" ref="userRepository"/>
    11. </bean>

2. AOP实现方式与适用场景

  • 问题:JDK动态代理与CGLIB代理的区别?
    解析:JDK动态代理基于接口,通过InvocationHandler实现;CGLIB通过继承目标类生成子类,适用于无接口场景。Spring默认对接口使用JDK代理,对类使用CGLIB。
    代码示例
    1. // JDK动态代理示例
    2. public interface LogService {
    3. void log(String message);
    4. }
    5. public class LogServiceImpl implements LogService {
    6. public void log(String message) {
    7. System.out.println("Log: " + message);
    8. }
    9. }
    10. // 代理类
    11. public class LogProxy implements InvocationHandler {
    12. private Object target;
    13. public LogProxy(Object target) {
    14. this.target = target;
    15. }
    16. @Override
    17. public Object invoke(Object proxy, Method method, Object[] args) {
    18. System.out.println("Before method");
    19. return method.invoke(target, args);
    20. }
    21. }
    22. // 创建代理对象
    23. LogService proxy = (LogService) Proxy.newProxyInstance(
    24. LogService.class.getClassLoader(),
    25. new Class[]{LogService.class},
    26. new LogProxy(new LogServiceImpl())
    27. );

四、数据库与SQL优化:从索引到事务

1. MySQL索引失效场景

  • 问题:哪些情况会导致索引失效?
    解析:常见场景包括对列使用函数(如WHERE DATE(create_time) = '2021-01-01')、隐式类型转换(如字符串与数字比较)、OR条件未全部使用索引列。
    优化建议:使用EXPLAIN分析执行计划,避免全表扫描。

2. 事务隔离级别与实现

  • 问题:MySQL默认隔离级别及可能的问题?
    解析:MySQL默认REPEATABLE READ,通过MVCC(多版本并发控制)实现,可能产生幻读(需通过SELECT ... FOR UPDATE加锁解决)。
    代码示例
    1. -- 设置事务隔离级别
    2. SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    3. -- 开启事务
    4. START TRANSACTION;
    5. SELECT * FROM accounts WHERE user_id = 1 FOR UPDATE; -- 加锁
    6. UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
    7. COMMIT;

五、综合问题:系统设计与算法

1. 高并发系统设计要点

  • 问题:如何设计一个秒杀系统?
    解析:核心策略包括限流(如令牌桶算法)、异步处理(消息队列)、库存预热(Redis预减库存)、防超卖(乐观锁或分布式锁)。
    架构示例
    1. 用户请求 Nginx负载均衡 网关限流 消息队列(Kafka 服务层处理 数据库(分库分表)

2. 算法题:链表反转

  • 问题:如何反转单链表?
    解析:迭代法时间复杂度O(n),空间复杂度O(1)。
    代码示例
    1. public ListNode reverseList(ListNode head) {
    2. ListNode prev = null;
    3. ListNode curr = head;
    4. while (curr != null) {
    5. ListNode next = curr.next;
    6. curr.next = prev;
    7. prev = curr;
    8. curr = next;
    9. }
    10. return prev;
    11. }

六、面试准备建议

  1. 系统复习:按JVM、多线程、Spring、数据库等模块分类整理知识点。
  2. 代码实践:通过LeetCode或自定义项目练习算法与系统设计。
  3. 模拟面试:与同行进行Mock Interview,重点训练表达逻辑。
  4. 关注动态:跟踪Java 11+新特性(如Var关键字、模块化系统)。

结语:2021年Java面试题更注重底层原理与实战能力,掌握本文所述核心知识点,结合持续练习,可显著提升面试通过率。