简易版题目
📚 数据库题库答案
1、OSI 七层模型
- 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
- 功能:自下而上分别负责传输介质、链路控制、路由转发、端到端传输、会话管理、数据格式转换、应用服务。
2、TCP 的三次握手
- 客户端 → 服务端:SYN=1(请求建立连接)。
- 服务端 → 客户端:SYN=1, ACK=1(确认并同意建立)。
- 客户端 → 服务端:ACK=1(确认)。
👉 作用:确保双方具备收发能力,并同步初始序列号。
3、TCP 的四次挥手
- 客户端 → 服务端:FIN=1(我没数据了)。
- 服务端 → 客户端:ACK=1(收到)。
- 服务端 → 客户端:FIN=1(我也没数据了)。
- 客户端 → 服务端:ACK=1(收到)。
👉 双方各自独立关闭。
4、SQL 语言和方言的区别
- SQL 语言:标准 SQL(ANSI/ISO 定义),如
SELECT * FROM table;。 - SQL 方言:不同数据库厂商在标准 SQL 基础上扩展的特性,如 MySQL 的
LIMIT,Oracle 的ROWNUM。
5、SQL 语句的分类
- DDL:数据定义语言(CREATE、ALTER、DROP)。
- DML:数据操作语言(INSERT、UPDATE、DELETE)。
- DQL:数据查询语言(SELECT)。
- DCL:数据控制语言(GRANT、REVOKE)。
- TCL:事务控制语言(COMMIT、ROLLBACK、SAVEPOINT)。
6、MySQL 中 double 和 decimal 的区别
- double:浮点型,近似值,速度快,可能有精度丢失。
- decimal:定点型,精确值(常用于金额)。
7、NULL 值参与计算的结果是什么
- 任何运算只要有
NULL参与,结果就是NULL。
例:1 + NULL = NULL。
8、关系型数据库中,NULL 值参与排序的结果
- 升序(ASC):NULL 在最前面。
- 降序(DESC):NULL 在最后面。
(部分数据库可用NULLS FIRST | LAST明确指定)。
9、NULL 值是否参与聚合函数的计算
COUNT(*)会统计 NULL。COUNT(col)、SUM(col)、AVG(col)、MAX/MIN忽略 NULL。
10、WHERE 和 HAVING 的区别
- WHERE:对原始数据过滤,作用在分组前。
- HAVING:对分组结果过滤,作用在
GROUP BY之后。
11、数据库中约束的分类
- 实体完整性约束:主键约束、唯一约束。
- 域完整性约束:非空约束、默认值约束、检查约束。
- 参照完整性约束:外键约束。
12、主键约束和非空且唯一约束的区别
- 主键:唯一且非空,一个表只能有一个主键,可以由多个字段组成(复合主键)。
- 唯一+非空:可以保证唯一性,但一个表可以有多个这样的约束。
13、一对多关系怎么设置外键
- 在“多”的一方设置外键,引用“一”的一方的主键。
14、数据库设计的三大范式
- 第一范式(1NF):字段不可再分。
- 第二范式(2NF):在 1NF 基础上,非主属性完全依赖于主键。
- 第三范式(3NF):在 2NF 基础上,非主属性不依赖于其他非主属性。
15、内连接和左外连接的区别
- 内连接:只返回匹配上的数据。
- 左外连接:返回左表的全部数据,没有匹配的地方用 NULL 填充。
16、数据库中是否支持全连接
- MySQL 不支持
FULL OUTER JOIN,但可以用UNION(LEFT JOIN+RIGHT JOIN)模拟。 - Oracle、SQL Server 支持
FULL JOIN。
17、UNION ALL 和 UNION 的区别
- UNION:合并去重。
- UNION ALL:合并不去重,效率更高。
18、什么是事务
- 一组数据库操作,要么全部执行,要么全部不执行,是数据库保证数据一致性的基本单位。
19、事务是怎么保证同时成功或者同时失败的
- 依靠 日志(Redo Log、Undo Log) 和 事务控制语句(COMMIT/ROLLBACK)。
- 失败时回滚(Undo),成功时提交(Redo)。
20、事务的四大特征(ACID)
- 原子性:不可分割。
- 一致性:数据前后状态一致。
- 隔离性:并发事务互不干扰。
- 持久性:提交后数据永久保存。
21、事务的隔离级别及可能出现的问题
- 读未提交:可能出现脏读。
- 读已提交:可能出现不可重复读。
- 可重复读(MySQL 默认):可能出现幻读。
- 串行化:无并发问题,但性能最差。
22、慢查询的作用是什么
- 帮助定位性能瓶颈,找出执行时间超过阈值的 SQL,用于优化数据库。
23、使用模糊查询时是否会导致索引失效
LIKE '%abc'前缀模糊查询会导致索引失效。LIKE 'abc%'可以利用索引。
24、联合索引走索引的条件是什么
- 遵循 最左前缀原则:必须从索引最左列开始匹配。
例:索引(a,b,c),可以用a,a,b,a,b,c,但不能只用b或c。
25、索引的底层结构是什么
- B+ 树(最常见,MySQL InnoDB)。
- 其他:哈希索引、全文索引、空间索引(较少用)。
26、B 树和 B+ 树的区别
- B 树:每个节点既存储键,也存储数据。
- B+ 树:数据只存储在叶子节点,非叶子节点只存储索引;叶子节点有链表,方便范围查询。
👉 B+ 树更适合数据库。
☕ Java 基础题库答案
27、final 特点
- 修饰类:类不可被继承。
- 修饰方法:方法不可被重写。
- 修饰变量:变量成为常量,只能赋值一次。
- 修饰引用类型:引用地址不可变,但对象内容可变。
28、static 的作用和特点
- 修饰变量 → 静态变量,属于类,共享一份存储。
- 修饰方法 → 静态方法,不依赖对象,可以通过类名调用。
- 修饰代码块 → 静态代码块,类加载时执行一次。
- 特点:先于对象存在,生命周期随类而生灭。
29、静态代码块、构造代码块、构造方法的执行顺序和特点
- 执行顺序:
静态代码块 → 构造代码块 → 构造方法。 - 特点:
- 静态代码块:类加载时执行一次。
- 构造代码块:每次创建对象时都会执行。
- 构造方法:每次创建对象时执行,用于初始化对象。
30、多态的前提
- 必须有继承或实现关系。
- 必须有方法重写。
- 必须有父类引用指向子类对象。
31、多态中的成员访问特点
- 成员变量:编译看左边(父类),运行看左边。
- 成员方法:编译看左边,运行看右边(动态绑定)。
- 静态方法:编译和运行都看左边(属于类)。
32、抽象类的特点
- 用
abstract修饰的类,不能实例化。 - 可以包含抽象方法,也可以有普通方法。
- 可以有构造方法。
- 必须被继承,子类实现抽象方法后才能实例化。
33、抽象类的成员特点
- 成员变量:可以是任意修饰符(public、protected、private)。
- 成员方法:既可以是抽象方法,也可以是普通方法。
- 构造方法:可以有,用于子类初始化。
34、接口中成员变量和成员方法的默认修饰符
- 成员变量:默认
public static final(常量)。 - 成员方法:默认
public abstract(抽象方法)。 - JDK 8+:允许有
default和static方法。 - JDK 9+:允许有
private方法(仅接口内部使用)。
35、接口的成员特点
- 变量:
public static final。 - 方法:
- 抽象方法(默认
public abstract)。 - 默认方法(
default,可以有方法体)。 - 静态方法(
static,只能用接口名调用)。 - 私有方法(
private,JDK 9 引入,用于接口内部复用)。
- 抽象方法(默认
36、接口和抽象类的区别
| 比较点 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class |
interface |
| 方法 | 可有抽象方法和普通方法 | 默认抽象方法,JDK8 以后支持 default & static |
| 变量 | 普通变量和常量 | 只能有常量 |
| 继承 | 只能单继承 | 可多实现 |
| 构造方法 | 可以有 | 不能有 |
| 设计目的 | 代码复用 | 规范约束 |
37、多态的三种方式
- 继承 + 方法重写。
- 实现接口。
- 抽象类的子类实现。
38、内部类的分类
- 成员内部类(定义在类中,和成员变量同级)。
- 静态内部类(用
static修饰,不依赖外部类实例)。 - 局部内部类(定义在方法中)。
- 匿名内部类(没有名字的内部类,多用于回调)。
39、匿名内部类的前提和本质
- 前提:必须继承一个类或实现一个接口。
- 本质:编译器生成一个没有名字的子类,并立即创建对象。
40、Object 类中有哪些常见的方法
toString():返回对象字符串表示。equals():比较对象是否相等。hashCode():返回哈希值。clone():对象克隆(需实现Cloneable)。finalize():垃圾回收前调用(已过时)。wait()/notify()/notifyAll():线程通信方法。getClass():获取运行时类对象。
好的 ✅
那我现在给你写 集合与源码机制(41–67 题) 的逐题答案,每道题都附上关键点总结,方便你复习。
📚 集合与源码机制题库答案
41. ArrayList 底层实现原理
- 底层是 动态数组,默认容量 10。
- 当容量不足时,会按
1.5 倍扩容。 - 支持随机访问,查找快(O(1)),插入/删除慢(O(n))。
42. ArrayList 扩容机制
- 默认容量 10。
- 新容量 = 旧容量 + (旧容量 >> 1) = 1.5 倍。
- 通过
Arrays.copyOf拷贝到新数组。
43. LinkedList 底层实现原理
- 底层是 双向链表。
- 每个节点存储
prev、next、item。 - 插入/删除快(O(1)),随机访问慢(O(n))。
44. ArrayList 和 LinkedList 区别
- ArrayList:底层数组,随机访问快,插入删除慢。
- LinkedList:底层链表,插入删除快,随机访问慢。
- 内存利用:ArrayList 连续内存,LinkedList 分散存储。
45. HashMap 底层实现原理
- JDK7:数组 + 链表。
- JDK8:数组 + 链表 + 红黑树(链表长度 ≥ 8 转为红黑树)。
- 通过 hash 值 + 取模运算 定位数组下标。
- 解决哈希冲突:拉链法。
46. HashMap 默认容量 & 负载因子
- 默认容量:16
- 负载因子:0.75
- 阈值:容量 × 负载因子
47. HashMap 扩容机制
- 当元素个数 > 阈值时,扩容为 2 倍。
- JDK8 之后扩容时会优化节点迁移,减少冲突。
48. HashMap 为什么容量必须是 2 的幂次方
- 计算下标时用
(n - 1) & hash。 - 这样比取模运算快,并且分布更均匀。
49. HashMap put 流程
- 计算 key 的 hash。
- 定位数组下标。
- 若位置为空 → 新建节点。
- 若存在节点 → 判断是否相等:
- 相等 → 覆盖 value。
- 不相等 → 拉链/红黑树插入。
- 如果 size 超过阈值 → 扩容。
50. HashMap get 流程
- 计算 key 的 hash,找到数组下标。
- 遍历链表/红黑树,比较 key。
- 返回 value。
51. HashMap 如何解决哈希冲突
- 采用 链地址法(拉链法)。
- JDK8 引入红黑树,提升查询效率。
52. HashMap 与 Hashtable 区别
- HashMap:线程不安全,允许
null key / null value。 - Hashtable:线程安全(synchronized),不允许 null。
53. HashMap 与 ConcurrentHashMap 区别
- HashMap:线程不安全。
- ConcurrentHashMap:线程安全,JDK7 用分段锁,JDK8 用 CAS + synchronized。
54. ConcurrentHashMap 底层实现
- JDK7:Segment + HashEntry(分段锁)。
- JDK8:Node + CAS + synchronized,链表转红黑树。
55. HashSet 底层实现原理
- 基于 HashMap 实现。
- 元素存入 HashMap 的 key,value 用一个常量
Object.PRESENT。
56. TreeSet 底层实现原理
- 基于 TreeMap(红黑树)实现。
- 元素必须实现
Comparable或传入Comparator。
57. HashMap 为什么线程不安全
- 多线程 put 可能覆盖,扩容可能死循环(JDK7)。
- 导致数据丢失或结构异常。
58. HashMap 死循环问题
- 出现在 JDK7 并发扩容时。
- 链表迁移时头插法,可能形成环,导致 get/put 死循环。
59. fail-fast 机制
- 迭代器遍历时,如果集合被结构性修改,会抛出
ConcurrentModificationException。 - 依赖
modCount判断。
60. fail-safe 机制
- 基于 副本 实现(如
CopyOnWriteArrayList、ConcurrentHashMap)。 - 修改不会影响原集合,不会抛异常。
61. HashMap 的 get() 能否判断 key 是否存在?
- 不能。因为返回 null 可能是:
- key 不存在
- key 存在但 value 为 null
- 判断应使用
containsKey()。
62. HashMap 线程安全替代方案
ConcurrentHashMap(推荐)Collections.synchronizedMap(new HashMap<>())(性能差)。
63. CopyOnWriteArrayList 底层原理
- 每次写操作都会复制新数组。
- 读操作无锁,写操作加锁。
- 适合 读多写少 场景。
64. ArrayList 与 Vector 区别
- ArrayList:线程不安全,扩容 1.5 倍。
- Vector:线程安全(方法加 synchronized),扩容 2 倍。
65. BlockingQueue 常见实现
- ArrayBlockingQueue:数组结构,固定容量。
- LinkedBlockingQueue:链表结构,容量可指定。
- PriorityBlockingQueue:带优先级。
- DelayQueue:延时队列。
66. WeakHashMap 底层原理
- key 使用 弱引用。
- 当 key 没有强引用时,GC 会回收该 entry。
67. EnumMap 底层实现
- key 必须是 enum 类型。
- 底层是一个数组,用枚举的
ordinal()作为索引,效率很高。
好的 ✅
那我接着帮你整理 68–80(多线程与并发相关) 的题目答案。
⚡ Java 并发与多线程
68. File 和 IO 的区别
File:表示文件或目录路径,不涉及数据读写。IO:负责文件内容的读写操作(InputStream / OutputStream / Reader / Writer)。
69. flush 和 close 的区别
flush():强制把缓冲区的数据写入目的地,但流仍然可用。close():先执行 flush,然后关闭流,流不可再用。
70. IO 流中的相对路径和绝对路径
- 绝对路径:从磁盘根目录开始的完整路径,如
C:/test/a.txt。 - 相对路径:相对于项目运行的工作目录(
user.dir)。
71. 程序、进程、线程、协程的区别
- 程序:静态的代码和数据集合。
- 进程:程序的执行实例,资源分配的最小单位。
- 线程:进程中的执行单元,CPU 调度的最小单位。
- 协程:用户态的轻量级线程,切换开销更低。
72. 并发和并行的区别
- 并发:一个 CPU 同时处理多个任务(宏观上同时,微观上切换)。
- 并行:多个 CPU 同时真正地执行多个任务。
73. run 和 start 的区别
run():普通方法调用,不会开启新线程。start():启动新线程,由 JVM 调用run()。
74. 描述线程的生命周期
- 新建(New):
new Thread()。 - 就绪(Runnable):调用
start(),等待 CPU 调度。 - 运行(Running):CPU 调度执行。
- 阻塞/等待(Blocked/Waiting/Timed Waiting):等待资源或条件。
- 终止(Terminated):线程执行完毕或异常退出。
75. HashMap 和 Hashtable 的区别
- HashMap:线程不安全,效率高,允许 null key 和 null value。
- Hashtable:线程安全(方法加 synchronized),不允许 null。
76. volatile 关键字的作用
- 保证 内存可见性(一个线程修改后,其他线程立即可见)。
- 禁止指令重排,保证一定的有序性。
- 不保证原子性。
77. 使用线程池的好处
- 避免频繁创建和销毁线程,降低开销。
- 控制最大并发数,防止资源耗尽。
- 提高线程的可管理性和可扩展性。
78. ThreadPoolExecutor 的七大参数
corePoolSize:核心线程数(常驻)。maximumPoolSize:最大线程数。keepAliveTime:非核心线程的存活时间。unit:存活时间的单位。workQueue:任务队列。threadFactory:线程工厂(自定义线程名等)。handler:拒绝策略。
79. ThreadPoolExecutor 线程池的工作原理
- 任务提交 → 判断线程数是否 < corePoolSize → 创建新线程。
- 否则 → 放入任务队列。
- 队列满 → 若线程数 < maximumPoolSize → 创建新线程。
- 若已达最大线程数 → 执行拒绝策略。
80. 实现多线程的方式
- 继承
Thread类,重写run()。 - 实现
Runnable接口,重写run()。 - 实现
Callable<V>接口,重写call(),可返回结果,配合FutureTask。 - 使用线程池
ExecutorService提交任务。
Java 基础 & OOP 题库答案
1、Java 的版本分类有哪些
- Java SE(Standard Edition):标准版,提供核心语法和 API。
- Java EE(Enterprise Edition):企业版,基于 SE,增加 Web、分布式开发支持。
- Java ME(Micro Edition):微型版,面向嵌入式、移动设备。
2、Java 中跨平台的原理是什么
- 原理:一次编译,到处运行。
.java → .class (字节码),由 JVM 在不同系统上解释/执行。
3、JDK、JRE 和 JVM 的区别
- JVM:Java 虚拟机,执行字节码。
- JRE:运行环境 = JVM + 核心类库。
- JDK:开发工具包 = JRE + 编译器
javac+ 调试工具等。
4、Java 中的注释有哪几种,分别怎么表示
- 单行注释:
// - 多行注释:
/* ... */ - 文档注释(javadoc):
/** ... */
5、Java 中常量的分类
- 字面值常量:如
123、"hello"。 - 符号常量:
final修饰的变量。
6、Java 中的数据类型有哪些
- 基本类型(8 个):
- 整数:
byte, short, int, long - 浮点:
float, double - 字符:
char - 布尔:
boolean
- 整数:
- 引用类型:类、接口、数组。
7、a++ 和 ++a 的区别
a++:先用后加(返回旧值,再自增)。++a:先加后用(先自增,返回新值)。
8、为什么要使用 IDEA
- 强大的 IDE,支持智能提示、调试、Maven/Gradle 集成、Spring 支持、快捷键高效。
9、方法的分类
- 按是否有返回值:有返回值 / 无返回值(
void)。 - 按访问权限:
public/protected/private。 - 按修饰符:实例方法 / 静态方法 / 抽象方法 / final 方法。
10、什么是方法重载
- 同一类中,方法名相同,参数列表不同(个数 / 类型 / 顺序),与返回值无关。
11、switch 小括号里的表达式可以是哪些数据类型
- 允许:
byte, short, int, char, enum, String(JDK7+)。 - 不允许:
long, float, double, boolean。
12、数组的特点
- 长度固定,不可变。
- 元素类型相同,内存连续。
- 支持随机访问(下标 O(1))。
13、数组的动态创建方式和静态创建方式的区别
- 静态初始化:
int[] a = {1, 2, 3};(定义+赋值同时进行)。 - 动态初始化:
int[] a = new int[3];(只分配空间,值为默认值)。
14、Java 中的内存划分
- 栈:方法调用、局部变量。
- 堆:对象实例、数组。
- 方法区(元空间):类信息、常量池、静态变量。
- 程序计数器:线程执行位置。
- 本地方法栈:JNI 调用。
15、面向对象和面向过程的区别
- 面向过程:关注步骤和流程。
- 面向对象:关注对象及其行为,强调封装、继承、多态。
16、什么是类?什么是对象?
- 类:对象的抽象(模板/蓝图)。
- 对象:类的实例,具体存在。
17、类和对象的关系
- 类是抽象概念,对象是具体实例。
- 类定义属性/行为,对象存储数据并调用方法。
18、成员变量和局部变量的区别
| 对比项 | 成员变量 | 局部变量 |
|---|---|---|
| 定义位置 | 类中,方法外 | 方法内、参数列表中 |
| 生命周期 | 随对象存在 | 随方法执行 |
| 默认值 | 有默认值 | 没有默认值,必须赋值 |
| 修饰符 | 可加权限修饰符 | 不可加权限修饰符 |
19、面向对象的三大特征
- 封装:隐藏细节,对外暴露接口。
- 继承:子类复用父类代码。
- 多态:同一接口,不同实现(编译时多态:重载;运行时多态:重写)。
20、封装的作用和原则
- 作用:隐藏内部实现,保证安全性和可维护性。
- 原则:
- 属性私有化(
private)。 - 提供
getter/setter控制访问。 - 对外暴露有限接口。
- 属性私有化(
Java 基础 & OOP
21、给成员变量赋值的方式有哪些
- 定义时直接赋值
- 构造方法赋值
- set 方法赋值
- 代码块赋值(静态/构造代码块)
22、继承的好处和缺点
- 好处:代码复用、结构清晰、扩展方便。
- 缺点:父类改动会影响子类,过度继承会造成耦合度高。
23、继承中成员变量、构造方法、成员方法的访问特点
- 成员变量:就近原则(子类和父类同名时,默认访问子类的)。
- 构造方法:不能被继承,但子类会通过
super()调用父类构造方法。 - 成员方法:子类重写覆盖父类方法;父类引用指向子类对象时,调用子类重写的方法(多态)。
24、方法重载和方法重写的区别
- 重载(Overload):同一类中,方法名相同,参数列表不同。
- 重写(Override):子类对父类方法进行改写,方法签名相同。
25、package、import、class 的顺序关系
- 顺序必须是:
package(最多一个)import(可以多个)class(类定义)
26、四种权限修饰符
| 修饰符 | 同类 | 同包 | 子类 | 其他包 |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | ❌ |
| 默认(不写) | ✅ | ✅ | ❌ | ❌ |
| private | ✅ | ❌ | ❌ | ❌ |
27、final 特点
- 类:不能被继承。
- 方法:不能被重写。
- 变量:值不能改变(常量)。
28、static 的作用和特点
- 作用:表示静态资源,属于类,不属于对象。
- 特点:
- 静态变量:所有对象共享。
- 静态方法:只能访问静态成员,不能访问
this。 - 静态代码块:类加载时执行一次。
29、静态代码块、构造代码块、构造方法的执行顺序和特点
- 顺序:静态代码块(只执行一次) → 构造代码块(每次 new 执行) → 构造方法。
- 特点:
- 静态代码块:初始化类信息。
- 构造代码块:对象公共初始化逻辑。
- 构造方法:对象个性化初始化逻辑。
30、多态的前提
- 继承或实现关系。
- 子类重写父类方法。
- 父类引用指向子类对象。
31、多态中的成员访问特点
- 成员变量:编译看左边(父类),运行也看左边。
- 成员方法:编译看左边(父类),运行看右边(子类,动态绑定)。
- 静态方法:不具备多态性,编译和运行都看左边。
32、抽象类的特点
- 用
abstract修饰。 - 不能直接实例化。
- 可以有构造方法、成员变量、普通方法、抽象方法。
33、抽象类的成员特点
- 成员变量:既可有普通变量,也可有常量。
- 构造方法:可以有。
- 成员方法:既可以有普通方法,也可以有抽象方法。
34、接口中成员变量和成员方法的默认修饰符
- 变量:
public static final(常量)。 - 方法(JDK8 之前):
public abstract。 - JDK8 新增:
default默认方法、static静态方法。 - JDK9 新增:
private方法。
35、接口的成员特点
- 只能包含常量和抽象方法(JDK8 之前)。
- 接口中的方法默认
public abstract,变量默认public static final。 - 支持多继承。
36、接口和抽象类的区别
| 对比项 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class |
interface |
| 继承 | 单继承 | 多继承 |
| 成员变量 | 普通变量 + 常量 | 只能常量 |
| 成员方法 | 普通方法 + 抽象方法 | 默认抽象(JDK8+ 可有 default、static) |
| 构造方法 | 可以有 | 不能有 |
37、多态的三种方式
- 继承 + 方法重写
- 接口实现
- 抽象类实现
38、内部类的分类
- 成员内部类(定义在类中,方法外)。
- 静态内部类(
static修饰)。 - 局部内部类(方法内部定义)。
- 匿名内部类(没有类名,直接创建实例)。
39、匿名内部类的前提和本质
- 前提:必须继承一个类或实现一个接口。
- 本质:继承父类/实现接口的子类匿名对象。
40、Object 类中有哪些常见的方法
toString():返回对象字符串表示。equals(Object obj):判断对象是否相等。hashCode():返回对象哈希值。clone():对象克隆(需实现Cloneable接口)。finalize():垃圾回收前调用(已过时)。getClass():获取运行时类对象。wait()/notify()/notifyAll():线程通信。
好的 ✅
那我接着帮你整理 41–67(集合 & 源码机制) 的答案。
集合 & 源码机制
41、Object类中的finalize方法的作用
- 在垃圾回收器回收对象之前调用,用于资源释放。
- 但不可靠,可能不会被调用(已被废弃)。
42、Java中垃圾回收机制(gc方法)的原理
System.gc()只是建议 JVM 进行 GC,真正是否执行由 JVM 决定。- 垃圾回收机制通过可达性分析(GC Roots)判断对象是否存活。
43、基本数据类型和包装类的对应关系
- byte → Byte
- short → Short
- int → Integer
- long → Long
- float → Float
- double → Double
- char → Character
- boolean → Boolean
44、基本数据类型怎么转字符串?字符串怎么转基本数据类型?
- 基本类型 → 字符串:
String.valueOf(x)或x + ""。 - 字符串 → 基本类型:
Integer.parseInt("123")、Double.parseDouble("3.14")等。
45、length和length()、size()的区别
length:数组长度。length():字符串长度(String的方法)。size():集合大小(List/Set/Map)。
46、String、StringBuffer、StringBuilder的区别
- String:不可变,每次修改生成新对象。
- StringBuffer:可变,线程安全(同步)。
- StringBuilder:可变,线程不安全,效率更高。
47、编译期异常和运行期异常的区别
- 编译期异常(Checked Exception):必须处理,否则编译不通过。例:IOException。
- 运行期异常(RuntimeException):可不处理,运行时可能抛出。例:NullPointerException。
48、try—catch和throws的区别
try-catch:在方法内部捕获并处理异常。throws:在方法声明处抛出异常,交给调用者处理。
49、throw和throws的区别
throw:方法内部,抛出异常对象。throws:方法声明处,声明可能抛出的异常类型。
50、设计模式的七大原则是什么
- 单一职责原则
- 开放封闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖倒置原则
- 合成复用原则
- 迪米特法则
51、Java中对方法的增强的方式有哪些
- 继承重写
- 装饰器模式
- 动态代理(JDK Proxy、CGLIB)
- AOP(Spring 面向切面编程)
52、集合和数组的区别是什么
- 数组:长度固定、存储单一数据类型。
- 集合:长度可变、存储对象,提供丰富的操作方法。
53、在List集合中删除元素的注意事项
- 遍历时删除需注意:
- for-each 删除会抛
ConcurrentModificationException。 - 解决:用
Iterator.remove()或ListIterator。
- for-each 删除会抛
54、ArrayList、LinkedList、Vector的区别
- ArrayList:基于数组,查询快,增删慢,线程不安全。
- LinkedList:基于双向链表,增删快,查询慢,线程不安全。
- Vector:基于数组,线程安全(方法加
synchronized),效率低。
55、ArrayList集合底层结构是什么
- 动态数组(
Object[])。
56、ArrayList集合的扩容机制
- 初始容量:10。
- 每次扩容:
1.5 倍(即 oldCapacity + oldCapacity >> 1)。
57、HashSet、LinkedHashSet、TreeSet集合的底层结构
- HashSet:基于 HashMap。
- LinkedHashSet:基于 LinkedHashMap,保证插入顺序。
- TreeSet:基于 TreeMap,红黑树实现,保证排序。
58、HashSet集合是怎么保证元素唯一性的
- 底层依赖 HashMap 的 key 唯一性。
- 判断逻辑:先比较
hashCode(),再比较equals()。
59、TreeSet集合是怎么保证元素唯一性的
- 依赖元素的
compareTo()或Comparator。 - 比较结果为 0,认为元素相等,不会存入。
60、可变参数的本质是什么
- 本质是一个数组。
- 方法内部把传入的参数打包成数组。
61、HashMap集合的底层结构
- JDK1.7:数组 + 链表。
- JDK1.8:数组 + 链表 + 红黑树。
62、HashMap集合的无参构造,在第一次添加元素的时候数组的初始大小
- 16(默认容量)。
63、HashMap集合加载因子为什么是0.75
- 在时间和空间利用率之间的折中。
- 较低:浪费空间。
- 较高:冲突多,效率下降。
64、HashMap集合中链表什么时候转红黑树
- 当链表长度 ≥ 8 且数组长度 ≥ 64 时,转为红黑树。
65、HashMap集合中红黑树什么时候转链表
- 当红黑树节点数 ≤ 6 时,退化为链表。
66、HashMap集合中数组的扩容为什么都是2的n次幂
- 计算下标时使用按位与(
(n - 1) & hash),比取余效率高。
67、HashMap集合添加元素的时候,计算数组下标为什么不使用取余,而使用按位与
- 按位与比取余运算快。
- 需要数组长度为 2 的幂,保证
(n - 1) & hash等价于hash % n。
好的 ✅
那我接着帮你写 68–80(多线程与并发) 的详细答案。
多线程与并发
68、File 和 IO 的区别
File:仅表示文件或目录的路径信息,不涉及读写内容。IO:真正执行输入/输出操作的流,用于读写文件数据。
69、flush 和 close 的区别
flush():强制将缓冲区数据写入目标,但流仍然可用。close():先flush(),再关闭流,流关闭后不能再使用。
70、IO流中的相对路径和绝对路径
- 绝对路径:从根目录开始,如
C:/test/a.txt或/usr/local/a.txt。 - 相对路径:相对于项目的工作目录(
user.dir),如./data/a.txt。
71、程序、进程、线程、协程的区别
- 程序:静态的代码集合。
- 进程:程序的一次执行,资源分配的最小单位。
- 线程:进程中的执行单元,CPU 调度的最小单位。
- 协程:用户态的轻量级线程,切换效率更高。
72、并发和并行的区别
- 并发:一个 CPU 同时处理多个任务(宏观上同时,微观上切换)。
- 并行:多个 CPU 同时真正执行多个任务。
73、run 和 start 的区别
run():普通方法调用,不会创建新线程。start():启动一个新线程,由 JVM 调用run()方法。
74、描述线程的生命周期
- 新建(New):创建 Thread 对象。
- 就绪(Runnable):调用
start(),等待 CPU 调度。 - 运行(Running):获得 CPU 执行
run()方法。 - 阻塞/等待(Blocked/Waiting/Timed Waiting):等待资源或超时等待。
- 终止(Terminated):线程执行完毕或异常结束。
75、HashMap 和 Hashtable 的区别
- HashMap:线程不安全,效率高,允许
null键和值。 - Hashtable:线程安全(方法加
synchronized),不允许null键或值。
76、volatile 关键字的作用
- 保证 内存可见性:线程修改后,其他线程能立即看到。
- 禁止 指令重排序,保证一定有序性。
- 不保证 原子性。
77、使用线程池的好处是什么
- 降低线程频繁创建/销毁的开销。
- 控制并发线程数,防止资源耗尽。
- 统一管理线程,提高可扩展性和稳定性。
78、ThreadPoolExecutor 的七大参数
corePoolSize:核心线程数,常驻线程数。maximumPoolSize:最大线程数。keepAliveTime:非核心线程空闲存活时间。unit:时间单位。workQueue:任务队列。threadFactory:线程工厂(可自定义线程名)。handler:拒绝策略(丢弃、抛异常、调用者执行等)。
79、ThreadPoolExecutor 线程池的工作原理
- 提交任务 → 判断运行线程数是否小于
corePoolSize,若是则创建线程执行。 - 否则 → 任务进入队列
workQueue。 - 队列满 → 若线程数 <
maximumPoolSize,创建非核心线程执行任务。 - 若已达最大线程数且队列满 → 执行拒绝策略。
80、实现多线程的方式
- 继承
Thread类,重写run()。 - 实现
Runnable接口,重写run()。 - 实现
Callable<V>接口,重写call(),配合FutureTask。 - 使用线程池
ExecutorService提交任务。
经典的线程通信案例——生产者消费者模式,使用 wait() 和 notify() 来实现线程通
生产者消费者案例
1 | class Product { |
核心点解析
- 共享资源:
Product类中的count表示库存。 - 同步机制:
synchronized确保同一时间只有一个线程访问资源。 - 线程通信:
wait():线程等待,释放锁,进入等待队列。notifyAll():唤醒等待线程,让其重新竞争锁。
- 生产/消费逻辑:
- 当库存满时,生产者阻塞等待。
- 当库存空时,消费者阻塞等待。
这个案例是面试中非常经典的 多线程通信问题,可以扩展为 阻塞队列 BlockingQueue 版本,效率更高且无需手动管理 wait/notify。
