面了30场Java开发,我发现面试官翻来覆去就问这20题

📅 2026/8/14 2:44:26
面了30场Java开发,我发现面试官翻来覆去就问这20题
前言刷了这么多 Java 岗的面经我发现一个规律不管公司大小问来问去总是那几个老面孔。HashMap、线程池、JVM、Spring……把这些底子打牢技术面基本就稳了。这篇是我按出现频率刷选出来的 Top 20。每道后面还标了面试官八成会接着追问的点——这部分才是真能拉开差距的。正在准备社招或校招的朋友可以收藏一下通勤路上翻一翻记一下比刷短视频管用。直接往下看吧。 与 equals这题我一般先给结论 比基本类型时比值比引用类型时比的是不是同一个内存地址。然后补一句equals 默认其实也是比地址但像 String、Integer 这些常用类都重写了变成比内容了。最后顺手提一下重写 equals 一定要重写 hashCode不然放到 HashMap/HashSet 里会出 bug。❓ 常见追问问String s1 new String(“abc”) 和 String s2 “abc”s1 s2 是 true 吗答false。new 一定在堆上新建对象字面量走常量池两者地址不同。问只重写 equals 不重写 hashCode 会怎样答逻辑相等的两个对象 hashCode 不同在 HashSet 里会被当成两个对象存两份。问Integer 的缓存池范围是多少答默认 -128 ~ 127超出范围每次都会 new 新对象。String / StringBuilder / StringBufferString 是不可变的底层是 final 的字符数组每次拼接其实都新建对象所以循环里拼字符串性能很差。如果要频繁改单线程用 StringBuilder它可变而且没加锁、最快多线程环境下用 StringBuffer方法都 synchronized 了线程安全但慢一点。现在基本都推荐 StringBuilder除非明确有多线程拼接。❓ 常见追问问String 为什么设计成不可变答安全可做 HashMap 的 key、常量池复用、hash 值可缓存。问StringBuilder 的扩容机制答默认容量 16满了就扩容为 原容量 * 2 2。问Java 9 之后 String 底层有变化吗答有char[] 改成 byte[] 编码标识省内存。面向对象三大特性封装、继承、多态。封装就是藏细节、只暴露接口继承是子类复用父类代码Java 是单继承、多实现接口多态最常见就是父类引用指向子类对象“编译看左边、运行看右边”靠方法重写实现。❓ 常见追问问多态的实现前提是什么答继承/实现 方法重写 向上转型。问组合和继承怎么选答多用组合少用继承继承表达的是 is-a 强耦合关系。问构造器能不能被重写答不能重写但子类可以用 super() 调用父类构造器。重载Overload与重写Override重载是同一个类里方法名一样、参数列表不一样类型/个数/顺序跟返回值没关系编译期就绑定了。重写是子类改父类的实现方法签名得一致访问权限不能更严抛的异常不能比父类多是运行期绑定。❓ 常见追问问只是返回值不同算不算重载答不算编译器会报错。问重写能不能抛更宽的异常答不能编译报错。问构造方法能重载或重写吗答能重载不能重写。接口interface与抽象类abstract class抽象类可以有普通方法、构造器、各种成员变量表达的是 is-a 关系接口更多是个能力契约 can-do。Java 8 之后接口也能有 default 和 static 方法但成员变量默认是 public static final。关键区别是类只能单继承抽象类但能实现多个接口。❓ 常见追问问Java 8 接口 default 方法会带来什么问题答一个类实现多个接口且都有同名 default 方法时会冲突类必须自己重写。问什么时候用抽象类、什么时候用接口答要共享代码/状态用抽象类只定义行为规范用接口。问接口能定义私有方法吗答Java 9 可以用于接口内部代码复用。Java 集合框架概览整体分两大块Collection 和 Map。Collection 下面List 有序可重复ArrayList 数组实现、查快改慢LinkedList 链表、增删快Set 不可重复HashSet 哈希、TreeSet 红黑树有序。Map 就是 KV 存储HashMap、ConcurrentHashMap 这些。❓ 常见追问问ArrayList 和 LinkedList 随机访问差异答ArrayList 是 O(1)LinkedList 是 O(n)。问既然有了 Hashtable 为什么还要 HashMap答Hashtable 全表锁且过时HashMap 非线程安全但快得多。问CopyOnWriteArrayList 适合什么场景答读多写极少如监听器列表。HashMap 底层原理JDK 1.8JDK 1.8 的结构是数组 链表 红黑树。默认容量 16负载因子 0.75算下标用 hash (n-1)这个 hash 还做了一次扰动 (h ^ h16) 来减少碰撞。当链表长度到 8 且数组容量 ≥ 64 就转红黑树树节点降到 6 又退化回链表。扩容是翻倍。注意它线程不安全并发要用 ConcurrentHashMap。❓ 常见追问问为什么负载因子是 0.75答时间/空间权衡按泊松分布此时冲突概率较低。问为什么链表转树阈值是 8答哈希均匀时链表长度到 8 的概率极低转树维护成本才划算。问容量为什么必须是 2 的幂答这样 hash (n-1) 等价于取模且位运算更快。fail-fast 与 fail-safefail-fast 像 HashMap、ArrayList遍历时如果发现集合被结构修改了而且不是用迭代器自己的 remove就直接抛 ConcurrentModificationException它靠 modCount 计数器判断。 fail-safe 像 ConcurrentHashMap、CopyOnWriteArrayList它是基于快照/副本来遍历不会抛异常但可能读不到最新数据。❓ 常见追问问modCount 是什么答记录集合结构修改次数的计数器。问单线程下会触发 fail-fast 吗答会比如遍历时调用集合本身的 remove 而不是 iterator.remove。问如何安全地边遍历边删除答用 Iterator.remove() 或 Java 8 的 removeIf()。创建线程的方式常见四种继承 Thread 重写 run实现 Runnable更推荐不占单继承名额实现 Callable 能有返回值和异常配 FutureTask再就是直接用线程池 ExecutorService生产环境基本都走线程池。❓ 常见追问问实现 Runnable 和继承 Thread 怎么选答优先 Runnable解耦、可复用、不浪费继承名额。问Callable 和 Runnable 区别答Callable 有返回值、可抛受检异常、支持泛型。问线程池创建线程算哪种方式答本质还是 new Thread线程池只是做统一管理。线程池核心参数与流程核心七个参数核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略。流程是任务来了先占核心线程核心满了进队列队列也满了才扩到最大线程再满就走拒绝策略。拒绝策略有四种默认 AbortPolicy 是直接抛异常。❓ 常见追问问为什么不建议用 Executors 的快捷方法答FixedThreadPool 队列无界可能 OOMCachedThreadPool 线程数无上限。问核心线程会被回收吗答默认不会设 allowCoreThreadTimeOut(true) 才可以。问拒绝策略怎么选答看业务重要任务用 CallerRuns 或自定义落库别轻易丢。synchronized 与 ReentrantLocksynchronized 是 JVM 层面的关键字自动加锁释放不可中断、非公平。JDK 1.6 之后做了锁升级无锁 → 偏向锁 → 轻量级 → 重量级性能已经不错了。ReentrantLock 是 API 层面的要手动 unlock好处是能中断、能公平、能多条件等待、能 tryLock 试一下。❓ 常见追问问锁升级的过程答偏向锁无竞争→ 轻量级锁CAS 自旋→ 重量级锁挂起进操作系统互斥。问ReentrantLock 怎么实现公平答靠队列 FIFO非公平模式允许新线程插队。问什么场景用 synchronized什么场景用 Lock答简单同步用 synchronized需要中断/公平/多条件时用 Lock。volatile 关键字volatile 主要解决两个问题一个是可见性一个线程改了别的线程立刻能看到因为它强制刷主内存另一个是禁止指令重排序靠内存屏障实现。但它不保证原子性像 i 这种读-改-写复合操作光加 volatile 没用得用 AtomicInteger 或者锁。❓ 常见追问问为什么 i 加 volatile 还不对答i 是三步操作不是原子的。问双重检查单例为什么要用 volatile答防止指令重排导致拿到半初始化的对象。问volatile 和 synchronized 区别答volatile 轻量只保证可见有序synchronized 还保证原子性。死锁死锁就是两个或以上的线程互相拿着对方要的锁、谁都不放。四个必要条件互斥、持有并等待、不可抢占、循环等待。避免的话最常用的是按固定顺序加锁或者用 tryLock 加超时别让线程无限等下去。❓ 常见追问问怎么定位死锁答jstack 看线程栈、jconsole/VisualVM 都能看出来。问已经发生死锁怎么解除答一般只能重启或 kill 线程所以预防优于补救。问银行家算法了解吗答死锁避免算法通过资源预分配矩阵判断是否安全。JVM 内存模型运行时数据区运行时数据区分线程私有和共享两类。私有的有程序计数器、虚拟机栈、本地方法栈共享的是堆和方法区元空间。堆是对象的老家也是 GC 主战场还分新生代和老年代。栈里放的是栈帧、局部变量这些。❓ 常见追问问程序计数器干嘛用的答记录当前线程执行到哪是唯一不会 OOM 的区域。问栈溢出和堆溢出有什么区别答递归过深抛 StackOverflowError对象太多撑爆堆抛 OutOfMemoryError。问元空间在哪、会 OOM 吗答在本地内存也会 OOM但默认基本无上限可配置。垃圾回收GC判断对象能不能回收主流用可达性分析从 GC Roots 往下找找不到的就是垃圾。算法有标记-清除、标记-整理、复制算法实际是分代回收。收集器的话G1 现在用得最多Region 化、能控停顿再新一点有 ZGC停顿超低。❓ 常见追问问GC Roots 都包括哪些答栈里局部变量、静态变量、常量、JNI 引用等。问强、软、弱、虚引用区别答回收时机依次变早软引用内存不足才回收弱引用下次 GC 就回收。问G1 和 CMS 区别答G1 可预测停顿、无内存碎片CMS 并发但容易产生碎片。双亲委派模型类加载器收到加载请求先不自己干甩给父类加载器一层层往上父类都加载不了才自己来。好处有两个一是避免重复加载二是防止有人自己写个 java.lang.String 把核心类顶替掉。❓ 常见追问问为什么叫双亲委派答就是 parent 委派的翻译梗实际是父加载器链。问哪些场景破坏了双亲委派答JDBC 的 SPI、Tomcat 隔离、热部署都需要自定义加载逻辑。问怎么自定义类加载器答继承 ClassLoader重写 findClass 即可。反射Reflection反射就是运行时动态拿到类的信息、还能操作对象比如 Class.forName、getMethod、invoke 这些。框架底层全靠它Spring、MyBatis 都是。缺点就是破坏封装而且性能比直接调用差毕竟要查元数据、做安全检查。❓ 常见追问问反射性能差在哪里答方法查找、访问安全检查、无法被 JIT 内联优化。问怎么提升反射性能答setAccessible(true) 关检查、缓存 Method、必要时用 MethodHandle。问怎么获取并修改私有字段答getDeclaredField setAccessible(true) 后 set。Java 8 新特性最常考的几块Lambda 表达式和函数式接口写代码简洁很多Stream API 处理集合像流水线Optional 优雅处理空指针新的日期 API LocalDate还有 CompletableFuture 做异步编排。另外 default 方法让接口也能带默认实现。❓ 常见追问问Lambda 和匿名内部类区别答Lambda 不是匿名类、没有独立 .class、this 指向外部类。问并行流要注意什么答线程安全、共用 ForkJoinPool别在并行流里改共享状态。问Optional 怎么用才不别扭答别用 get()用 orElse/ifPresent/map 链式处理。Spring 的 IoC 与 AOPIoC 就是控制反转原来对象自己 new现在交给 Spring 容器管依赖关系由容器注入这就是 DI。AOP 是面向切面把日志、事务、权限这些横切逻辑抽出来不侵入业务代码底层是动态代理有接口用 JDK 动态代理没接口用 CGLIB。Bean 生命周期从实例化、属性填充、init 到销毁。❓ 常见追问问Spring 怎么解决循环依赖答三级缓存但构造器注入的循环依赖解决不了。问JDK 动态代理和 CGLIB 区别答前者基于接口、后者基于继承CGLIB 不能代理 final 类/方法。问Spring Bean 默认线程安全吗答默认单例无状态 Bean 一般安全有状态需要注意。Java 异常体系顶层是 Throwable分 Error 和 Exception。Error 是系统级的、基本救不回来比如 OOM。Exception 里又分受检异常编译期就逼你处理比如 IOException和非受检也就是 RuntimeException比如空指针。写代码的原则是能处理就 catch处理不了就往上抛finally 一定会执行。❓ 常见追问问常见的 RuntimeException 有哪些答空指针、数组越界、类型转换、算术异常等。问finally 里有 return 会怎样答会覆盖 try 里的 return非常不推荐这样写。问自定义业务异常应该继承哪种答一般继承 RuntimeException省去到处声明 throws。总结20 道过完一遍是不是觉得没想象中吓人Java 面试题看着多其实就围着集合、并发、JVM、Spring 这几块打转。说句实在的背答案最没用。面试官换个说法问你脑子里的逻辑还能接住那才叫真懂。所以每道题的追问部分别跳过那才是拉分的地方。要是这篇帮到你点个赞或者转给也在找工作的朋友。下篇我准备写 MySQL 和 Redis 的高频题想看的在评论区扣个 1。