--- title: "01-Java基础高频面试题" created: 2026-09-01 tags: - Java --- # Java基础高频面试题 ## 使用说明 这是 01-Java基础 全组的**压轴复盘篇**:把前面九个板块的必考点收拢成一份速答题库。每题只给"速答"(面试时开口就要说的那几句),详细展开全部指路既有篇目——**答题靠速答,讲透靠回读**。建议自测方式:只看题目,先自己说 30 秒,再对照速答找差距。 --- ## 一、数据类型(01-数据类型体系) ### 1. == 和 equals 的区别? **速答**:\=\= 比较基本类型的值、引用类型的地址;`equals` 默认就是 \=\=(Object 原始实现),但 String、Integer 等都重写成了比较内容。所以问区别,本质是问"有没有被重写"。 **展开**:[[01-Object通用方法|Object通用方法]] ### 2. 为什么重写 equals 必须重写 hashCode? **速答**:契约要求"equals 相等则 hashCode 必须相等"。只重写 equals 时,两个"相等"对象 hashCode 不同,放进 HashMap 会被散列到不同桶,contains/get 查不到——集合行为直接出错。 **展开**:[[01-Object通用方法|Object通用方法]] ### 3. Integer 缓存是什么?`Integer a=127, b=127; a==b` 呢? **速答**:自动装箱走 `Integer.valueOf()`,对 -128~127 走缓存池返回同一对象,所以 `127==127` 为 true;`128==128` 超出缓存,各自 new 对象,\=\= 比地址为 false。结论:**包装类比较永远用 equals**。 **展开**:[[03-缓存池机制(Cache Pool)|缓存池机制(Cache Pool)]] ### 4. 自动拆箱有什么坑? **速答**:三连坑——\=\= 比较两个包装类比地址;包装类为 null 时拆箱抛 NPE(典型:三元运算符 `flag ? intVal : integerVal` 自动拆箱);循环里 `Long sum += i` 产生大量临时对象。缓存池只能救 -128~127。 **展开**:[[02-自动装拆箱|自动装拆箱]] ## 二、核心类(02-核心类详解) ### 5. String 为什么设计成不可变? **速答**:三点——字符串常量池复用的前提(池里共享一个对象,可变就互相污染);hashCode 可以缓存(HashMap key 稳定);天然线程安全。实现上 `private final char[]` 且不提供修改入口。 **展开**:[[01-String详解|String详解]] ### 6. String、StringBuilder、StringBuffer 怎么选? **速答**:拼接频繁用 StringBuilder;多线程共享拼接用 StringBuffer(方法加 synchronized);String 适合不变量。`str += ` 在循环里每次都 new 对象,是典型反模式。 **展开**:[[02-StringBuilder系列|StringBuilder系列]] ### 7. BigDecimal 为什么能保证精度?有什么坑? **速答**:用"无标度值+标度"存十进制数,不走二进制浮点。坑:`new BigDecimal(0.1)` 传入的就已经不精确,要用字符串构造或 `valueOf`;除不尽必须指定舍入模式否则抛异常;equals 连 scale 一起比,`0.1` 和 `0.10` 不相等,比数值用 compareTo。 **展开**:[[03-BigDecimal|BigDecimal]] ## 三、面向对象(03-面向对象核心) ### 8. 重载和重写的区别?("两同两小一大") **速答**:重载是同一个类里方法名同、参数列表不同(编译期绑定);重写是子类覆盖父类方法,规则"两同两小一大"——方法名参数表相同,返回值和异常范围更小或相同,访问权限更大或相同(编译期看引用类型,运行期看对象实际类型)。 **展开**:[[04-重写与重载|重写与重载]]、[[05-编译时多态和运行时多态|编译时多态和运行时多态]] ### 9. 接口和抽象类的区别? **速答**:抽象类是"是什么"(单继承,可有状态/构造器/具体实现),接口是"能做什么"(多实现,Java 8 前纯规范,之后可有 default/static 方法但无实例字段)。选型:共享代码用抽象类,定义能力用接口。 **展开**:[[02-抽象类与接口|抽象类与接口]]、[[01-抽象类与接口的区别|抽象类与接口的区别]] ### 10. Java 为什么不支持多重继承?接口多实现为什么又可以? **速答**:类的多继承有菱形歧义(两个父类对同一方法各有实现,子类不知道听谁的);接口多实现无歧义,因为 Java 8 前接口无实现,Java 8 后 default 冲突被编译器强制要求重写(可用 `A.super.xxx()` 显式指定),歧义在编译期消灭。 **展开**:[[07-为什么Java不支持多重继承?|为什么Java不支持多重继承?]]、[[03-接口多实现|接口多实现]] ### 11. 两个接口有同名 default 方法,实现类怎么办? **速答**:编译报错,必须重写。想复用某一方的实现用 `接口名.super.方法名()`。补充裁决顺序:类/父类方法永远优先于接口 default(类优先原则);子接口的 default 优先于父接口。 **展开**:[[03-接口多实现|接口多实现]] ## 四、关键字(04-关键字) ### 12. final 有哪几种用法? **速答**:修饰类不能被继承(String)、修饰方法不能被重写、修饰变量不可重新赋值——基本类型是值不变,引用类型是**指向不变**(对象内容仍可改)。另外 final + 语义上不可变,才构成真正的不可变对象。 **展开**:[[01-final关键字|final关键字]] ### 13. static 有哪些用法? **速答**:静态变量(类级别共享,方法区/元空间)、静态方法(无 this,不能直接访问实例成员)、静态代码块(类加载时执行一次)、静态内部类(不持有外部类引用,单例模式常用)。 **展开**:[[02-static关键字|static关键字]] ## 五、核心机制(05-核心机制) ### 14. 反射是什么?有什么用?代价是什么? **速答**:运行时获取 Class 对象、动态访问字段/方法/构造器的能力。价值:框架的基石(Spring IOC、MyBatis、序列化都靠它)。代价:绕过编译期检查、有安全检查开销(可用 setAccessible)、优化器难以内联,性能低于直接调用。 **展开**:[[01-Class对象与反射机制|Class对象与反射机制]] ### 15. JDK 动态代理和 CGLIB 有什么区别? **速答**:JDK 动态代理基于**接口**(Proxy+InvocationHandler,运行时生成实现同接口的代理类);CGLIB 基于**继承**(ASM 生成目标类子类,覆写非 final 方法)。Spring 默认:有接口用 JDK,无接口用 CGLIB。 **展开**:[[05-动态代理|动态代理]] ### 16. 受检异常和非受检异常怎么区分? **速答**:受检异常(IOException 等)继承 Exception 但不继承 RuntimeException,编译器强制 try-catch 或 throws;非受检异常(RuntimeException 及其子类如 NPE、越界)编译器不管,属于程序 bug,正确姿势是修代码而不是 catch 了事。Error 一律不捕获。 **展开**:[[04-异常体系|异常体系]] ### 17. 什么是泛型擦除? **速答**:泛型只存在于编译期,编译后 `List` 和 `List` 都是 `List`(原始类型),运行期拿不到泛型实参(能通过 getGenericSuperclass 等拿到声明处的签名)。代价:不能 `new T()`、不能泛型数组、不能基本类型。桥方法是为了保证擦除后多态仍然正确。 **展开**:[[02-泛型详解|泛型详解]] ### 18. 枚举实现单例为什么天然安全? **速答**:JVM 层面禁止反射创建枚举实例(Constructor.newInstance 遇到枚举直接抛 IllegalArgumentException),且枚举序列化由 JVM 保证——同一常量反序列化后仍是同一实例。普通单例要手动防反射(构造器抛异常)、防序列化(readResolve),枚举两样都自带。 **展开**:[[06-枚举|枚举]] ### 19. Lambda 捕获局部变量为什么要求 final(或事实最终)? **速答**:Lambda 捕获的是变量的值副本,存在合成字段里;如果允许变量再被修改,副本与原变量会各改各的,语义混乱,所以编译器强制"捕获后不可再改"。成员变量不受限——通过 this 引用,改的就是对象字段本身。 **展开**:[[07-Lambda与函数式接口|Lambda与函数式接口]] ### 20. map 和 flatMap 的区别?Stream 为什么惰性? **速答**:map 一进一出(元素换形态),flatMap 一进多出(把每个元素摊平成流再拼接,适合处理嵌套结构)。惰性是因为中间操作只"记账",终端操作触发时把整条链融合成一次遍历执行——还能靠 limit 短路提前终止,比手写多层循环少跑趟数。 **展开**:[[08-Stream与Optional|Stream与Optional]] ### 21. 字节流和字符流怎么选?transient 是干什么的? **速答**:字节流(InputStream/OutputStream)处理一切原始数据;字符流(Reader/Writer)处理文本,多一层按字符集编解码——文本读写永远显式指定编码。transient 修饰的字段不参与 Java 序列化,反序列化后为默认值(密码、缓存句柄等敏感/临时字段用)。 **展开**:[[09-IO流体系|IO流体系]] ## 六、JVM(07-JVM与内存模型 + 08-java的优势) ### 22. JVM 内存区域怎么划分?哪些线程共享? **速答**:线程共享——堆(对象实例)、方法区(Java 8 起由元空间实现,存类信息);线程私有——虚拟机栈(栈帧:局部变量表+操作数栈)、本地方法栈、程序计数器。OOM 多发在堆和元空间,StackOverflowError 发生在栈。 **展开**:[[01-JVM与内存模型|JVM与内存模型]] ### 23. 类加载过程和双亲委派是什么? **速答**:加载→验证→准备→解析→初始化五阶段。双亲委派:收到加载请求先交给父加载器(应用→平台→启动),父类搞不定才自己干——保证核心类(如 String)不被用户自定义类替换,安全且避免重复加载。 **展开**:[[02-类加载机制|类加载机制]] ### 24. GC 怎么判断对象该不该回收? **速答**:可达性分析——从 GC Roots(栈帧局部变量、静态变量、常量、JNI 引用等)向下搜索,搜不到的就是可回收。引用计数法有循环引用缺陷所以 JVM 不用。finalize/四种引用(强软弱虚)影响回收时机。 **展开**:[[03-垃圾回收与GC算法|垃圾回收与GC算法]] ### 25. 内存泄漏和内存溢出的区别与联系? **速答**:泄漏是"该死的对象没死"(被无意识引用拖住,GC 够不着),慢性病,症状是老年代持续走高;溢出是"内存彻底不够用"抛 OOM,急性发作。持续泄漏迟早引发堆溢出,但溢出不一定是泄漏(也可能是堆太小)。典型泄漏:静态集合、ThreadLocal 不 remove、监听器不注销。 **展开**:[[01-内存泄漏|内存泄漏]]、[[02-内存溢出|内存溢出]] ### 26. Java 为什么能跨平台?"写一次到处跑"的完整链路? **速答**:源码编译成平台无关的**字节码**,各平台的 JVM 负责把字节码翻译成本地机器码。性能靠 JIT 编译热点代码补回来(接近本地代码),快速启动场景用 AOT 提前编译。链路:`.java → javac → .class → JVM(解释器 + JIT)→ 机器码`。 **展开**:[[00-java的优势|java的优势]]、[[02-字节码|字节码]]、[[03-JIT编译|JIT编译]] ## 七、其他(09-其他) ### 27. 内部类有哪几种?各用在什么场景? **速答**:成员内部类(依赖外部类实例,少见)、静态内部类(不持有外部引用,Builder/单例 Holder 常用)、局部内部类(方法内,罕见)、匿名内部类(实现接口/继承类的单次使用,Java 8 后多数被 Lambda 替代,但"捕获 effectively final"的规则一脉相承)。 **展开**:[[02-内部类|内部类]] ### 28. 位运算有哪些实战技巧? **速答**:判断奇偶 `n & 1`;乘除 2 的幂用移位;`n & (n-1)` 消去最低位的 1(统计 1 的个数/判断 2 的幂);异或找"只出现一次的数"(`a^a=0`);HashMap 里 `hash & (n-1)` 定位桶就是取模的位运算优化。 **展开**:[[01-java中的位运算技巧|java中的位运算技巧]] --- ## 复盘建议 - 全对:可以往 [[01-容器高频面试题|容器高频面试题]] 和 [[07-并发高频面试题|并发高频面试题]] 进阶了,那两份是同一套"速答+指路"体例; - 错 3 题以上:别急着背答案,点开对应篇目回读原理——这套题库每道题背后都有完整篇目撑腰,这也是本组和其他八组并存的意义。 --- ⬅️ [[02-内部类|内部类]] 🏠 [[00-Java|00-Java]] ➡️ [[00-Java|00-Java]]