📚 数据库题库答案

1、OSI 七层模型

  • 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
  • 功能:自下而上分别负责传输介质、链路控制、路由转发、端到端传输、会话管理、数据格式转换、应用服务。

2、TCP 的三次握手

  1. 客户端 → 服务端:SYN=1(请求建立连接)。
  2. 服务端 → 客户端:SYN=1, ACK=1(确认并同意建立)。
  3. 客户端 → 服务端:ACK=1(确认)。
    👉 作用:确保双方具备收发能力,并同步初始序列号。

3、TCP 的四次挥手

  1. 客户端 → 服务端:FIN=1(我没数据了)。
  2. 服务端 → 客户端:ACK=1(收到)。
  3. 服务端 → 客户端:FIN=1(我也没数据了)。
  4. 客户端 → 服务端: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、数据库设计的三大范式

  1. 第一范式(1NF):字段不可再分。
  2. 第二范式(2NF):在 1NF 基础上,非主属性完全依赖于主键。
  3. 第三范式(3NF):在 2NF 基础上,非主属性不依赖于其他非主属性。

15、内连接和左外连接的区别

  • 内连接:只返回匹配上的数据。
  • 左外连接:返回左表的全部数据,没有匹配的地方用 NULL 填充。

16、数据库中是否支持全连接

  • MySQL 不支持 FULL OUTER JOIN,但可以用 UNIONLEFT 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)

  1. 原子性:不可分割。
  2. 一致性:数据前后状态一致。
  3. 隔离性:并发事务互不干扰。
  4. 持久性:提交后数据永久保存。

21、事务的隔离级别及可能出现的问题

  • 读未提交:可能出现脏读。
  • 读已提交:可能出现不可重复读。
  • 可重复读(MySQL 默认):可能出现幻读。
  • 串行化:无并发问题,但性能最差。

22、慢查询的作用是什么

  • 帮助定位性能瓶颈,找出执行时间超过阈值的 SQL,用于优化数据库。

23、使用模糊查询时是否会导致索引失效

  • LIKE '%abc' 前缀模糊查询会导致索引失效。
  • LIKE 'abc%' 可以利用索引。

24、联合索引走索引的条件是什么

  • 遵循 最左前缀原则:必须从索引最左列开始匹配。
    例:索引 (a,b,c),可以用 aa,ba,b,c,但不能只用 bc

25、索引的底层结构是什么

  • B+ 树(最常见,MySQL InnoDB)。
  • 其他:哈希索引、全文索引、空间索引(较少用)。

26、B 树和 B+ 树的区别

  • B 树:每个节点既存储键,也存储数据。
  • B+ 树:数据只存储在叶子节点,非叶子节点只存储索引;叶子节点有链表,方便范围查询。
    👉 B+ 树更适合数据库。

☕ Java 基础题库答案

27、final 特点

  • 修饰类:类不可被继承。
  • 修饰方法:方法不可被重写。
  • 修饰变量:变量成为常量,只能赋值一次。
  • 修饰引用类型:引用地址不可变,但对象内容可变。

28、static 的作用和特点

  • 修饰变量 → 静态变量,属于类,共享一份存储。
  • 修饰方法 → 静态方法,不依赖对象,可以通过类名调用。
  • 修饰代码块 → 静态代码块,类加载时执行一次。
  • 特点:先于对象存在,生命周期随类而生灭。

29、静态代码块、构造代码块、构造方法的执行顺序和特点

  • 执行顺序:
    静态代码块 → 构造代码块 → 构造方法
  • 特点:
    • 静态代码块:类加载时执行一次。
    • 构造代码块:每次创建对象时都会执行。
    • 构造方法:每次创建对象时执行,用于初始化对象。

30、多态的前提

  1. 必须有继承或实现关系。
  2. 必须有方法重写。
  3. 必须有父类引用指向子类对象。

31、多态中的成员访问特点

  • 成员变量:编译看左边(父类),运行看左边。
  • 成员方法:编译看左边,运行看右边(动态绑定)。
  • 静态方法:编译和运行都看左边(属于类)。

32、抽象类的特点

  • abstract 修饰的类,不能实例化。
  • 可以包含抽象方法,也可以有普通方法。
  • 可以有构造方法。
  • 必须被继承,子类实现抽象方法后才能实例化。

33、抽象类的成员特点

  • 成员变量:可以是任意修饰符(public、protected、private)。
  • 成员方法:既可以是抽象方法,也可以是普通方法。
  • 构造方法:可以有,用于子类初始化。

34、接口中成员变量和成员方法的默认修饰符

  • 成员变量:默认 public static final(常量)。
  • 成员方法:默认 public abstract(抽象方法)。
  • JDK 8+:允许有 defaultstatic 方法。
  • JDK 9+:允许有 private 方法(仅接口内部使用)。

