前言

本文整理了 160 道 Java 常见面试题,涵盖 Java 基础、集合、多线程、JVM、Spring、数据库、设计模式、分布式、算法等多个核心领域,旨在帮助读者系统复习,从容应对技术面试。

一、Java 基础(30道)

1. Java 语言有哪些特点?

面向对象、平台无关性、简单性、安全性、多线程、动态性、高性能、分布式支持。

2. JDK、JRE、JVM 的区别是什么?

  • JDK (Java Development Kit):Java 开发工具包,包含 JRE 和开发工具(如编译器 javac)。
  • JRE (Java Runtime Environment):Java 运行时环境,包含 JVM 和核心类库。
  • JVM (Java Virtual Machine):Java 虚拟机,负责执行字节码,实现平台无关性。

3. == 和 equals() 的区别?

== 比较两个对象的引用地址是否相同;equals() 默认比较引用地址,但可被重写用于比较对象内容(如 String、Integer)。

4. final、finally、finalize 的区别?

  • final:修饰类(不可继承)、方法(不可重写)、变量(常量)。
  • finally:异常处理中,无论是否发生异常都会执行的代码块。
  • finalize:Object 类的方法,垃圾回收前调用,不推荐使用。

5. String、StringBuffer、StringBuilder 的区别?

  • String:不可变字符序列,线程安全。
  • StringBuffer:可变字符序列,线程安全(方法 synchronized)。
  • StringBuilder:可变字符序列,线程不安全,性能更高。

6. 重载(Overload)和重写(Override)的区别?

  • 重载:同一类中,方法名相同,参数列表不同(类型、个数、顺序),与返回值、访问修饰符无关。
  • 重写:子类重写父类方法,方法名、参数列表、返回值类型(或子类)相同,访问权限不能更严格,不能抛出更宽泛的异常。

7. 抽象类(abstract class)和接口(interface)的区别?

  • 抽象类:可以有构造方法、成员变量、具体方法和抽象方法;单继承。
  • 接口:Java 8 前只能有常量和抽象方法;Java 8 后可以有默认方法、静态方法;多实现。

8. List、Set、Map 的区别?

  • List:有序、可重复。
  • Set:无序、不可重复。
  • Map:键值对,键不可重复。

9. ArrayList 和 LinkedList 的区别?

  • ArrayList:基于动态数组,随机访问快(O(1)),增删慢(需移动元素)。
  • LinkedList:基于双向链表,增删快(O(1)),随机访问慢(O(n))。

10. HashMap 的工作原理?

基于数组+链表/红黑树(JDK 8+)。通过 key 的 hashCode 计算数组下标,解决哈希冲突使用链表法,当链表长度超过阈值(默认 8)且数组长度≥64时,链表转为红黑树。

11. ConcurrentHashMap 如何保证线程安全?

JDK 7 使用分段锁(Segment);JDK 8 使用 synchronized + CAS 操作锁住数组的每个桶(Node),粒度更细,并发度更高。

12. 创建线程有哪几种方式?

  1. 继承 Thread 类,重写 run() 方法。
  2. 实现 Runnable 接口,实现 run() 方法。
  3. 实现 Callable 接口,结合 FutureTask。
  4. 使用线程池(ExecutorService)。

13. sleep() 和 wait() 的区别?

  • sleep():Thread 类的静态方法,不释放锁,时间到自动恢复。
  • wait():Object 类的方法,释放锁,需 notify()/notifyAll() 唤醒。

14. synchronized 和 ReentrantLock 的区别?

  • synchronized:JVM 内置锁,自动释放,不可中断,非公平锁。
  • ReentrantLock:API 级别锁,需手动释放,可中断,可设置公平/非公平,支持多个条件变量。

15. volatile 关键字的作用?

保证变量的可见性(直接读写主内存),禁止指令重排序,但不保证原子性。

16. 什么是 CAS?ABA 问题如何解决?

CAS(Compare And Swap)是一种无锁原子操作。ABA 问题指变量值从 A 变为 B 又变回 A,CAS 会误认为没变。解决:使用版本号(AtomicStampedReference)。

17. 线程池的核心参数?

  • corePoolSize:核心线程数。
  • maximumPoolSize:最大线程数。
  • keepAliveTime:非核心线程空闲存活时间。
  • workQueue:任务队列。
  • threadFactory:线程工厂。
  • handler:拒绝策略。

