先把地图摊开
欢迎来到「十天 Java 后端入门突击课」。你不需要先背完一本厚厚的 Java 书,也不需要一上来就搭出一个复杂的微服务集群。我们先做一件更重要的事:把后端世界的地图摊开,知道每条路通向哪里。
这不是“十天精通 Java”的魔法。十天能做的是建立一副面试骨架:看到 HashMap、事务、Redis、消息队列这些词时,你知道它们解决什么问题,能用自己的话讲清楚,还能在一个小项目里把它们串起来。真正扎实的功夫,要靠后面三个月继续写、读、改。
我们的故事
我们一起给一条虚拟的街区做一个小系统,名字叫“邻里站”。居民可以注册、登录、修改资料,管理员可以查询用户。系统一开始只有一个 Java 服务,后来遇到数据慢、访问拥堵、任务堆积,我们再一步步请来 MySQL、Redis、RabbitMQ 和网关帮忙。
你可以把每天的知识想成一位新同事:Java 负责思考,MySQL 负责记账,Spring 负责组织团队,Redis 负责记住常用答案,RabbitMQ 负责传话。理解它们为什么存在,比背一长串定义更有用。
十天路线
- Day 1:Java 基础——给大脑装工具箱
- Day 2:MySQL——给数据建一座不会迷路的图书馆
- Day 3:Spring / Spring Boot——让团队自动运转
- Day 4:手搓一个项目——先跑起来再变漂亮
- Day 5:Redis——给系统请一个记性很好的前台
- Day 6:RabbitMQ——让任务排队,不让消息丢失
- Day 7:微服务与系统设计——把一间厨房拆成几间店
- Day 8:算法——只练面试最常见的几类题
- Day 9:网络与操作系统——理解请求是怎么旅行的
- Day 10:模拟面试与复盘——把知识讲给面试官听
每天怎么学
每一天都按同一套节奏走:先读故事,建立直觉;再看白话解释,补上术语;然后完成一个小练习;最后闭上资料,用三分钟把今天的内容讲出来。每天安排四到六个专注小时就够了,不必把“十四小时”当成门槛。
遇到不会的代码,不要先怀疑自己。把问题拆成三个小问题:输入是什么,程序要做什么,结果应该是什么。后端开发就是不断把大问题拆成能验证的小问题。
学完的验收标准
- 能用一句话说清楚 Java、MySQL、Spring、Redis、消息队列分别解决什么问题。
- 能独立画出“登录请求”的基本流程,并说出 Controller、Service、DAO 各自负责什么。
- 能写出链表反转、简单 LRU、常用 SQL 和一个缓存读写流程。
- 能诚实地介绍“邻里站”项目:它解决了什么问题,哪里还只是 demo,下一步怎么改。
先从 Day 1 开始。不要急着把所有词背下来,我们会在故事里反复遇见它们。
Day 1:Java 基础——给大脑装工具箱
今天完成什么
你不需要把 JDK 源码背下来。今天的目标是:看到集合、堆栈、垃圾回收、synchronized、volatile 和线程池时,能说出它们在解决什么麻烦。
故事:仓库管理员小李
“邻里站”刚开张,仓库管理员小李遇到了三个问题:货物怎么摆才找得快?临时工和正式工的工作台怎么分?几个人同时改同一箱货时,怎么不把账改乱?
Java 的集合像仓库货架。ArrayList 像一排连续货架,按编号取东西很快;LinkedList 像一串用绳子连接的箱子,中间插入方便,但找第 80 个箱子要一路走过去;HashMap 像一个按“编号计算货架位置”的仓库。
Map<String, Integer> stock = new HashMap<>();
Integer count = stock.get("apple");
HashMap 先根据 key 算出位置。如果两个 key 恰好撞到同一个位置,就叫哈希冲突。它会把多个元素挂在同一个桶里,冲突严重时会把桶里的结构优化成树,让查找别退化得太厉害。面试时重点说清“哈希定位、冲突处理、扩容成本”,不要一上来就背几十行源码。
JVM:每个人都有自己的工作台
线程像仓库里的工人。每个人有一张自己的小工作台,这就是栈;大家共同使用的大仓库是堆,Java 对象主要放在那里。一个对象没人再使用时,GC 就像夜班清洁工,把它整理掉,腾出空间。
所以常见的 OutOfMemoryError 可以先想成“公共仓库塞满了”,而 StackOverflowError 更像“某个工人的工作台上一直套新的任务,最后堆满了”。这不是完整的 JVM 实现细节,但它能帮你先建立正确方向。
多线程:同一张订单不能被两个人改坏
synchronized 像给一段操作挂上“单人通行牌”:同一时间只有一个线程进入。volatile 像在门口挂一块实时公告板:一个线程改了值,其他线程更容易看到最新值,但它不能保证“读取、修改、写回”这一整组动作是不可分割的。
线程池则像一支固定的配送队。任务来了就交给空闲工人,避免每个任务都重新招人。面试可以说:线程池要关注核心线程数、最大线程数、队列和拒绝策略,不能只说“开几个线程”。
ConcurrentHashMap 适合并发读写的场景,它通过更细粒度的并发控制减少互相等待;普通 HashMap 不应该在多线程下直接承担并发写入。
面试可以这样说
HashMap 通过 key 的哈希值定位桶,冲突时在桶内继续查找,数据量增长会触发扩容。并发场景下我会根据读写特点选择 ConcurrentHashMap,并用线程池统一管理任务。synchronized 保证临界区互斥,volatile 主要保证可见性,不能替代复合操作的原子性。
动手练习
写一个库存扣减方法:库存为 10,启动 20 个任务,每个任务扣 1。先用普通变量写一次,再给扣减操作加 synchronized,观察结果有什么不同。你不需要追求复杂,先把“多个工人同时改一张纸”的问题看见。
三分钟小测
- 为什么 HashMap 需要扩容?
- volatile 能不能保证
count++ 安全?为什么?
- 栈和堆,你会用什么生活中的东西来解释?
今天只记住一句话:集合解决“怎么放”,JVM 解决“放在哪里”,并发工具解决“多人怎么一起做”。Java 并发集合的官方入门说明可以继续看 Oracle 并发集合教程。
Day 2:MySQL——给数据建一座不会迷路的图书馆
故事:图书管理员的检索卡
邻里站的用户从 100 人变成 100 万人。管理员说:“请帮我找手机号是 138xxxx 的用户。”如果数据库像一本没有目录的书,就得从第一页翻到最后一页。索引就是图书馆的检索卡:先根据条件缩小范围,再找到真正的数据。
B+ 树:一层层缩小范围
B+ 树像一棵矮胖的目录树。根节点先告诉你大概在哪一段,下一层继续缩小范围,最后叶子节点按顺序连在一起。它适合磁盘读取:树不必太高,而且范围查询可以顺着叶子节点往后走。
CREATE INDEX idx_user_phone ON user(phone);
EXPLAIN SELECT * FROM user WHERE phone = '13800000000';
索引不是越多越好。每多一个索引,新增和修改数据时都要维护一份目录,还会占用空间。联合索引 (city, age) 通常先利用最左边的 city,这就是常说的最左匹配原则。面试时不要只背这句话,要能举出查询条件的例子。
聚簇和非聚簇:目录指向哪里
可以把聚簇索引想成“书本本身按主键排好”;主键索引的叶子节点直接放着整行数据。普通二级索引更像另一套检索卡,找到索引值后可能还要根据主键回表取完整记录。于是 SELECT * 和只查索引中已有的字段,成本可能不同。
事务:四个人一起记账
转账不是简单的“扣钱,再加钱”。它至少要满足:要么两步都成功,要么两步都失败,这叫原子性;数据要遵守规则,这叫一致性;多人同时操作不能互相看乱,这和隔离性有关;成功提交后结果不能凭空消失,这叫持久性。合起来就是 ACID。
隔离级别像图书馆规定“读者能看到哪一版账本”。MVCC 会保留版本信息,让普通读取不必总和写入互相堵住;行锁则像只给正在修改的那一行挂上维修牌。具体行为要结合数据库引擎、索引和 SQL 一起看。
EXPLAIN 只看三件事
先看有没有走合适的索引,再看扫描了多少行,最后看是否出现明显的全表扫描、临时表或额外排序。不要把 EXPLAIN 当成神谕:它是线索,真正结论还要结合数据量和实际耗时。
面试可以这样说
索引是为了减少查找范围,但会增加写入维护成本。B+ 树适合磁盘和范围查询;事务用 ACID 描述可靠性,MVCC 通过版本让读写更好地并行,锁用来保护并发修改。遇到慢 SQL,我会先用 EXPLAIN 看访问路径和扫描行数,再结合索引设计和实际数据验证。
动手练习
建一张 user 表,插入几十条数据,分别为 phone 和 (city, age) 建索引。用 EXPLAIN 比较 WHERE city = ?、WHERE age = ?、WHERE city = ? AND age = ? 的差异。把自己的猜测先写在纸上,再看结果。
小测
- 索引为什么会让写入变慢?
- 为什么联合索引通常强调最左列?
- 转账为什么不能拆成两个互不相关的 SQL?
继续阅读:MySQL InnoDB 事务隔离级别。
Day 3:Spring / Spring Boot——让团队自动运转
故事:一家不再靠老板喊人的餐厅
刚开始开餐厅时,厨师小王要自己买锅、找服务员、记菜单。后来来了一个后厨经理:他负责准备工具,把合适的人安排到合适的位置,开饭时再把任务交出去。这个经理就是 Spring 容器。
IoC:不要每个人都自己造依赖
传统写法里,OrderService 可能自己 new OrderDao()。这样一换实现,Service 就要跟着改。IoC 的意思可以先理解为:对象需要什么,不由它自己到处寻找,而是交给容器统一准备。依赖注入就是把准备好的对象传进来。
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
@Component、@Service、@Repository 可以看成“请把这个类登记进后厨名册”。容器创建 Bean、注入依赖、在合适的时机销毁它。Bean 生命周期不必今天背全,但要知道它不是普通 new 完就结束了。
循环依赖就像厨师甲等厨师乙先交工具,厨师乙又等甲先交工具,大家一起站着不动。发现循环依赖时,先问设计是否真的需要互相持有,而不是急着背某个版本的解决细节。
AOP:给每张订单统一盖章
日志、权限、事务这些事情,很多业务方法都要做。如果每个方法都手写一遍,代码会越来越胖。AOP 像在订单经过传送带时统一加一道工序:进入前记录日志,执行中开启事务,结束后提交或回滚。
Spring 常通过代理对象完成这件事。调用方以为自己拿到的是 Service,实际上先经过代理,再进入真正的目标方法。@Transactional 的常见坑也来自这里:同一个类内部直接调用另一个事务方法,可能绕过代理,导致你以为生效的事务没有按预期工作。
Spring Boot:把常用后厨设备自动装好
Spring Boot 不是另一个完全不同的 Spring,而是把依赖、默认配置和启动流程整理好。你引入 Web 相关依赖,Boot 会根据类路径和配置猜测“你大概需要一套 Web 环境”,再自动装配许多基础组件。自动配置是为了少写样板代码,但关键配置仍然要能解释。
面试可以这样说
IoC 把对象创建和依赖管理交给容器,降低类之间的耦合。AOP 通过代理把日志、事务等横切逻辑织入方法调用。Spring Boot 在 Spring 基础上提供约定优于配置和自动配置,让项目更快启动,但最终仍然是根据条件注册 Bean。
动手练习
创建一个最小 Spring Boot 项目:写 UserController、UserService、UserDao 三个类,用构造器注入连起来;再给 Service 方法加一个日志切面或 @Transactional,观察调用链。
小测
- 为什么推荐构造器注入?
- AOP 解决的是哪类重复问题?
- Spring Boot 的“自动”从哪里来?
继续阅读:Spring IoC 容器 和 Spring AOP 官方文档。
Day 4:手搓一个项目——先跑起来再变漂亮
故事:给邻里站装上第一扇门
前几天我们认识了厨房里的各个角色,今天把它们放进一栋真正的房子。用户从浏览器发来请求,Controller 像前台接待;Service 像店长,负责业务判断;DAO 像仓库管理员,负责和数据库沟通。每一层只做自己擅长的事,房子才不容易乱。
项目目标
做一个最小用户管理系统:注册、登录、查询用户、修改昵称、删除用户。先不用头像上传、复杂权限和微服务,先让主流程完整跑通。
接口可以先定成:POST /api/auth/register、POST /api/auth/login、GET /api/users/me、PUT /api/users/me。每个接口都写清请求、成功响应和失败响应,前后端才像在用同一本字典。
JWT 登录像一张盖章通行证
登录时,服务器验证账号密码,生成一张签名后的 token 返回给客户端。之后客户端每次请求把 token 放在 Authorization: Bearer ... 中,服务器验证签名和过期时间,再把用户身份放入当前请求。
注意:JWT 不是把密码藏起来的保险箱,密码仍然要用安全哈希保存;token 也不能无限期有效。面试时能主动说出过期、撤销和刷新,就比只说“我用了 JWT”更完整。
今天的开发顺序
- 建表:
id、username、password_hash、nickname、created_at。
- 先写 DAO 查询,再写 Service 规则,最后写 Controller。
- 用 Postman 或 curl 调通注册和查询。
- 加登录,拿 token 访问需要登录的接口。
- 故意制造用户名重复、token 过期等错误,补上清晰响应。
MyBatis-Plus 可以替你减少基础 CRUD 样板,但你仍要知道 SQL 大概做了什么。工具是自行车,不是替你走路的人。
面试可以这样说
项目采用 Controller、Service、DAO 分层。Controller 负责参数和响应,Service 负责业务规则与事务边界,DAO 负责持久化。登录后使用 JWT 做无状态身份校验,密码不明文存储。这个项目是可运行 demo,复杂权限和部署自动化还可以继续完善。
动手练习
今天只完成一条纵向链路:POST /api/auth/register 从请求进入 Controller,经过 Service 校验,最后写入 MySQL。成功后再复制这条链路做查询,不要同时开十个功能。
小测
- 为什么 Controller 不应该直接写 SQL?
- JWT 里为什么不能放密码?
- 一个接口的成功和失败响应,为什么都应该提前设计?
Day 5:Redis——给系统请一个记性很好的前台
故事:酒店前台不再每次翻总账
邻里站首页每天都有人问“热门用户是谁”“这个用户的公开资料是什么”。如果每次都去 MySQL 总账房查,前台会排长队。于是我们请 Redis 做前台:常见答案放在手边,先问它;它没有,再去 MySQL 查,并把结果记下来。
这就是常见的 Cache Aside:读时先查缓存,未命中再查数据库并回填;写时先更新数据库,再按需要删除缓存。缓存不是永久真相,数据库才是主要账本。
五种抽屉
- String:一张便签,适合计数、简单文本和 token。
- Hash:一个小档案袋,适合保存用户资料的多个字段。
- List:一列排队的人,适合简单队列或时间线。
- Set:一盒不重复的标签,适合共同关注、去重。
- ZSet:带分数的名单,适合排行榜。
ZADD neighbor:ranking 98 user:1001
ZADD neighbor:ranking 87 user:1002
ZREVRANGE neighbor:ranking 0 9 WITHSCORES
三种“门口堵车”
缓存穿透:有人不断查询根本不存在的用户,前台每次都只能去总账房。可以用空值缓存或布隆过滤器挡一挡。
缓存击穿:某个特别热门的用户资料刚好过期,很多请求一起冲向数据库。可以设置互斥锁,让一个请求负责回填,其他请求稍等。
缓存雪崩:大量 key 同时过期,整个请求潮一起撞向数据库。可以给过期时间加随机值,分批过期,并准备限流和降级。
分布式锁像“正在补货,请稍候”的牌子。要设置过期时间,释放时确认锁的归属,不能因为一个服务崩溃就把牌子永远挂着。生产方案还要认真处理续期、误删和故障,这里先记住问题边界。
面试可以这样说
Redis 适合承接高频、低延迟访问,但缓存不是最终数据源。我会根据场景选择 String、Hash、List、Set 或 ZSet,并考虑穿透、击穿、雪崩、过期策略和一致性。排行榜可以用 ZSet,登录 token 或计数可以用 String。
动手练习
给“邻里站”加一个热门用户排行榜:每次用户获得点赞就给 ZSet 增加分数;查询前十名时从 Redis 读取。再模拟一个不存在的用户,设计空值缓存的过期时间。
小测
- 为什么缓存命中后还要考虑数据是否过期?
- 穿透、击穿、雪崩分别像什么?
- 为什么排行榜适合 ZSet?
继续阅读:Redis 数据类型。
Day 6:RabbitMQ——让任务排队,不让消息丢失
故事:快递站的传送带
邻里站要给新用户发送欢迎邮件。注册接口如果自己发邮件,邮件服务慢了,用户就要一直等。于是注册成功后,只把“请发欢迎邮件”这张任务单交给 RabbitMQ,接口先返回成功,后台工人稍后处理。
生产者像寄件人,交换机像分拣台,队列像不同目的地的传送带,消费者像取件工人,绑定规则决定一张单子该去哪条带子。
先掌握一条完整链路
注册服务 -> exchange -> welcome.queue -> 邮件消费者
消费者处理成功后发送 ack,表示“这张单我完成了”。如果处理失败,可以重试;反复失败的消息不要无限重试,而是送到死信队列,留给人检查。消息队列解决的是解耦、削峰和异步,不是凭空让业务可靠。
消息为什么会重复
消费者处理完业务但还没来得及 ack 就崩溃,队列可能再次投递同一条消息。所以消费者必须幂等:同一个订单号或消息 ID 来两次,结果也只能生效一次。可以用数据库唯一约束、处理记录表或业务状态机来保护。
顺序消费也不是一句“开一个消费者”就能永久保证。要明确哪些消息属于同一业务分区,是否允许并行,以及失败重试会不会打乱顺序。
面试可以这样说
我选择 RabbitMQ 来承接异步任务。生产者把消息发到交换机,由绑定规则路由到队列;消费者成功处理后 ack,失败进入重试或死信流程。由于消息可能重复投递,业务侧用消息 ID 或唯一约束保证幂等。真正的可靠性需要生产、存储、消费三段一起设计。
动手练习
不必先搭集群。用一个本地 RabbitMQ 或在线实验环境,完成“注册后发送欢迎邮件”的流程。先让消息成功,再拔掉消费者,重新启动后观察消息是否还在;最后让消费者故意失败,设计重试次数和死信队列。
小测
- exchange 和 queue 分别负责什么?
- 为什么 ack 之后才算消费完成?
- 消息重复时,谁负责保证幂等?答案通常是业务消费者。
Day 7:微服务与系统设计——把一间厨房拆成几间店
故事:从一家餐厅到一条美食街
邻里站变大后,用户、订单、通知全挤在一间后厨里。改一处代码要重启整家店,某个慢任务还会拖住所有客人。于是我们把它拆成用户店、订单店、通知店,每家店只负责自己的菜单。
拆开以后也出现新麻烦:店在哪里?菜单怎么统一?客人先去哪家?某家店停电时,其他店怎么办?微服务不是把一个项目切成几个文件夹,而是用网络把独立服务连接起来。
四个面试高频角色
- 注册中心:像美食街的店铺地图,服务启动时登记地址,调用方查询可用实例。Nacos、Eureka 都属于这一类思路。
- 配置中心:像统一的公告栏,数据库地址、开关和环境配置集中管理。
- 网关:像街区入口,负责路由、鉴权、限流和统一入口。
- 熔断与降级:像某家店排长队时,入口先告诉客人“暂时只提供简版菜单”,避免全街一起被拖垮。Sentinel、Hystrix 代表过这类思路。
CAP 先用一句人话记
当分布式系统发生网络分区时,不可能同时保证所有节点看到完全一致的数据,又保证所有请求都立刻成功。面试时重点不是背字母,而是能说出一个选择:订单支付这类数据更偏向一致性;推荐列表短暂旧一点,通常更看重可用性。
现在不要急着拆
“邻里站”在学习阶段完全可以先做成单体应用。你可以在代码边界上按用户、通知、订单分包,等业务和流量真的需要,再拆成服务。面试时可以诚实表达:我理解微服务的组件和取舍,当前项目采用单体是因为规模和维护成本更合适。
面试可以这样说
微服务拆分的价值是独立演进和隔离故障,但代价是网络调用、数据一致性和运维复杂度。注册中心解决服务发现,网关统一入口,配置中心集中管理配置,熔断降级避免故障扩散。我会先根据业务边界和团队能力决定是否拆分。
动手练习
画一张“用户注册”的流程图:浏览器到网关,网关到用户服务,用户服务写 MySQL,再发一条欢迎消息给通知服务。给每一步标上“可能失败的地方”和“失败后怎么办”。
小测
- 微服务拆开后,为什么反而更复杂?
- 网关和注册中心分别站在流程的哪一段?
- 什么情况下单体应用是更合理的选择?
Day 8:算法——只练面试最常见的几类题
故事:仓库里的传送带
算法题不是故意为难人的机关,而是让你在一条传送带上安排货物。题目给你输入,要求你按照规则得到输出。真正重要的是先看出货物的结构:是一条链、一棵树、一张表,还是有重复子问题。
链表反转:把箭头一节节掉头
准备两个指针:prev 指向已经反转的部分,current 指向正在处理的节点。每次先保存下一个节点,再把箭头反过来,最后两个指针一起向前走。顺序不能乱,否则剩下的链会丢。
while (current != null) {
Node next = current.next;
链表判环可以用快慢指针:一个走一步,一个走两步。它们在环里迟早相遇,就像操场上快跑的人最终会追上慢跑的人。
二叉树和动态规划
树的前序、中序、后序,本质是“什么时候处理当前节点”。递归写法直观,迭代写法能帮助你理解栈。动态规划则像做旅行预算:今天的最优结果,往往来自之前已经算好的小结果。斐波那契只是入门,重点是找到状态、选择和转移。
LRU:最久没用的东西先离场
LRU 缓存需要同时做到“按 key 快速找”和“按使用顺序快速移动”。常见组合是 HashMap 加双向链表:Map 找节点,链表维护新旧顺序。访问一次就把节点移到头部,容量满了就删除尾部。
一套固定解题动作
先用自己的话复述题目;画出一个最小例子;写能跑的暴力解;再问哪里重复、哪里能用哈希或双指针优化;最后说时间复杂度和空间复杂度。不会的题先看题解没关系,但一定要关掉题解后自己重写三到五遍。
面试可以这样说
我会先确认输入规模和边界条件,再用例子验证思路。能用哈希表把查找从 O(n) 降到平均 O(1) 时,我会考虑空间换时间;如果使用双指针或动态规划,会同时说明指针不变量或状态转移。
动手练习
今天完成四题:链表反转、链表判环、二叉树层序遍历、LRU 设计。每道题都写出一个空输入、一个最小输入和一个重复输入的测试例子。
小测
- 链表反转时为什么要先保存
next?
- LRU 为什么不能只用一个 HashMap?
- 你能说出一道动态规划题的“状态”是什么吗?
Day 9:网络与操作系统——理解请求是怎么旅行的
故事:一封要穿过城市的快递
浏览器访问邻里站,就像寄一件快递。DNS 先查地址,TCP 先和对方确认线路,HTTP 说明信件格式,HTTPS 再给信件加上加密的信封。任何一层出了问题,请求都可能到不了终点。
TCP:先握手,再送货
三次握手可以先记成:客户端说“我想建立连接”(SYN),服务端回复“我听到了,也可以”(SYN + ACK),客户端再说“好,确认”(ACK)。这样双方都确认了发送和接收能力。挥手断开时通常需要更多次确认,因为两边的数据发送方向要分别结束。
HTTP 是应用层的请求和响应格式,常见方法有 GET、POST、PUT、DELETE。HTTPS 可以理解为在 HTTP 外面加上 TLS 保护,重点解决传输过程中的窃听和篡改问题。
进程和线程
进程像一家独立的店,有自己的地址空间和资源;线程像店里的员工,共享这家店的许多资源,但每个人仍有自己的执行路线。线程切换成本通常比进程低,但共享数据也带来并发安全问题。
死锁像四辆车互相堵住:每辆车都占着一段路,又等另一段路释放。形成死锁通常要同时满足互斥、占有且等待、不可剥夺、循环等待。工程上可以统一加锁顺序、减少锁范围、设置超时来降低风险。
常用 Linux 命令
curl -I https://example.com
先会用这些命令定位“服务有没有启动、端口有没有监听、日志哪里报错”,比背一整本 Linux 命令大全更实用。
面试可以这样说
一个 HTTP 请求通常先通过 DNS 找到地址,再通过 TCP 建立可靠连接,HTTPS 在此基础上增加 TLS 保护。进程有独立资源空间,线程共享进程资源;共享数据时要关注可见性、原子性和死锁。
动手练习
用 curl 请求自己的站点,观察响应状态码和响应头;再在本地启动一个服务,用 ss -lntp 找到它监听的端口,用 tail -f 观察一条请求日志。
小测
- TCP 三次握手每一步想确认什么?
- HTTP 和 HTTPS 的主要差异是什么?
- 死锁的四个必要条件你能说出几个?
Day 10:模拟面试与复盘——把知识讲给面试官听
故事:今天不是考试,是彩排
面试官像第一次来邻里站的访客。他不一定想听你背百科,而是想知道:你做过什么,遇到什么问题,为什么这样解决,出了问题会怎么办。今天把“邻里站”从一堆零件讲成一个完整故事。
一分钟自我介绍模板
我主要学习 Java 后端,最近围绕一个用户管理项目练习了 Spring Boot、MySQL、Redis 和 JWT。项目包含注册、登录和用户资料管理,我负责接口设计、分层实现和缓存思路。这个项目目前是学习型 demo,我重点理解了事务边界、缓存一致性和接口错误处理,后续会继续补测试和部署。
把模板换成自己的真实经历。没有实习就说学习项目,没有做过集群就不要说“负责集群稳定性”。可信比夸大更能经得住追问。
项目追问的五步回答法
遇到“为什么用 Redis”,按这个顺序说:场景、问题、方案、结果、反思。
- 场景:首页频繁查询热门用户。
- 问题:每次都查 MySQL,访问量上来后数据库压力变大。
- 方案:用 Redis ZSet 保存排行榜,查询优先走缓存,写入时更新分数。
- 结果:减少重复查询,响应更快;具体数字只有测过才能说。
- 反思:还要考虑缓存过期、数据一致性和 Redis 故障降级。
今天做三轮模拟
第一轮只讲项目结构,控制在五分钟;第二轮随机回答 HashMap、事务、Spring、Redis、消息队列和 TCP;第三轮专门回答“如果出问题怎么办”。每轮结束后写下三个卡住的地方,回到对应课程补洞。
最后的检查单
- 能画出注册、登录、查询资料的请求流程。
- 能说清 Controller、Service、DAO 的边界。
- 能解释索引为什么能加速,也能说出索引的代价。
- 能解释缓存未命中、消息重复和事务回滚。
- 能说出一个自己还不会的地方,并给出下一步学习计划。
十天结束不是终点,而是你终于有了一张可以继续填充的地图。接下来三个月,建议每周真正完成一个小功能:写代码、测接口、看日志、改 bug,再把过程记录到自己的博客里。这样下一次面试时,你讲的就不再是背过的答案,而是自己走过的路。