📚 Java 基础
1. Java 的特点有哪些?
了解 Java 语言的核心特征
- 面向对象:支持封装、继承、多态等特性
- 平台独立性:Write Once, Run Anywhere (WORA)
- 自动内存管理:垃圾回收机制自动释放内存
- 多线程支持:内置线程机制,便于并发编程
- 安全性强:提供类型检查、异常处理等安全机制
- 动态特性:运行时类型检查和动态加载
2. JDK、JRE、JVM 有什么区别?
理解 Java 运行环境的三层结构
- JVM (Java Virtual Machine):虚拟机,执行字节码的抽象计算机,实现跨平台特性
- JRE (Java Runtime Environment):运行环境,包含 JVM 和运行必需的类库
- JDK (Java Development Kit):开发工具包,包含 JRE + 编译器 + 调试工具等开发工具
- 包含关系:JDK > JRE > JVM
3. 什么是字节码?为什么 Java 能跨平台?
深理解 Java 跨平台机制的原理
- 字节码定义:Java 源代码编译生成的中间代码,文件扩展名为 .class
- 跨平台原理:Java 源代码 → 字节码 → 不同平台的 JVM 执行
- 优势:一次编译,到处运行;开发者无需针对不同平台重新编写代码
- 执行流程:javac 编译器将 .java 文件编译为 .class 文件,再由各平台 JVM 解释执行
4. Java 中的基本数据类型有哪些?
掌握 Java 的类型系统
- 整数型:byte (1字节), short (2字节), int (4字节), long (8字节)
- 浮点型:float (4字节), double (8字节)
- 字符型:char (2字节,支持 Unicode)
- 布尔型:boolean (1字节,true/false)
- 默认值:整数 0,浮点 0.0,布尔 false,char '\u0000'
- 包装类:Integer, Long, Float, Double, Boolean 等对应的引用类型
5. final、finally、finalize 的区别?
区分三个相似但完全不同的概念
- final (关键字):修饰类不能继承,修饰方法不能重写,修饰变量不能重新赋值
- finally (关键字):try-catch-finally 中的代码块,无论是否异常都会执行,常用于资源释放
- finalize (方法):Object 类的方法,对象被垃圾回收前调用,用于清理资源(已过时)
- 应用场景:final 用于不变性控制,finally 用于异常安全,finalize 已被 try-with-resources 替代
🎯 面向对象编程
6. 什么是面向对象?有哪四大特性?
理解 OOP 的核心理念
- 面向对象定义:以对象为单位的编程思想,强调对象的属性和行为
- 四大特性:
- 抽象:提取事物的共同特征,忽略非本质细节
- 封装:隐藏内部实现细节,暴露必要接口,提高安全性
- 继承:子类继承父类的属性和方法,实现代码复用
- 多态:同一接口的不同实现,运行时动态绑定
7. 什么是多态?多态的实现方式有哪些?
深入理解 Java 多态机制
- 多态定义:一个对象拥有多种形态,同一方法调用在不同对象上有不同表现
- 实现方式:
- 编译时多态(重载):同名方法,参数不同,编译期确定调用
- 运行时多态(重写):父类引用指向子类对象,运行期确定实际调用的方法
- 运行时多态条件:继承、重写、向上转型
- 优势:提高代码灵活性和可维护性,支持接口编程
8. 接口和抽象类的区别?
对比两种抽象机制的异同
- 抽象类:使用 abstract 修饰,可有抽象方法和具体方法
- 接口:使用 interface 定义,默认方法为 public abstract(Java 8+ 支持默认实现)
- 继承关系:类只能单继承抽象类,但可实现多个接口
- 访问修饰符:抽象类可用 private/protected,接口成员默认 public
- 变量:抽象类有实例变量,接口只有静态常量
- 使用场景:抽象类用于共享代码,接口定义规范
9. 什么是重载(Overload)和重写(Override)?
区分两个相似但不同的概念
- 重载 (Overload):
- 同一类中,方法名相同,参数类型/个数/顺序不同
- 编译时确定,属于静态绑定
- 返回值类型可以不同(但不能仅通过返回值区分)
- 重写 (Override):
- 子类重新实现父类的方法,方法签名完全相同
- 运行时确定,属于动态绑定
- 返回值类型和异常必须兼容
10. 访问修饰符有哪些?作用范围是什么?
掌握 Java 的访问控制机制
- public:公有,所有类都可访问
- protected:受保护,同包和子类可访问
- default (无修饰符):默认,同包可访问
- private:私有,仅类内部可访问
- 访问范围对比:public > protected > default > private
- 最佳实践:最小化访问权限,遵循封装原则
💾 内存管理与垃圾回收
11. Java 内存结构包括哪些?
理解 JVM 的内存分配
- 堆 (Heap):存储对象实例,被所有线程共享,垃圾回收的主要区域,可配置大小
- 栈 (Stack):存储局部变量和方法调用,每个线程独有,自动释放内存
- 方法区 (Method Area):存储类的结构信息、运行时常量池、静态变量等,被所有线程共享
- 程序计数器:记录当前线程执行的字节码指令地址
- 本地方法栈:执行本地方法(C/C++ 代码)
12. 什么是垃圾回收?垃圾回收算法有哪些?
深入理解 GC 机制
- 垃圾回收定义:自动回收不再使用的对象占用的内存
- 主要算法:
- 标记清除:标记存活对象,清除垃圾,产生碎片
- 复制算法:分两块内存,清除时复制存活对象,无碎片但浪费内存
- 标记整理:标记后压缩整理,无碎片但性能较差
- 分代算法:对象分为新生代和老生代,不同代使用不同策略
- 优点:自动内存管理,避免内存泄漏
13. 堆和栈的区别?
对比两个重要的内存区域
- 存储内容:堆存对象,栈存基本类型和引用
- 线程:堆被所有线程共享,每个线程有独立栈
- 管理:堆由垃圾回收器管理,栈自动释放
- 大小:堆一般较大,栈相对较小
- 性能:栈分配速度快,堆分配相对慢
- 异常:堆溢出 OutOfMemoryError,栈溢出 StackOverflowError
14. 什么是内存泄漏?如何避免?
认识常见的内存问题
- 内存泄漏定义:程序申请的内存无法被释放,长期占用内存
- 常见原因:
- 长生命周期对象引用短生命周期对象
- 集合中的对象未及时清理
- 监听器或回调未取消注册
- 静态集合无限增长
- 避免方法:及时释放引用、使用 try-with-resources、定期检查内存使用
15. 什么是强引用、软引用、弱引用、虚引用?
理解 Java 的四种引用类型
- 强引用:普通引用,对象不被回收,直到无强引用
- 软引用 (SoftReference):内存不足时被回收,用于缓存
- 弱引用 (WeakReference):下次 GC 时被回收,用于 WeakHashMap
- 虚引用 (PhantomReference):任何时候都可被回收,必须与引用队列使用,用于跟踪对象回收
- 回收优先级:虚引用 > 弱引用 > 软引用 > 强引用
📦 集合框架
16. Java 集合框架的结构?
掌握集合框架的整体概念
- Collection 接口:
- List:有序可重复,如 ArrayList、LinkedList
- Set:无序不重复,如 HashSet、TreeSet
- Queue:队列,如 LinkedList、PriorityQueue
- Map 接口:
- 键值对映射:HashMap、TreeMap、ConcurrentHashMap
- 整体层次:Iterable → Collection/Map → 具体实现类
17. ArrayList 和 LinkedList 的区别?
对比两种常用列表实现
- 数据结构:ArrayList 基于数组,LinkedList 基于双向链表
- 随机访问:ArrayList O(1),LinkedList O(n)
- 插入删除:ArrayList O(n),LinkedList O(1)
- 内存占用:ArrayList 连续,LinkedList 分散(指针开销)
- 线程安全:都不同步,可用 Collections.synchronizedList() 或 CopyOnWriteArrayList
- 选择:频繁查询用 ArrayList,频繁插删用 LinkedList
18. HashMap 的原理和性能?
深入理解 HashMap 的工作机制
- 数据结构:数组 + 链表 + 红黑树(JDK 8+)
- 工作原理:hash(key) % table.length 计算数组下标,冲突时链接或树化
- 加载因子:默认 0.75,当已用容量 ≥ 容量 × 加载因子时扩容
- 扩容机制:容量翻倍,元素重新哈希定位
- 时间复杂度:平均 O(1),最差 O(n)(大量冲突时)
- 线程安全:不同步,多线程用 ConcurrentHashMap 或 Collections.synchronizedMap()
19. HashSet 如何保证不重复?
理解 Set 的去重机制
- 底层实现:基于 HashMap,key 为元素,value 为固定 Object
- 去重机制:先比较 hashCode(),再用 equals() 判断相等
- 添加流程:计算 hash → 检查是否存在 → 不存在添加 → 存在则忽略
- 自定义对象:必须重写 equals() 和 hashCode(),保证一致性
- 性能:添加、删除、查找平均 O(1),取决于 hash 质量
20. fail-fast 和 fail-safe 是什么?
理解集合的迭代器安全机制
- fail-fast:
- 迭代过程中修改集合会抛 ConcurrentModificationException
- 通过 modCount 和 expectedModCount 检测
- 如 ArrayList、HashMap(非线程安全)
- fail-safe:
- 迭代基于集合的快照或副本,修改原集合不影响迭代
- 如 CopyOnWriteArrayList、ConcurrentHashMap
- 使用建议:迭代时用迭代器的 remove(),或用 fail-safe 集合
⚠️ 异常处理
21. Java 异常体系?
理解 Java 异常的分类
- Throwable(根类):
- Exception(异常):可恢复的异常
- Error(错误):虚拟机级别错误,不可恢复
- Exception 分类:
- 检查异常 (Checked):必须捕获或声明,如 IOException
- 非检查异常 (Unchecked):可不捕获,如 NullPointerException、IndexOutOfBoundsException
- 常见异常:NPE、ClassCastException、ArrayIndexOutOfBoundsException 等
22. try-catch-finally 执行顺序?
掌握异常处理的执行流程
- 正常情况:try → finally → 正常返回
- 异常情况:try → 异常发生 → catch → finally → 异常传递或返回
- finally 特性:必定执行,即使 catch 中 return、throw、System.exit()
- 例外情况:finally 中 return 会覆盖 try/catch 的 return
- 资源释放:推荐使用 try-with-resources,自动关闭资源
- 最佳实践:不要在 finally 中改变返回值,会导致异常丢失
23. throws 和 throw 的区别?
区分两个异常处理关键字
- throw:
- 手动抛出异常实例,必须在方法内使用
- 格式:throw new Exception("message")
- throws:
- 在方法签名中声明可能抛出的异常
- 格式:public void method() throws IOException
- 处理方式:throw 由 throws 声明,throws 由调用者处理
- 应用:throw 用于具体异常处理,throws 用于异常传递
24. 如何自定义异常?
创建项目特定的异常类
- 继承关系:继承 Exception(检查异常)或 RuntimeException(非检查异常)
- 必要部分:
- 提供无参构造器
- 提供含 message 的构造器
- 提供含 message 和 cause 的构造器
- 代码示例:public class CustomException extends Exception { ... }
- 最佳实践:明确命名,清晰文档,继承合适的异常类
25. try-with-resources 语法的好处?
理解资源自动管理
- 语法:try (InputStream is = ...) { ... } 自动关闭资源
- 要求:资源必须实现 AutoCloseable 接口
- 优势:
- 自动调用 close() 方法,无需手动管理
- 异常被压制时能正确处理
- 代码更简洁,避免资源泄漏
- 适用场景:File、Stream、Connection、Statement 等资源类
🔤 String、StringBuilder、StringBuffer
26. String 的不可变性及其原因?
理解 String 设计的深层考虑
- 不可变定义:String 对象创建后无法被修改,任何修改操作返回新对象
- 实现方式:value 数组被 final 修饰,无 setter 方法
- 不可变原因:
- 字符串缓冲池优化,避免重复创建
- 线程安全,无需同步
- 支持 hashCode 缓存,适合做 HashMap key
- 缺点:频繁修改时会创建大量中间对象
27. String、StringBuilder、StringBuffer 的区别?
选择合适的字符串操作类
- String:不可变,线程安全,性能差(频繁修改)
- StringBuilder:可变,非线程安全,性能优(单线程)
- StringBuffer:可变,线程安全(同步方法),性能一般(多线程)
- 性能对比:StringBuilder > StringBuffer > String
- 使用建议:
- 单线程字符串拼接用 StringBuilder
- 多线程字符串拼接用 StringBuffer
- 不需修改用 String
28. 字符串常量池是什么?
理解字符串缓存机制
- 定义:JVM 维护的字符串缓存,存储字符串字面量
- 位置:JDK 7+ 在堆中,之前在方法区
- 创建机制:
- 字面量如 "abc" 自动进入常量池
- new String("abc") 若 "abc" 不在池中则添加
- intern() 方法:将字符串添加到常量池或返回现有引用
- 优化效果:节省内存,加快字符串比较
29. 如何比较字符串相等?
掌握字符串比较的方法
- == 比较:比较引用是否相同,不比较内容
- equals() 比较:比较字符串内容是否相同,推荐使用
- equalsIgnoreCase() 比较:忽略大小写比较内容
- compareTo() 比较:字典序比较,返回整数
- Objects.equals():处理 null 值的安全比较
- 最佳实践:比较字符串内容用 equals(),避免用 ==
30. String 拼接的性能问题?
优化字符串拼接的性能
- 问题:String s = "a" + "b" + "c" 产生多个中间对象和复制
- 原因:String 不可变,每次拼接都创建新对象
- 性能影响:循环拼接时 O(n²) 时间复杂度
- 优化方案:
- 直接 + 在循环外:编译器会优化为 StringBuilder
- 显式使用 StringBuilder 或 StringBuffer
- 使用 String.join()、StringJoiner 等工具
🔄 多线程与并发
31. 什么是线程?如何创建线程?
掌握线程的基本概念和创建方式
- 线程定义:进程内独立执行流,共享内存但有独立栈
- 创建方式:
- 继承 Thread:public class MyThread extends Thread { public void run() {} }
- 实现 Runnable:public class MyRunnable implements Runnable { public void run() {} }
- 实现 Callable:返回值和异常支持
- 区别:Runnable 推荐,避免单继承限制
- 启动:调用 thread.start(),不能直接调用 run()
32. 线程的生命周期和状态?
理解线程的各种状态转换
- NEW:线程创建但未启动
- RUNNABLE:可运行状态(等待执行或正在执行)
- BLOCKED:阻塞状态,等待获取锁
- WAITING:等待状态,等待其他线程通知
- TIMED_WAITING:等待指定时间
- TERMINATED:线程终止
- 状态转换:NEW → RUNNABLE → BLOCKED/WAITING → TERMINATED
33. synchronized 的工作原理?
理解 Java 的内置锁机制
- 同步机制:使用对象的内置锁实现同步
- 使用方式:
- 同步方法:public synchronized void method() {}
- 同步块:synchronized(obj) { ... }
- 工作原理:
- 每个对象都有一个监视器 (monitor)
- 线程获取锁进入临界区,互斥执行
- 字节码:monitorenter、monitorexit
- 特性:可重入,排他,自动释放
34. volatile 关键字的作用?
理解内存可见性和禁止指令重排
- 作用:
- 可见性:保证修改对其他线程立即可见
- 禁止重排:阻止编译器和 CPU 重排优化
- 实现:内存屏障,强制从主存读写
- 限制:不能保证原子性,不能替代 synchronized
- 应用场景:标志位、双检查单例、状态标记
- 对比 synchronized:volatile 更轻量但功能受限
35. wait()、notify()、notifyAll() 的作用和区别?
掌握线程间通信机制
- wait():
- 当前线程释放锁进入等待,直到被唤醒
- 必须在同步块内调用
- notify():
- 唤醒一个等待线程(随机选择)
- 不释放锁,等同步块结束才释放
- notifyAll():
- 唤醒所有等待线程
- 使用模式:Producer-Consumer 模式、管程模式
💡 面试准备建议
深化基础:不只知道概念,更要理解原理和实现细节
多做练习:通过编码实践巩固知识,特别是多线程和并发相关
阅读源码:研究 Java 库源码(集合、并发等)加深理解
关注细节:掌握常见陷阱和最佳实践,如字符串比较、集合修改等
举例说明:面试时用具体示例说明概念,增强表达力