18. JVM 内存区域划分?

  • 程序计数器:当前线程执行的字节码行号指示器。
  • Java 虚拟机栈:存储局部变量表、操作数栈、动态链接、方法出口。
  • 本地方法栈:为 Native 方法服务。
  • 堆:存放对象实例,GC 主要区域。
  • 方法区(元空间):存储类信息、常量、静态变量。

19. 垃圾回收算法有哪些?

  • 标记-清除:产生内存碎片。
  • 标记-整理:避免碎片,但移动对象成本高。
  • 复制:将内存分为两块,每次使用一块,存活对象复制到另一块。
  • 分代收集:根据对象存活周期将堆分为新生代(复制算法)和老年代(标记-清除/整理)。

20. 类加载过程?

  1. 加载:将类文件加载到内存。
  2. 验证:确保字节码安全。
  3. 准备:为静态变量分配内存并设置默认值。
  4. 解析:将符号引用转为直接引用。
  5. 初始化:执行静态代码块和静态变量赋值。

21. 双亲委派模型是什么?

类加载器收到加载请求后,先委派给父类加载器,只有当父类加载器无法完成时,子加载器才尝试加载。避免类重复加载,保证核心类安全。

22. Spring 框架的核心?

IoC(控制反转)和 AOP(面向切面编程)。

23. Spring Bean 的作用域?

  • singleton:单例(默认)。
  • prototype:每次请求创建新实例。
  • request:每次 HTTP 请求创建一个。
  • session:每个 HTTP Session 创建一个。
  • global-session:全局 Session(Portlet 应用)。

24. Spring 事务传播行为?

  • REQUIRED(默认):如果当前没有事务,就新建一个;如果已存在,则加入。
  • REQUIRES_NEW:新建事务,挂起当前事务。
  • SUPPORTS:有事务则加入,没有则以非事务执行。
  • NOT_SUPPORTED:以非事务执行,挂起当前事务。
  • MANDATORY:必须在一个事务中运行,否则抛异常。
  • NEVER:必须在非事务中运行,否则抛异常。
  • NESTED:如果当前存在事务,则在嵌套事务内执行。

25. Spring MVC 工作流程?

  1. 用户发送请求到前端控制器 DispatcherServlet。
  2. DispatcherServlet 调用 HandlerMapping 查找 Handler。
  3. 返回 HandlerExecutionChain(包含 Handler 和拦截器)。
  4. DispatcherServlet 调用 HandlerAdapter 执行 Handler。
  5. Handler 执行后返回 ModelAndView。
  6. DispatcherServlet 将 ModelAndView 传给 ViewResolver 解析。
  7. ViewResolver 返回 View。
  8. DispatcherServlet 渲染视图并返回给用户。

26. MyBatis 中 #{} 和 ${} 的区别?

  • #{}:预编译处理,防止 SQL 注入,会自动加引号。
  • ${}:字符串替换,有 SQL 注入风险,直接拼接到 SQL 中。

27. 数据库事务的 ACID 特性?

  • 原子性(Atomicity):事务要么全部完成,要么全部不完成。
  • 一致性(Consistency):事务前后数据库完整性约束不被破坏。
  • 隔离性(Isolation):并发事务之间互不干扰。
  • 持久性(Durability):事务提交后,对数据的修改是永久的。

28. 数据库隔离级别?

  • 读未提交(Read Uncommitted):可能脏读、不可重复读、幻读。
  • 读已提交(Read Committed):避免脏读,可能不可重复读、幻读。
  • 可重复读(Repeatable Read):避免脏读、不可重复读,可能幻读(MySQL 默认,通过 MVCC 避免幻读)。
  • 串行化(Serializable):最高隔离级别,避免所有问题,性能最低。

29. 索引失效的场景?

  • 对索引列进行运算或函数操作。
  • 使用 != 或 <>。
  • 使用 OR 连接条件(除非所有列都有索引)。
  • like 以通配符开头(如 ‘%abc’)。
  • 字符串类型不加引号(隐式类型转换)。
  • 联合索引不满足最左前缀原则。

30. Redis 持久化方式?

  • RDB:定时快照,恢复快,可能丢失最后一次快照后的数据。
  • AOF:记录写命令,数据更安全,文件较大,恢复慢。
  • 混合持久化(Redis 4.0+):结合两者优点。

二、Java 集合(15道)

31. ArrayList 扩容机制?

