一、Java核心技术
1. 集合框架
HashMap 的实现原理?JDK 1.7 vs 1.8 区别?
- JDK 1.7:
- 数组 + 链表(拉链法)
- 插入采用头插法,扩容时多线程下可能形成环形链表导致死循环
- 初始容量 16,负载因子 0.75,扩容为原来 2 倍
- JDK 1.8:
- 数组 + 链表 + 红黑树
- 当链表长度 ≥ 8 且数组长度 ≥ 64 时,链表转为红黑树
- 使用尾插法,避免多线程死循环
- 性能更稳定,最坏情况从 O(n) 提升到 O(log n)
核心原理:通过 hash 找到桶位置,解决冲突用链表/红黑树。
ConcurrentHashMap 如何保证线程安全?
- JDK 1.7:分段锁(Segment),每段相当于一个小 HashMap,锁粒度为 Segment
- JDK 1.8:
- 放弃 Segment,使用 CAS + synchronized
- 对 Node 数组的每个桶(bucket)加锁(synchronized 锁住链表头或红黑树根节点)
- 插入、扩容等操作通过 CAS 配合 volatile 控制并发
- 扩容时使用 transfer 机制,支持多线程协助迁移
优点:锁粒度更细,性能更高,避免 Segment 内存开销
ArrayList vs LinkedList
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 查询效率 | O(1) | O(n) |
| 增删效率 | 尾部 O(1),中间 O(n) | 任意位置 O(1)(已知节点) |
| 扩容机制 | 扩容 1.5 倍(oldCapacity + (oldCapacity >> 1)) |
无固定容量,动态增删 |
| 内存占用 | 小(仅数组) | 大(每个节点有 prev/next 指针) |
适用场景:
ArrayList:读多写少,随机访问频繁LinkedList:频繁在中间插入/删除,且不常随机访问
HashSet vs TreeSet
| 特性 | HashSet | TreeSet |
|---|---|---|
| 底层实现 | HashMap(value 为 PRESENT) |
TreeMap(红黑树) |
| 排序 | 无序 | 自然排序或 Comparator 排序 |
| 时间复杂度 | O(1) | O(log n) |
| 允许 null | 允许一个 null | null 取决于 Comparator(否则 NPE) |
使用建议:
- 快速查重 →
HashSet- 需要有序集合 →
TreeSet
fail-fast vs fail-safe
| 类型 | fail-fast | fail-safe |
|---|---|---|
| 实现机制 | 直接遍历原集合 | 遍历快照副本(如 CopyOnWriteArrayList) |
| 是否抛异常 | 是(ConcurrentModificationException) |
否 |
| 常见集合 | ArrayList, HashMap | CopyOnWriteArrayList, ConcurrentHashMap |
- ConcurrentModificationException:modCount ≠ expectedModCount 时抛出
- 避免方式:
- 使用
Iterator.remove() - 使用并发集合(如 ConcurrentHashMap)
- 使用
CopyOnWriteArrayList
- 使用
Comparator vs Comparable
| 特性 | Comparable | Comparator |
|---|---|---|
| 定义位置 | 类内部实现 compareTo() |
外部定义 compare() |
| 是否可变 | 固定一种排序逻辑 | 可定义多种排序策略 |
| 使用场景 | 默认排序(自然序) | 自定义排序、多条件排序 |
java
深色版本
// Comparable
class Person implements Comparable<Person> {public int compareTo(Person p) { ... }
}// Comparator
Collections.sort(list, (a, b) -> a.age - b.age);
2. 多线程与并发
创建线程的方式
- 继承
Thread - 实现
Runnable(无返回值) - 实现
Callable(有返回值,配合FutureTask) - 线程池提交任务(推荐)
Runnable vs Callable:
- Runnable:
run()无返回值,不能抛 checked exception- Callable:
call()有返回值(Future),可抛异常
线程生命周期(6种状态)
java
深色版本
NEW -> RUNNABLE -> BLOCKED/WAITING/TIMED_WAITING -> TERMINATED
NEW:新建未启动RUNNABLE:运行或就绪BLOCKED:等待监视器锁WAITING:无限等待(wait(),join())TIMED_WAITING:定时等待(sleep(1000),wait(1000))TERMINATED:结束
synchronized 原理 & 锁升级
- 对象头:包含 Mark Word(锁信息、GC 分代年龄、hashCode)
- Monitor(管程):每个对象有一个 Monitor,控制同步
- 锁升级过程:
- 无锁 → 2. 偏向锁(线程 ID 记录,避免重复加锁)→
- 轻量级锁(CAS 修改 Mark Word,自旋)→
- 重量级锁(阻塞,进入等待队列)
升级目的:减少线程阻塞带来的性能损耗
volatile 原理
- 可见性:写操作后立即刷新到主内存,其他线程读取时强制从主存加载
- 禁止指令重排:通过内存屏障(Memory Barrier)防止编译器/处理器重排序
- 不保证原子性:如
i++仍需synchronized或AtomicInteger
ReentrantLock vs synchronized
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现方式 | JVM 内置 | JDK 实现(AQS) |
| 是否可中断 | 否 | 是(lockInterruptibly()) |
| 超时获取 | 否 | 是(tryLock(timeout)) |
| 公平锁 | 否(默认非公平) | 可设置公平锁 |
| 条件变量 | wait/notify |
Condition.await/signal |
- AQS(AbstractQueuedSynchronizer):
- 使用 CLH 队列 管理等待线程
- 通过
state变量控制同步状态(0: 无锁, 1: 加锁) - 子类实现
tryAcquire/tryRelease定义同步语义
ThreadPoolExecutor 核心参数
java
深色版本
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler
);
- corePoolSize:核心线程数(常驻)
- maximumPoolSize:最大线程数
- keepAliveTime:非核心线程空闲存活时间
- workQueue:任务队列(ArrayBlockingQueue、LinkedBlockingQueue 等)
- threadFactory:线程创建工厂
- handler:拒绝策略(Abort, Discard, CallerRuns, DiscardOldest)
工作流程:
- ≤ core → 直接创建线程
core,队列未满 → 入队
core 且队列满 → 创建新线程 ≤ max
max → 触发拒绝策略
常见线程池:
FixedThreadPool:固定大小,队列无界 → 易 OOMCachedThreadPool:弹性,空闲 60s 回收 → 适合短任务SingleThreadExecutor:单线程ScheduledThreadPool:定时任务
合理配置:
- CPU 密集型:
N + 1- IO 密集型:
2N ~ N/(1 - 阻塞系数)(如 2N)
ThreadLocal 原理 & 内存泄漏
- 原理:每个线程持有
ThreadLocalMap,key 为ThreadLocal实例(弱引用),value 为值 - 内存泄漏原因:
- key 是弱引用,GC 后 key 为 null
- 但 value 强引用未清理 → 内存泄漏
- 解决方案:
- 调用
remove()主动清理 - 使用静态
ThreadLocal+try-finally模式
- 调用
应用场景:
- 用户上下文传递(如
UserContext)- 数据库连接管理
- 事务管理
并发工具类
| 工具类 | 用途 | 示例 |
|---|---|---|
CountDownLatch |
等待 N 个线程完成 | 主线程等子线程 |
CyclicBarrier |
N 个线程互相等待,到达屏障后一起执行 | 并发测试 |
Semaphore |
控制并发访问资源数量 | 限流 |
Exchanger |
两个线程交换数据 | 生产者-消费者 |
3. I/O
NIO vs BIO
| 特性 | BIO | NIO |
|---|---|---|
| 模型 | 阻塞 I/O | 非阻塞 I/O(多路复用) |
| 核心组件 | Stream | Buffer, Channel, Selector |
| 连接数 | 少连接 | 高并发(C10K) |
| 编程复杂度 | 简单 | 复杂 |
NIO 优势:单线程处理多个连接(Selector 监听事件)
序列化与 transient
- 序列化:对象 → 字节流(
ObjectOutputStream) - transient:标记字段不参与序列化
- 常见协议:JDK 原生、JSON、XML、Hessian、Protobuf、Kryo
缓冲流作用
BufferedInputStream:减少系统调用次数,提升 I/O 效率- 原因:直接读文件每次系统调用开销大;缓冲流先读一块到内存,再逐字节读
4. JDBC
JDBC 步骤
- 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver") - 获取连接:
DriverManager.getConnection(url, user, pwd) - 创建 Statement
- 执行 SQL
- 处理结果集
- 关闭资源(try-with-resources)
PreparedStatement vs Statement
| 特性 | PreparedStatement | Statement |
|---|---|---|
| SQL 注入 | 防止(预编译) | 不防 |
| 性能 | 预编译,缓存执行计划 | 每次编译 |
| 参数设置 | setString(1, "xxx") |
字符串拼接 |
推荐使用 PreparedStatement
事务 ACID
- Atomicity:原子性(全成功或全失败)
- Consistency:一致性(状态合法)
- Isolation:隔离性(并发不影响)
- Durability:持久性(提交后不丢失)
JDBC 事务管理:
java
深色版本
conn.setAutoCommit(false);
try {// 执行 SQLconn.commit();
} catch (Exception e) {conn.rollback();
}
数据库连接池原理
- 预创建连接,复用连接,避免频繁创建销毁
- 管理连接生命周期、超时、最大连接数
- 常见连接池:HikariCP(性能最好)、Druid(功能丰富,监控强)
5. 反射
反射原理
- 运行时获取类信息(字段、方法、构造器)
- 动态创建对象、调用方法
Class 获取方式:
obj.getClass()类名.classClass.forName("全限定名")
forName()会触发类初始化,.class不会
应用场景
- 框架(Spring IOC、MyBatis)
- 动态代理
- ORM 映射
- 注解处理
二、Java Web & 主流框架
Spring Framework
IoC / DI
- IoC:控制反转,由容器管理对象生命周期
- DI:依赖注入,自动装配依赖
- 注入方式:
- 构造器(推荐,不可变)
- Setter(可选依赖)
- 字段(方便,但不利于测试)
作用域:
singleton:单例(默认)prototype:每次获取新实例request/session/application:Web 作用域
AOP
- 解决:横切关注点(日志、事务、权限)
- 核心概念:
- 切面(Aspect):横切逻辑模块
- 通知(Advice):
@Before,@After,@Around - 切入点(Pointcut):匹配连接点的表达式
- 连接点(JoinPoint):方法执行点
动态代理:
- JDK Proxy:基于接口
- CGLIB:基于子类(无接口可用)
Spring AOP vs AspectJ:
- Spring AOP:运行时代理,功能有限
- AspectJ:编译期织入,功能强大
Spring MVC 流程
- 前端请求 →
DispatcherServlet HandlerMapping查找处理器HandlerAdapter调用 Controller- 返回
ModelAndView ViewResolver解析视图- 渲染响应
常用注解:
@Controller:控制器@RequestMapping:映射路径@ResponseBody:返回 JSON@RestController=@Controller + @ResponseBody
Spring 事务管理
- 传播行为:
REQUIRED:有则加入,无则新建(最常用)REQUIRES_NEW:挂起当前,新建事务
- 隔离级别:对应数据库
- @Transactional 失效场景:
- 方法非 public
- 自调用(this.method())
- 异常被捕获未抛出
- 代理未生效(如未扫描包)
Spring Boot
核心优势
- 自动配置(
@EnableAutoConfiguration) - 起步依赖(starter)
- 内嵌容器(Tomcat/Jetty)
@SpringBootApplication=@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
自动配置原理
spring.factories中定义@Configuration类- 使用
@ConditionalOnXXX条件注解按需加载
自定义 Starter
xx-spring-boot-autoconfigurespring.factories配置EnableAutoConfiguration- 条件装配 Bean
- 提供默认配置属性(
@ConfigurationProperties)
MyBatis
#{} vs ${}
#{}:预编译参数(安全,防 SQL 注入)${}:字符串替换(不安全,用于动态表名、排序字段)
动态 SQL
xml
深色版本
<if test="name != null"> AND name = #{name} </if>
<choose><when test="id != null">id = #{id}</when><otherwise>name = #{name}</otherwise>
</choose>
<foreach collection="list" item="item" separator=","> #{item} </foreach>
分页
- PageHelper:拦截
Executor.query,重写 SQL 加LIMIT - 物理分页:数据库
LIMIT(推荐) - 逻辑分页:查所有再内存分页(不推荐)
延迟加载
- 原理:关联对象在使用时才查询(通过代理)
- 配置:
lazyLoadingEnabled=true - N+1 问题解决:
- 使用
JOIN查询一次性加载 - 使用
fetchType="eager"强制立即加载
- 使用
一级缓存 & 二级缓存
- 一级缓存:SqlSession 级别,自动开启
- 二级缓存:Mapper 级别,需手动开启(
<cache/>),序列化支持 - 失效:执行
commit()、clearCache()、update操作
三、数据库(MySQL)
索引优化
- B+树优势:
- 层高低(3层可存千万级)
- 叶子节点有序、链表连接,适合范围查询
- 聚集索引:主键索引,数据存于叶子节点
- 非聚集索引:二级索引,叶子存主键值
- 覆盖索引:查询字段全在索引中,无需回表
事务隔离级别
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ |
| READ COMMITTED | ❌ | ✅ | ✅ |
| REPEATABLE READ(MySQL 默认) | ❌ | ❌ | ❌(MVCC + Gap Lock) |
| SERIALIZABLE | ❌ | ❌ | ❌ |
MVCC 原理
- 多版本并发控制,提升读并发
- 基于
undo log保存历史版本 ReadView判断哪些版本可见
Redis
数据类型 & 场景
| 类型 | 场景 |
|---|---|
| String | 缓存、计数器 |
| Hash | 用户信息、购物车 |
| List | 消息队列、最新列表 |
| Set | 去重、共同关注 |
| ZSet | 排行榜、延迟队列 |
持久化
- RDB:定时快照,恢复快,可能丢数据
- AOF:日志追加,数据安全,文件大
- 混合模式(4.0+):RDB 快照 + AOF 增量
缓存问题
| 问题 | 解决方案 |
|---|---|
| 穿透 | 布隆过滤器、缓存空值 |
| 击穿 | 互斥锁、热点永不过期 |
| 雪崩 | 随机过期时间、集群、降级 |
一致性方案
- 先更新 DB → 删除缓存(推荐)
- 延迟双删
- 监听 binlog(Canal)异步更新缓存
四、前端(Vue2 + Element-UI)
Vue 2 响应式原理
Object.defineProperty劫持get/setget收集依赖(Watcher)set派发更新- 数组通过重写
push/pop/splice等方法监听
局限性:
- 无法监听属性添加/删除
- 数组索引修改、length 变化不响应 → Vue.set() / vm.$set()
Vuex
- State:状态源
- Getter:计算属性
- Mutation:同步修改状态(必须)
- Action:异步操作,提交 Mutation
- Module:模块化
Mutation 同步:便于 DevTools 跟踪状态变化
五、开发工具
Git
rebase:变基,提交历史更干净merge:合并,保留分支历史- Git Flow:
master、develop、feature、release、hotfix
六、项目经验(MES系统)
⚠️ 这是重中之重!建议准备 2-3 个真实案例,结合 STAR 法则(Situation, Task, Action, Result)讲述
示例回答(高并发包装关箱):
S:MES 系统中,包装工位扫码速度极快,高峰期每秒数十次请求,导致数据库锁竞争严重。
T:需保证包装数据准确,避免重复包装或漏记。
A:
- 使用 Redis 分布式锁(Redisson)控制同一 SN 的并发操作
- 包装计数使用
INCR原子操作- 关键操作日志异步落库
- 数据库分表(按订单 ID)
R:系统吞吐提升 5 倍,零重复包装错误,响应时间 < 200ms。