35、接口的成员特点

  • 变量:public static final
  • 方法:
    • 抽象方法(默认 public abstract)。
    • 默认方法(default,可以有方法体)。
    • 静态方法(static,只能用接口名调用)。
    • 私有方法(private,JDK 9 引入,用于接口内部复用)。

36、接口和抽象类的区别

比较点 抽象类 接口
关键字 abstract class interface
方法 可有抽象方法和普通方法 默认抽象方法,JDK8 以后支持 default & static
变量 普通变量和常量 只能有常量
继承 只能单继承 可多实现
构造方法 可以有 不能有
设计目的 代码复用 规范约束

37、多态的三种方式

  1. 继承 + 方法重写。
  2. 实现接口。
  3. 抽象类的子类实现。

38、内部类的分类

  1. 成员内部类(定义在类中,和成员变量同级)。
  2. 静态内部类(用 static 修饰,不依赖外部类实例)。
  3. 局部内部类(定义在方法中)。
  4. 匿名内部类(没有名字的内部类,多用于回调)。

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 底层实现原理

  • 底层是 双向链表
  • 每个节点存储 prevnextitem
  • 插入/删除快(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 流程

  1. 计算 key 的 hash。
  2. 定位数组下标。
  3. 若位置为空 → 新建节点。
  4. 若存在节点 → 判断是否相等:
    • 相等 → 覆盖 value。
    • 不相等 → 拉链/红黑树插入。
  5. 如果 size 超过阈值 → 扩容。

50. HashMap get 流程

  1. 计算 key 的 hash,找到数组下标。
  2. 遍历链表/红黑树,比较 key。
  3. 返回 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 机制

  • 基于 副本 实现(如 CopyOnWriteArrayListConcurrentHashMap)。
  • 修改不会影响原集合,不会抛异常。

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. 描述线程的生命周期

  1. 新建(New)new Thread()
  2. 就绪(Runnable):调用 start(),等待 CPU 调度。
  3. 运行(Running):CPU 调度执行。
  4. 阻塞/等待(Blocked/Waiting/Timed Waiting):等待资源或条件。
  5. 终止(Terminated):线程执行完毕或异常退出。

75. HashMap 和 Hashtable 的区别

  • HashMap:线程不安全,效率高,允许 null key 和 null value。
  • Hashtable:线程安全(方法加 synchronized),不允许 null。

76. volatile 关键字的作用

  • 保证 内存可见性(一个线程修改后,其他线程立即可见)。
  • 禁止指令重排,保证一定的有序性。
  • 不保证原子性

77. 使用线程池的好处

  • 避免频繁创建和销毁线程,降低开销。
  • 控制最大并发数,防止资源耗尽。
  • 提高线程的可管理性和可扩展性。

78. ThreadPoolExecutor 的七大参数

  1. corePoolSize:核心线程数(常驻)。
  2. maximumPoolSize:最大线程数。
  3. keepAliveTime:非核心线程的存活时间。
  4. unit:存活时间的单位。
  5. workQueue:任务队列。
  6. threadFactory:线程工厂(自定义线程名等)。
  7. handler:拒绝策略。

79. ThreadPoolExecutor 线程池的工作原理

  1. 任务提交 → 判断线程数是否 < corePoolSize → 创建新线程。
  2. 否则 → 放入任务队列。
  3. 队列满 → 若线程数 < maximumPoolSize → 创建新线程。
  4. 若已达最大线程数 → 执行拒绝策略。

80. 实现多线程的方式

  1. 继承 Thread 类,重写 run()
  2. 实现 Runnable 接口,重写 run()
  3. 实现 Callable<V> 接口,重写 call(),可返回结果,配合 FutureTask
  4. 使用线程池 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、给成员变量赋值的方式有哪些

  1. 定义时直接赋值
  2. 构造方法赋值
  3. set 方法赋值
  4. 代码块赋值(静态/构造代码块)

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、多态的前提

  1. 继承或实现关系。
  2. 子类重写父类方法。
  3. 父类引用指向子类对象。

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、多态的三种方式

  1. 继承 + 方法重写
  2. 接口实现
  3. 抽象类实现

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、设计模式的七大原则是什么

  1. 单一职责原则
  2. 开放封闭原则
  3. 里氏替换原则
  4. 接口隔离原则
  5. 依赖倒置原则
  6. 合成复用原则
  7. 迪米特法则

51、Java中对方法的增强的方式有哪些

  • 继承重写
  • 装饰器模式
  • 动态代理(JDK Proxy、CGLIB)
  • AOP(Spring 面向切面编程)

52、集合和数组的区别是什么

  • 数组:长度固定、存储单一数据类型。
  • 集合:长度可变、存储对象,提供丰富的操作方法。

53、在List集合中删除元素的注意事项

  • 遍历时删除需注意:
    • for-each 删除会抛 ConcurrentModificationException
    • 解决:用 Iterator.remove()ListIterator

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、描述线程的生命周期

  1. 新建(New):创建 Thread 对象。
  2. 就绪(Runnable):调用 start(),等待 CPU 调度。
  3. 运行(Running):获得 CPU 执行 run() 方法。
  4. 阻塞/等待(Blocked/Waiting/Timed Waiting):等待资源或超时等待。
  5. 终止(Terminated):线程执行完毕或异常结束。

75、HashMap 和 Hashtable 的区别

  • HashMap:线程不安全,效率高,允许 null 键和值。
  • Hashtable:线程安全(方法加 synchronized),不允许 null 键或值。

76、volatile 关键字的作用

  • 保证 内存可见性:线程修改后,其他线程能立即看到。
  • 禁止 指令重排序,保证一定有序性。
  • 不保证 原子性

77、使用线程池的好处是什么

  • 降低线程频繁创建/销毁的开销。
  • 控制并发线程数,防止资源耗尽。
  • 统一管理线程,提高可扩展性和稳定性。

78、ThreadPoolExecutor 的七大参数

  1. corePoolSize:核心线程数,常驻线程数。
  2. maximumPoolSize:最大线程数。
  3. keepAliveTime:非核心线程空闲存活时间。
  4. unit:时间单位。
  5. workQueue:任务队列。
  6. threadFactory:线程工厂(可自定义线程名)。
  7. handler:拒绝策略(丢弃、抛异常、调用者执行等)。

79、ThreadPoolExecutor 线程池的工作原理

  1. 提交任务 → 判断运行线程数是否小于 corePoolSize,若是则创建线程执行。
  2. 否则 → 任务进入队列 workQueue
  3. 队列满 → 若线程数 < maximumPoolSize,创建非核心线程执行任务。
  4. 若已达最大线程数且队列满 → 执行拒绝策略。

80、实现多线程的方式

  1. 继承 Thread 类,重写 run()
  2. 实现 Runnable 接口,重写 run()
  3. 实现 Callable<V> 接口,重写 call(),配合 FutureTask
  4. 使用线程池 ExecutorService 提交任务。

经典的线程通信案例——生产者消费者模式,使用 wait()notify() 来实现线程通


生产者消费者案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class Product {
private int count = 0;
private final int MAX_COUNT = 5;

// 生产产品
public synchronized void produce() throws InterruptedException {
while (count == MAX_COUNT) {
wait(); // 容量满,等待消费者消费
}
count++;
System.out.println(Thread.currentThread().getName() + " 生产了产品,当前库存:" + count);
notifyAll(); // 通知消费者可以消费
}

// 消费产品
public synchronized void consume() throws InterruptedException {
while (count == 0) {
wait(); // 库存空,等待生产者生产
}
count--;
System.out.println(Thread.currentThread().getName() + " 消费了产品,当前库存:" + count);
notifyAll(); // 通知生产者可以生产
}
}

class Producer implements Runnable {
private Product product;

public Producer(Product product) {
this.product = product;
}

@Override
public void run() {
try {
while (true) {
product.produce();
Thread.sleep(500); // 模拟生产时间
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class Consumer implements Runnable {
private Product product;

public Consumer(Product product) {
this.product = product;
}

@Override
public void run() {
try {
while (true) {
product.consume();
Thread.sleep(800); // 模拟消费时间
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public class ProducerConsumerDemo {
public static void main(String[] args) {
Product product = new Product();

Thread producer1 = new Thread(new Producer(product), "生产者1");
Thread producer2 = new Thread(new Producer(product), "生产者2");
Thread consumer1 = new Thread(new Consumer(product), "消费者1");
Thread consumer2 = new Thread(new Consumer(product), "消费者2");

producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
}

核心点解析

  1. 共享资源Product 类中的 count 表示库存。
  2. 同步机制synchronized 确保同一时间只有一个线程访问资源。
  3. 线程通信
    • wait():线程等待,释放锁,进入等待队列。
    • notifyAll():唤醒等待线程,让其重新竞争锁。
  4. 生产/消费逻辑
    • 当库存满时,生产者阻塞等待。
    • 当库存空时,消费者阻塞等待。

这个案例是面试中非常经典的 多线程通信问题,可以扩展为 阻塞队列 BlockingQueue 版本,效率更高且无需手动管理 wait/notify