默认初始容量 10,扩容时新容量 = 旧容量 + 旧容量 >> 1(即 1.5 倍)。

32. HashMap 的负载因子为什么是 0.75?

权衡时间和空间。0.75 时,链表长度超过 8 的概率极小,红黑树转换概率低,空间利用率较高。

33. HashMap 和 Hashtable 的区别?

  • HashMap 线程不安全,Hashtable 线程安全(方法 synchronized)。
  • HashMap 允许 null 键和 null 值,Hashtable 不允许。
  • HashMap 迭代器是 fail-fast,Hashtable 不是。

34. ConcurrentHashMap 1.7 和 1.8 的区别?

  • JDK 7:分段锁(Segment),锁粒度较粗。
  • JDK 8:synchronized + CAS 锁桶(Node),锁粒度更细,并发度更高;链表长度≥8 且数组长度≥64 时转为红黑树。

35. LinkedHashMap 如何实现有序?

继承 HashMap,内部维护一个双向链表,记录插入顺序或访问顺序(accessOrder=true 实现 LRU)。

36. TreeMap 和 HashMap 的区别?

  • HashMap:基于哈希表,无序,O(1) 时间复杂度。
  • TreeMap:基于红黑树,键有序(自然顺序或 Comparator),O(log n) 时间复杂度。

37. CopyOnWriteArrayList 原理?

写时复制。写操作(add、set、remove)时复制一个新数组,在新数组上操作,完成后将原数组引用指向新数组。读操作无锁,适合读多写少场景。

38. BlockingQueue 常用实现类?

  • ArrayBlockingQueue:有界,数组实现。
  • LinkedBlockingQueue:可选有界,链表实现。
  • PriorityBlockingQueue:无界,支持优先级。
  • SynchronousQueue:不存储元素,每个插入操作必须等待另一个线程的移除操作。

39. fail-fast 和 fail-safe 机制?

  • fail-fast:快速失败,迭代过程中发现集合被修改(非迭代器自己的 remove 方法),立即抛出 ConcurrentModificationException。如 ArrayList、HashMap。
  • fail-safe:安全失败,迭代时先复制原集合,在副本上迭代,不会抛异常。如 CopyOnWriteArrayList、ConcurrentHashMap。

40. Comparable 和 Comparator 的区别?

  • Comparable:自然排序,类实现 compareTo() 方法。
  • Comparator:定制排序,单独实现 compare() 方法,更灵活。

41. HashSet 如何保证元素不重复?

基于 HashMap 实现,元素作为 HashMap 的 key(value 为固定 Object),利用 HashMap 键的唯一性。

42. Iterator 和 ListIterator 的区别?

  • Iterator:可遍历 Set 和 List,只能向后遍历,只能 remove。
  • ListIterator:只能遍历 List,可向前向后遍历,可 add、set、remove。

43. Arrays.asList() 的注意事项?

返回的 List 是 Arrays 的内部类 ArrayList(固定大小),不支持 add、remove 操作。若要修改,可 new ArrayList<>(Arrays.asList(...))。

44. Collections.sort() 内部实现?

底层调用 Arrays.sort(),对于对象数组使用 TimSort(归并排序优化),对于基本类型数组使用 Dual-Pivot QuickSort。

45. 如何实现一个 LRU 缓存?

使用 LinkedHashMap(accessOrder=true)或自己实现:双向链表 + HashMap。最近访问的移到链表头部,容量满时淘汰尾部。

三、Java 多线程与并发(20道)

46. 线程的生命周期?

新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed Waiting)、终止(Terminated)。

47. 创建线程池的几种方式?

  1. Executors.newFixedThreadPool(int n):固定大小线程池。
  2. Executors.newCachedThreadPool():可缓存线程池。
  3. Executors.newSingleThreadExecutor():单线程线程池。
  4. Executors.newScheduledThreadPool(int corePoolSize):定时任务线程池。
  5. 手动 new ThreadPoolExecutor():自定义参数。

48. 线程池的拒绝策略?

  • AbortPolicy(默认):直接抛出 RejectedExecutionException。
  • CallerRunsPolicy:由调用者线程执行任务。
  • DiscardPolicy:直接丢弃任务,不抛异常。
  • DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试执行当前任务。

49. synchronized 锁升级过程?

无锁 → 偏向锁(单线程) → 轻量级锁(多线程竞争不激烈) → 重量级锁(竞争激烈)。

50

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