一、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()
类名.class
Class.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-autoconfigure
spring.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/set
get
收集依赖(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。