Skip to content

Latest commit

 

History

History
592 lines (381 loc) · 22.8 KB

File metadata and controls

592 lines (381 loc) · 22.8 KB

面试题

无法跳转到题目请在右侧目录索引中输入对应题目的序号点击即可

1. JVM 内存管理与 C++ 有哪些区别?

项目 Java(JVM) C++
内存分区 堆、栈、方法区、元空间等 手动划分,堆/栈
内存分配 自动由 JVM 管理 开发者手动管理(new/delete
垃圾回收 有 GC,自动回收 无 GC,容易内存泄漏
内存泄漏 少,除非引用未释放 易发生,需要手动释放

2. 什么是多态?哪里体现了多态的概念?

多态是面向对象的三大特性之一,指同一操作作用于不同对象时表现出不同的行为。

  • 主要体现:方法重写(子类重写父类方法)
  • 运行时通过动态绑定决定调用哪个方法
  • 例子:父类引用指向子类对象,调用方法时执行子类重写的方法

3. HashMap 源码分析,是不是线程安全?

  • HashMap 是非线程安全
  • 结构是数组+链表(Java 8+ 有树化)
  • 多线程并发写会导致数据不一致,甚至死循环
  • 线程安全场景需要外部同步或使用 ConcurrentHashMap

4. ConcurrentHashMap 源码分析

  • 线程安全的哈希表
  • Java 8 以后采用CAS + synchronized + 链表/红黑树结合策略
  • 支持分段锁(以前版本)或锁粒度更细的同步
  • 多线程读写性能高,支持并发操作

5. 类加载,双亲委托机制

  • 类加载器按层级关系委托加载任务
  • 当前类加载器先委托给父加载器加载
  • 避免重复加载和保证核心类唯一性
  • 有三个主要加载器:启动类加载器、扩展类加载器、应用类加载器

6. Java 内存模型(堆和栈的细节)

  • :存储对象实例,所有线程共享,垃圾回收主要针对堆
  • :每个线程独有,存储方法调用的局部变量和执行状态
  • 栈内存随线程创建销毁自动释放,堆内存由 GC 管理
  • 方法区存类信息、常量池等,属于堆的逻辑部分

7. 线程池

  • 线程池是预先创建好一定数量的线程,重复利用,避免频繁创建销毁带来的开销
  • 主要参数有核心线程数、最大线程数、任务队列和拒绝策略
  • 优点:提高性能、控制最大并发数、资源复用、便于管理线程生命周期

8. 事务的四种特性(ACID)

  • 原子性(Atomicity):事务要么全部完成,要么全部不做
  • 一致性(Consistency):事务执行前后,数据必须保持一致状态
  • 隔离性(Isolation):并发执行的事务互不干扰
  • 持久性(Durability):事务提交后,结果持久保存,不会丢失

9. 什么是死锁?

  • 多个线程相互等待对方持有的资源,形成循环等待,导致所有线程无法继续执行
  • 四个必要条件:互斥、占有且等待、不可剥夺、循环等待
  • 解决办法:破坏上述条件,比如资源排序、超时重试、死锁检测

10. 乐观锁和悲观锁的策略

  • 悲观锁:假设会发生冲突,操作前先加锁,保证独占资源
  • 乐观锁:假设不会冲突,操作前不加锁,操作时通过版本号或时间戳检查是否冲突,冲突则重试
  • 乐观锁适合读多写少场景,悲观锁适合写多或高竞争场景

11. 高可用网站的设计(有什么技术实现)

  • 多机房部署,负载均衡(DNS 轮询、反向代理、硬件LB)
  • 服务冗余,故障自动切换(主备切换)
  • 数据库主从复制,分布式缓存,消息队列解耦
  • 健康检查、自动扩缩容、容灾备份
  • 监控报警和自动恢复机制

12. 低耦合高内聚

  • 低耦合:模块间依赖少,改变一个模块影响小
  • 高内聚:模块内部功能相关度强,职责单一明确
  • 目标是提高代码可维护性和复用性

13. 设计模式了解不?用过哪些?为什么用?单例模式有什么作用和好处?

  • 设计模式是解决软件设计中常见问题的最佳实践
  • 用过单例、工厂、观察者、策略等模式
  • 单例模式保证类只有一个实例,提供全局访问点
  • 好处:节省资源,保证共享状态一致性,方便管理

14. MySQL索引优化知道哪些?

  • 选择高选择性的字段建索引
  • 使用覆盖索引,避免回表
  • 避免在索引列上用函数导致失效
  • 合理设计联合索引,遵循最左前缀原则
  • 用 EXPLAIN 查看执行计划,确认索引是否被用到
  • 避免过多索引影响写性能
  • 定期维护索引,如重建和优化

15. MySQL为什么发生死锁,如何解决?

  • 多个事务互相等待对方持有的锁,形成环路
  • 解决办法:减少锁持有时间、统一加锁顺序、使用合理事务隔离级别、检测并重试死锁事务、优化SQL避免长事务

16. MySQL如何排查慢SQL?

  • 开启慢查询日志定位慢SQL
  • 用 EXPLAIN 分析执行计划
  • 检查索引使用情况,避免全表扫描
  • 优化SQL写法,减少复杂查询
  • 关注服务器资源瓶颈

17. MySQL你知道哪些参数?

  • innodb_buffer_pool_size:缓冲池大小
  • max_connections:最大连接数
  • innodb_log_file_size:日志文件大小
  • slow_query_log:慢查询日志开关
  • wait_timeout:连接超时设置
  • character_set_server:字符集设置

18. Redis你知道的持久化策略,具体实现?

  • RDB(快照):定时将数据写入磁盘
  • AOF(追加文件):记录所有写操作日志,恢复更完整
  • 混合持久化:结合RDB和AOF优点,提高恢复速度和完整性

19. Redis的zset的数据结构有哪些?

  • 跳表(skip list):支持快速有序范围查询
  • 哈希表(hash table):存元素和分值映射,支持快速查找

20. 跳表的底层原理,为什么要使用跳表不使用红黑树?

  • 跳表是多层链表,支持平均O(log n)查找
  • 实现简单,便于并发
  • 红黑树实现复杂,不易并发操作
  • Redis选择跳表兼顾性能和实现复杂度

21. 热key如何处理?

  • 多级缓存分散压力
  • 使用限流和降级策略
  • 本地缓存减少访问压力
  • 一致性哈希分片负载均衡
  • 异步处理热点请求

22. Redis的过期键如何处理,有哪些方式,会导致问题吗,如何解决?

  • 惰性删除:访问时检查过期并删除
  • 定期删除:周期性扫描删除过期键
  • 问题:过期键不能及时清理,造成内存压力
  • 解决:合理调整删除频率,监控内存,结合主动过期机制

23. Redis的内存淘汰策略有哪些?

  • noeviction:内存满时写操作失败
  • allkeys-lru:淘汰所有键中最近最少使用的
  • volatile-lru:淘汰设置过期的键中最近最少使用的
  • allkeys-random:随机淘汰键
  • volatile-random:随机淘汰设置过期的键
  • volatile-ttl:淘汰即将过期的键

24. Redis zset的底层结构:压缩列表和跳表,区别,什么时候使用哪一种,为什么不用红黑树?

  • 压缩列表(ziplist):内存占用小,适合元素少(默认128个元素以下)且元素较小(字符串少于64字节)时使用
  • 跳表(skiplist):支持快速有序范围查询,适合元素多且更新频繁的场景
  • 为什么不用红黑树:跳表实现简单,插入删除效率高,且更容易支持并发;红黑树实现复杂,且Redis设计偏好跳表

25. Java序列化,serialVersionUID的作用,怎么让字段不序列化(除了transient还有吗?)

  • serialVersionUID:用于版本控制,序列化时用来验证类版本一致,避免反序列化失败
  • 不序列化字段:常用transient关键字,标记该字段不参与序列化
  • 其他方案:使用自定义序列化方法(writeObject / readObject),或者使用@JsonIgnore(针对JSON序列化)

26. 深拷贝和浅拷贝,怎么实现深拷贝?(多种方式)

  • 浅拷贝:复制对象的引用,引用对象未复制

  • 深拷贝:复制对象及其引用的所有对象,完整拷贝一份

  • 实现深拷贝的方式

    • 实现Cloneable接口并重写clone(),递归克隆所有引用对象
    • 序列化反序列化(对象写入流再读出)
    • 使用第三方库(如Apache Commons SerializationUtils)
    • 手动构造新对象并复制字段

27. 秒杀系统要考虑什么,保险品退保返保费要注意什么?

  • 秒杀系统考虑点:高并发控制、库存原子扣减、流量削峰(限流/排队)、缓存击穿穿透、异步消息队列解耦、数据一致性、系统可用性和扩展性
  • 保险品退保返保费注意:资金流水准确、并发控制、防止重复退费、退费时数据回滚、审计日志完善、异步补偿机制

28. Redis过期策略,有没有局限性,怎么解决?

  • 过期策略:惰性删除、定期删除
  • 局限性:过期键不能保证实时删除,可能造成内存压力峰值,导致缓存雪崩
  • 解决方案:合理配置过期时间,配合内存淘汰策略,使用主动删除和监控,分散大量key过期时间

29. 序列化写入后读出的对象变化(版本不一致问题)

  • 反序列化时如果类结构变化(字段增删改),可能导致数据丢失或异常
  • 使用serialVersionUID控制版本兼容性
  • 可使用自定义序列化方法处理兼容问题
  • 推荐设计时保持序列化兼容性,避免频繁修改类结构

30. 线程池的核心参数,给了一个线程池问有什么问题?

  • 核心参数:核心线程数corePoolSize,最大线程数maximumPoolSize,任务队列workQueue,线程存活时间keepAliveTime,线程工厂ThreadFactory,拒绝策略RejectedExecutionHandler

  • 常见问题

    • 线程池线程数配置不合理,导致资源浪费或阻塞
    • 队列选择不当,可能导致任务积压或OOM
    • 拒绝策略未配置或配置不合理,任务丢失
    • 线程泄漏或没有及时关闭线程池

31. Hadoop 生态圈有哪些组件?展开说一下

核心三件套:

  • HDFS:分布式文件系统,负责存储大规模数据
  • YARN:资源调度框架,管理集群资源与任务调度
  • MapReduce:分布式计算模型,进行批量数据处理

常见生态组件:

  • Hive:类SQL查询工具,建表、查询、分析方便
  • HBase:NoSQL数据库,适合存储非结构化/海量数据
  • Sqoop:数据在关系型数据库和HDFS之间导入导出
  • Flume:日志采集工具,实时收集数据写入HDFS/Hive
  • Oozie:任务调度工具,管理定时和依赖任务
  • Zookeeper:分布式协调服务
  • Spark:替代MapReduce的更快计算框架(后起之秀)

32. 数仓建模遵循哪些规范?数据治理怎么做?异常数据如何监控?

数仓建模三层规范(Kimball 模型)

  • ODS(操作数据层):原始数据汇总,不加工
  • DWD(明细数据层):清洗后的业务明细
  • DWS(宽表数据层):面向分析的汇总表
  • ADS(应用数据层):最终用于展示、BI、报表的表

数据治理

  • 数据标准化、质量管理、权限管理、血缘追踪、元数据管理
  • 加入数据稽核、校验规则、数据版本控制

异常监控

  • 配置指标阈值报警(如某字段值突降)
  • 数据校验(行数对账、字段非空、唯一值判断)
  • 定期数据探查与回滚机制

33. 实时画像标签有接触吗?实时性怎么定义?怎么确保实时数据准确?

实时画像:根据用户行为数据(如点击、浏览)实时更新用户画像标签,如“活跃用户”“潜在转化”等

实时性定义:一般指分钟级、秒级(如T+0)的数据更新时效

保证实时准确性:

  • 数据链路要有“Exactly once”语义(Flink 支持)
  • 严格依赖 Kafka/Flink 的 checkpoint + watermark 机制
  • 使用幂等写入、去重、延迟窗口处理机制
  • 数据补偿:支持历史数据回补,确保最终一致性

34. 看板工具有哪些?底层数据如何做到秒级更新?

常见工具

  • BI 工具:如 Superset、FineBI、Tableau、DataV、Quick BI、Metabase
  • 可视化工具:如 Echarts、AntV、DataV 大屏

秒级数据更新关键点

  • 实时数据来源:如 Kafka + Flink + Redis/ES
  • 中间层缓存(Redis、Druid)提供快速查询
  • 前端定时拉取/订阅式刷新(WebSocket/Polling)

35. 如何评价一个数仓“干得好”?为什么?

评价标准:

  • 数据标准统一、质量稳定,能支撑复杂报表/分析
  • 建模清晰、可复用、文档完备
  • 查询快、时效高、支持多业务线
  • 维护成本低、权限清晰、监控完善
  • 能推动业务改进和决策

36. 如果让你从0到1设计一个数仓全栈,你会怎么设计?

设计步骤:

  1. 需求调研:明确业务目标、数据来源、核心指标
  2. 数据采集:Flume/Sqoop/CDC 采集 MySQL、日志、埋点等
  3. 建模分层:ODS → DWD → DWS → ADS,维表、宽表建模
  4. ETL调度:使用 Airflow/Oozie/Flink 定时处理数据
  5. 数据存储:离线层(Hive/HDFS)、实时层(Kafka/Redis/ES)
  6. 质量监控:数据校验、报警、血缘分析
  7. 服务支撑:API 接口、BI 查询、看板展示
  8. 文档管理:元数据、建模文档、数据字典

37. Synchronized 和 Lock 有哪些理论上的理解?本质区别是什么?

  • Synchronized:JVM 层面实现的内置锁,属于悲观锁,自动加解锁,粒度较粗
  • Lock(如 ReentrantLock):JDK 提供的显式锁,可以灵活控制加锁、释放、可重入、公平锁、可中断等

本质区别

  • Synchronized 依赖 JVM + Monitor 对象 实现,不能中断,适合简单场景
  • Lock 是 Java代码层面实现,提供更多控制能力(如 tryLock、可中断、公平锁)

38. Synchronized 锁膨胀经历哪些过程?

锁的优化过程(轻到重):

  1. 无锁:对象刚创建,处于无锁状态
  2. 偏向锁:线程第一次访问时加锁,偏向该线程,后续加锁无需操作
  3. 轻量级锁:有多个线程竞争时,使用 CAS 方式尝试加锁
  4. 重量级锁:CAS 自旋失败,升级为重量级锁,线程进入阻塞,JVM 使用 OS 层面的 Monitor

39. 对于信号量、volatile 有没有理解?

  • 信号量(Semaphore):控制并发线程数量的工具

    • 典型用法:限流、限资源访问
    • acquire() 获取许可,release() 释放许可
    • 可实现多个线程抢占固定资源(比如连接池)
  • volatile:用于线程之间变量可见性,并禁止指令重排

    • 不能保证原子性,仅解决可见性+有序性问题
    • 用于状态标志、双重检查锁单例等轻量同步场景

40. 项目中数据库有没有分库分表的场景?

有,比如用户表、订单表数据量非常大,单表查询慢、主从压力大

常见分库分表策略:

  • 分库:按业务维度,比如按地区、租户、业务类型
  • 分表:按范围(时间/数值)或哈希(mod)
  • 中间件:ShardingSphere、MyCat
  • 注意全局 ID、跨分片事务、聚合查询、分布式事务一致性问题

41. 秒杀场景中悲观锁有没有了解?

悲观锁适用秒杀场景:对库存等关键数据强一致要求

  • 使用数据库的 select ... for update 加锁操作
  • 适用于低并发、确保强一致、无锁冲突则效率高
  • 容易造成阻塞,吞吐量低,高并发场景推荐乐观锁 + 限流 + 异步削峰方案

42. 有了解过布隆过滤器吗?

是的,布隆过滤器(Bloom Filter)是一个概率型数据结构,用于判断某个元素是否存在

特点

  • 通过多个哈希函数映射到位数组上
  • 判断为“不存在”一定准确,“存在”可能有误判
  • 空间效率高、插入查询都很快

使用场景

  • 防止缓存穿透(先查布隆过滤器)
  • 爬虫、去重、黑名单判断等

43. 布隆过滤器为什么适合防止缓存穿透?

因为它能快速过滤掉不存在的数据请求,避免请求直接打到数据库:

  • 请求到来 → 查布隆过滤器
  • 不存在 → 拦截,直接返回空
  • 存在 → 再查缓存或数据库

误判会放行,但不会漏拦;能显著减少数据库压力。

44. Java线程模型,Thread类底层实现,线程和内核线程的对应关系,有什么优缺点?

Java线程模型:

  • Java 使用 “一对一”线程模型(1 Java线程 ≈ 1 内核线程)
  • 线程由 JVM 管理,但实际调度由 OS 内核完成

底层实现:

  • Thread 类底层通过 native 方法(如 start0())调用 JVM 层接口 → 最终由操作系统创建内核线程

线程 vs 内核线程关系:

  • Java 每个线程 ≈ 对应一个内核线程(HotSpot虚拟机默认策略)

优缺点:

  • ✅ 优点:调度灵活、性能高、可并行
  • ❌ 缺点:创建销毁线程开销大,频繁上下文切换影响性能

45. 一条 Java 语句从创建到执行的全过程,类加载流程,JDK 内置类如何被启动类加载器加载?

Java 语句执行流程(以对象创建为例):

  1. 源码编译.java.class(字节码)
  2. 类加载(类加载器加载类)
  3. 字节码执行(解释执行 or JIT 编译)
  4. JVM执行引擎 → 映射到系统指令 → 执行语句

类加载流程(类加载机制):

  1. 加载(load)
  2. 验证(verify)
  3. 准备(prepare)
  4. 解析(resolve)
  5. 初始化(init)

启动类加载器加载 JDK 内置类:

  • JVM 启动时加载 rt.jar 包中核心类(如 java.lang.*),由启动类加载器(BootstrapClassLoader)负责
  • 它是 JVM 的一部分,由 C/C++ 实现,不是 Java 类

46. 垃圾回收机制,JVM 常见的垃圾回收器?

垃圾回收机制核心概念:

  • 新生代、老年代、元空间(替代永久代)
  • 判断垃圾:引用计数 / 可达性分析
  • 回收策略:Minor GC(新生代)、Major GC(老年代)、Full GC(全堆)

常见垃圾回收器:

回收器 特点
Serial 单线程,新生代,适合单核
ParNew 多线程版 Serial
CMS 并发标记清除,低停顿
G1 分区式回收,低延迟,替代 CMS
ZGC 极低延迟,支持大内存
Shenandoah 类似 ZGC,JDK11+支持

47. MySQL 一条语句的执行流程?为什么要写 Redo Log?不能直接写 B+ 树吗?

SQL 执行流程:

  1. 客户端发 SQL → 连接层
  2. 查询缓存(8.0已废弃)
  3. 分析器 → 语法/词法解析
  4. 优化器 → 确定执行计划
  5. 执行器 → 执行语句
  6. 存储引擎(如 InnoDB)访问数据

为什么要有 Redo Log?

  • B+ 树存储在磁盘页中,若直接写磁盘效率低且不具备事务持久性保证
  • Redo Log 是 WAL(Write Ahead Logging)机制,先记录操作日志,再批量刷盘,提高性能

48. Undo Log 的作用?

  • Undo Log 用于 回滚数据,支持事务的 原子性一致性

主要功能:

  • 回滚:当事务失败或被手动回滚时,Undo Log 记录的数据可还原之前状态
  • MVCC:多版本并发控制,用 Undo Log 生成旧版本数据,实现 RC、RR 等隔离级别下的一致性读

49. Java 中 Class 的生命周期有哪些阶段?

Class 生命周期分为 5 个阶段:

  1. 加载(Loading):将 .class 文件读入内存,生成 Class 对象
  2. 验证(Verification):确保字节码合法、安全
  3. 准备(Preparation):为类的静态变量分配内存,赋默认值
  4. 解析(Resolution):将符号引用转为直接引用
  5. 初始化(Initialization):执行 <clinit> 方法,初始化静态变量等

50. JNI 效率优化怎么做?

JNI(Java 调用 C/C++)效率低的常见原因:

  • Java/Native 来回切换成本高
  • 数据结构转换损耗
  • 频繁调用小函数

优化方式:

  • 减少 Java ↔ Native 调用频率(批处理)
  • 缓存 jclass/jmethodID 避免每次查找
  • 避免使用 NewStringUTF 等频繁字符串转换
  • 用 DirectByteBuffer 减少内存复制
  • 用 registerNatives 提高方法绑定效率

51. 4 亿行数据怎么去重?

取决于数据存储在哪里:

  • 数据库中

    • create table new_table as select distinct * from old_table;
    • 分批查询 + 去重后插入临时表
    • 添加索引提高 distinct 效率
  • 大数据平台(Hive、Spark)

    • 使用 distinct + 分区处理
    • MapReduce:Hash分组去重
  • 内存中(文件/数据流)

    • Bloom Filter 过滤
    • HashSet 去重(可配合外部排序 / 分块处理)
    • Hadoop/Spark 分布式去重

52. 乐观锁和悲观锁的区别与使用场景?

对比项 乐观锁 悲观锁
核心思想 默认无冲突,冲突时重试 默认会冲突,先加锁阻止冲突
实现方式 CAS(版本号、时间戳) select ... for update,加行锁
性能 高,适合读多写少 低,适合写多读少
使用场景 秒杀、抢购、分布式场景 银行转账、库存扣减

53. 常见设计模式,你用过哪些?

分类

  • 创建型:单例、工厂、建造者
  • 结构型:代理、装饰器、适配器、桥接
  • 行为型:策略、模板方法、观察者、责任链、状态机

常见例子

  • 单例模式:Spring 中 Bean 默认是单例
  • 工厂模式:Spring 的 BeanFactory / FactoryBean
  • 策略模式:支付方式 / 排序策略切换
  • 责任链模式:Spring Filter / Servlet Filter 链

54. 分布式事务怎么实现?

三种主流方案:

  1. 两阶段提交(2PC):强一致,但性能差,常用于 XA
  2. TCC:Try-Confirm-Cancel 三步操作,每个服务实现补偿逻辑
  3. 本地消息表 + 最终一致性:如阿里方案,用消息队列确保事务达成

常见工具

  • Seata、DTM、LCN、RocketMQ 事务消息

55. 如何手写一个简单的 RPC 框架?

核心流程:

  1. 定义接口:如 HelloService
  2. 服务注册:将服务地址注册到注册中心(或 Map)
  3. 客户端代理:生成代理对象,封装网络调用
  4. 网络传输:使用 Netty/Socket 建立 TCP 通信
  5. 序列化协议:使用 JSON、Hessian、protobuf
  6. 服务端暴露:监听端口,接收请求,反射调用实现类
  7. 结果返回:序列化响应返回给客户端