简介:《Java学习路线》PDF 面向Java初学者及希望系统补强的开发者,用一条较完整的知识主线回答“先学什么、后学什么、如何查漏补缺”的问题。内容从基础语法、面向对象、容器、异常、泛型与反射讲起,延伸至JVM类加载、内存模型、GC与调优,并发编程、数据结构与算法、计算机网络、数据库与SQL,并覆盖Web开发、Spring全家桶、微服务、前端基础、研发工具、Linux、设计模式、任务调度、链路追踪、ELK日志分析及Docker/Kubernetes容器化等后端常见技术栈。压缩包共1个PDF文件,约92KB,轻量便于随时查阅;已有492人学习。其价值在于以模块化路线图呈现技能层级和学习顺序,帮助读者建立全局认知、制定阶段目标,并对照目录发现薄弱环节,配合实际项目逐步深入。
1. 一份 Java 后端路线图 PDF:它更像施工图,不是书单
收藏夹里躺着几份 Java 学习路线 pdf 的人不少,真正照着走完的没几个。这份资源的可用之处不在于列了多少名词,而在于它按「能落地」的顺序把后端拆成编程基础、JVM、并发编程、数据结构与算法、计算机网络、数据库与 SQL、Web 开发、后端框架、微服务、前端、研发工具十一块,每块下面直接点名到具体组件——缓存写的是 Redis 的五大类型、事务、持久化、集群,微服务写的是 Eureka/Consul/Nacos 这一组注册中心,分布式事务连 2PC、TCC、SAGA、LCN 几条实现路线都摆出来了。它回答的是「先学谁、学到什么程度算过关」这两个最容易卡住新人的问题,而不是再给你一份更长的书单。适合入行一两年想补体系的后端、从别的方向转过来的自学者,也适合带新人的组长拿它当排期底稿。指望存下来就会写代码的,趁早别下。
2. 把脑图拆成可执行清单:从 Java 基础到 JVM 的四层递进
拿到这类 PDF,最常见的用法是扔进阅读器从头翻到尾,翻完感觉学到了,合上什么也没剩下。脑图类资源天生不适合线性阅读,它的正确打开方式是拆——把每个节点拆成一个可以验收的小任务,再按依赖关系重新排序。路线图里编程基础那一块列了语言基础、JVM、并发、数据结构与算法、计算机网络、数据库、操作系统、设计模式八个方向,看着是并列的,实际有严格的前后依赖:没有容器和泛型的基础,并发容器那节只能背结论;没有线程和锁的直觉,JVM 的 GC 日志看半天也看不出所以然。下面按我实际带人走的顺序拆一遍。
2.1 语言基础与容器:先保证写出来的代码不返工
路线图把语言基础拆成基础语法、面向对象、接口、容器、异常、泛型、反射、注解八项,很多人卡在反射和注解,觉得这两块「工作中用不到」。恰恰相反,Spring 的整个骨架就建在这两块上:@Component是注解,Bean 的实例化和依赖注入靠反射。跳过它们直接学 Spring,最后只能记住「加个注解就行」,出问题连从哪儿查都不知道。
我的建议是先花半天把注解和反射串成一个最小可运行的东西,让「元数据 + 运行时读取」这件事变得具体。下面这段代码不依赖任何框架,三十行就能跑出 Spring MVC 里 HandlerMapping 的雏形:
import java.lang.annotation.*; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; @Retention(RetentionPolicy.RUNTIME) // 关键:保留到运行期,反射才读得到 @Target(ElementType.METHOD) // 限定只能标在方法上 @interface Route { String value(); } class Controller { @Route("/user/list") public String list() { return "user-list"; } @Route("/order/list") public String orders() { return "order-list"; } } public class MiniScanner { public static void main(String[] args) throws Exception { Map<String, Method> table = new HashMap<>(); Class<?> clazz = Controller.class; for (Method m : clazz.getDeclaredMethods()) { Route r = m.getAnnotation(Route.class); if (r != null) { table.put(r.value(), m); // 路径 -> 方法,等价于请求映射表 } } Object bean = clazz.getDeclaredConstructor().newInstance(); // 反射调用,等价于框架收到请求后分发到具体方法 System.out.println(table.get("/user/list").invoke(bean)); } }这段代码有三个参数值得抠。RetentionPolicy.RUNTIME决定注解能不能被getAnnotation读到,写成CLASS就什么都拿不到;@Target(ElementType.METHOD)限定注解位置,写错位置编译期就报错,比运行期排查省事;getDeclaredMethods只拿本类声明的方法,父类继承来的要用getMethods,这个差别在写通用工具时经常翻车。跑通之后再看 Spring 的RequestMappingHandlerMapping,会发现它做的就是同一件事,只是加了拦截器、参数解析和返回值处理。
容器这一节的重点不是背 API,而是搞清楚三件事:ArrayList扩容时的数组拷贝代价、HashMap的容量与扩容因子对哈希冲突的影响、ConcurrentHashMap为什么不允许 null 键。这三个问题在面试里出现频率极高,在工作中也真的会咬人——把一个几千元素的ArrayList频繁往中间插,接口 P99 直接翻倍,这种事故我见过不止一次。
异常和泛型容易被当成语法糖跳过。异常的关键是分清「能恢复的」和「不能恢复的」,不要在最外层catch (Exception e)一把梭,那等于给自己埋了个黑匣子。泛型要理解类型擦除,知道为什么List<String>和List<Integer>在运行期是同一个类,这直接决定了你后面写通用返回体时会不会踩到桥接方法的坑。
2.2 并发与 JVM:路线图里最容易被跳过的两块硬骨头
路线图把并发排在 JVM 前面,这个顺序是对的。JVM 的内存模型、GC 日志、线程栈,只有在你写过一段真实的并发代码之后才有观察价值。先啃 JVM 的人,多半会陷入「记住了一堆名词但不知道用在哪」的状态。
并发这块按「基础 → 线程池 → 并发容器 → 原子类 → JUC 工具类」推。基础的synchronized和volatile要能用javap -c看出字节码层面的monitorenter和内存屏障;线程池要能说清七个参数在什么情况下生效;JUC 那组工具类不用全背,但CountDownLatch、Semaphore、CyclicBarrier三者的区别要能一句话讲明白。下面这段代码用来观察线程池的真实行为:
import java.util.concurrent.*; public class PoolProbe { public static void main(String[] args) throws Exception { ThreadPoolExecutor pool = new ThreadPoolExecutor( 4, // corePoolSize 8, // maximumPoolSize 30, TimeUnit.SECONDS, // 非核心线程空闲存活时间 new ArrayBlockingQueue<>(16), // 有界队列,避免无界堆积掩盖问题 new ThreadPoolExecutor.CallerRunsPolicy()); // 满了回退到调用线程 CountDownLatch latch = new CountDownLatch(20); for (int i = 0; i < 20; i++) { pool.execute(() -> { try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断标记,别吞掉 } finally { latch.countDown(); } }); } latch.await(5, TimeUnit.SECONDS); System.out.println("池大小=" + pool.getPoolSize() + " 活跃=" + pool.getActiveCount() + " 队列=" + pool.getQueue().size()); pool.shutdown(); } }参数怎么调,路线图不会告诉你,但这是实战里最常被问的。核心线程数按任务类型估:CPU 密集型取核数加一,IO 密集型可以放到核数的两倍起,但真正的上限由下游决定——数据库连接池如果是 10,你把线程池开到 200,多出来的 190 个线程只是在等连接,除了增加上下文切换没有别的用。队列一定要设边界,LinkedBlockingQueue不传容量默认是Integer.MAX_VALUE,等于把背压问题推迟到内存爆掉那一刻。拒绝策略里CallerRunsPolicy是最温和的一种,它让提交任务的线程自己跑,天然形成限流。
JVM 那一段,路线图列了类加载机制、字节码执行、内存模型、GC、性能监控、调优六项。前四项是理解,后两项是手艺。类加载要能画出双亲委派的过程,说清为什么自定义类加载器能实现热部署;GC 要能根据日志判断是 Young GC 频繁还是 Full GC 频繁,前者多半是新生代太小或者对象晋升太快,后者要看是不是有大对象直接进了老年代。
调优不要一上来就改参数。我一般的顺序是先看现象再动手:
# 每 2 秒采样一次,共 10 次,看 Eden/Old 使用率和 GC 次数增长 jstat -gcutil <pid> 2000 10 # 打印实际生效的 JVM 参数,确认 -Xmx、收集器和你以为的一致 jcmd <pid> VM.flags # 线程栈,排查死锁和长时间阻塞 jstack <pid> > thread.txt # 堆快照,配合 MAT 找大对象和泄漏点,注意文件可能很大 jmap -dump:format=b,file=heap.hprof <pid>jstat的-gcutil输出的是百分比,重点看O(老年代使用率)是不是只涨不跌、FGC次数是不是在短时间内快速增加。jcmd VM.flags比翻启动脚本可靠,容器化部署时经常出现「脚本里写了但没生效」的情况,原因多半是JAVA_TOOL_OPTIONS或者镜像里的默认参数覆盖了。jmap导出堆快照会触发一次较长的停顿,生产环境执行前要掂量时间窗口。
2.3 用脚本把 PDF 目录抽成 CSV:让知识点变成可勾选的清单
脑图类 PDF 有个麻烦:内容明明都在,但没法勾选进度。我习惯先把它转成一张 CSV 清单,导入表格工具,学完一条勾一条,进度可视化的心理作用比想象中大。下面这段脚本用pdfplumber抽文本再按模块归类:
import csv import re import pdfplumber SRC = "java-roadmap.pdf" # 路线图 PDF 路径 OUT = "checklist.csv" # 导出的勾选清单 # 一级模块名,按 PDF 里实际出现的标题微调 TOP = re.compile(r"^(编程基础|JVM|并发|数据结构|计算机网络|数据库|Web开发|后端框架|微服务|前端|研发工具)") rows, current = [], "" with pdfplumber.open(SRC) as pdf: for page in pdf.pages: text = page.extract_text() or "" for line in text.splitlines(): line = line.strip() if not line: continue m = TOP.match(line) if m: current = m.group(1) continue # 过滤页脚、水印一类噪声行 if len(line) < 2 or "扫码" in line: continue rows.append({"module": current, "item": line, "done": 0}) with open(OUT, "w", newline="", encoding="utf-8-sig") as f: w = csv.DictWriter(f, fieldnames=["module", "item", "done"]) w.writeheader() w.writerows(rows) print(f"导出 {len(rows)} 条 -> {OUT}")三个参数说明一下。encoding="utf-8-sig"不是可有可无,不带 BOM 的 UTF-8 用表格软件直接打开大概率乱码;TOP正则必须对着实际 PDF 的标题改,脑图导出时模块名可能带序号前缀或者换行断开;done列先全部置 0,后面在表格里手动改成 1 就行。
有两个坑提前说。第一,如果 PDF 是扫描件或者脑图直接导出的图片型文件,extract_text会返回空字符串,得先走 OCR,这一步的识别成本比想象中高,纯图片版不如直接照着脑图手抄一遍。第二,脑图导出 PDF 的文本顺序不一定是阅读顺序,同一个模块的条目可能跨页散落,跑完脚本务必人工扫一遍,别把分类错误的行直接拿去用。
3. 框架与中间件怎么排期:Spring 家族、缓存、MQ 的依赖顺序
路线图的后半段信息密度陡然上升:Spring 家族、缓存、消息队列、RPC、数据库中间件、微服务组件全挤在一起,新手看到这儿最容易犯的错是「并行开工」——今天摸摸 Redis,明天装个 RabbitMQ,一个月过去哪个都只会敲两条命令。这一段的正确姿势是排依赖:先有 Web 容器和 Spring 的容器,才谈得上整合;先有一条能跑通的读写链路,才谈得上选型。
3.1 Spring 家族的正确打开顺序:IoC 到 Boot 的依赖链
路线图里 Spring 这一支列的是 IoC、AOP、Spring MVC、Spring Boot,后面跟着「自动配置、开箱即用」和「整合 Web、数据库、权限、中间件」。这个顺序不能乱,原因很实在:IoC 解决对象怎么来,AOP 解决横切逻辑往哪儿放,MVC 解决请求怎么进来,Boot 解决上面这些怎么少写配置。跳过前面直接上 Boot,你会得到一堆「能跑但不知道为什么能跑」的代码,出问题时连从哪个注解开始查都没有头绪。
IoC 那一节,我建议手写一个最简陋的容器:扫描包、读注解、反射实例化、按类型注入。写完再回头看ApplicationContext的继承体系,会顺畅很多。AOP 要理解代理的两种实现——JDK 动态代理基于接口,CGLIB 基于继承,这决定了你的类能不能被代理、final方法为什么切不到。这两点在第 4 章的排查里还会再出现一次。
Spring MVC 的九大组件不用背名字,但要能说清一个请求从DispatcherServlet进来之后经过了谁。整合数据库那一段,重点是事务:传播行为的七种取值里,REQUIRED、REQUIRES_NEW、NESTED三个必须能讲出区别,尤其是「同一个类内部方法自调用导致事务失效」这个经典问题,它不是玄学,是代理机制的直接后果。
Boot 阶段的配置我一般会先落一份最小可用的:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai hikari: maximum-pool-size: 10 # 先和数据库最大连接数对齐,不是越大越好 connection-timeout: 3000 # 拿不到连接时快速失败,别让请求一直挂着 idle-timeout: 600000 data: redis: host: 127.0.0.1 port: 6379 timeout: 2s # 缓存超时要短,挂了要能降级到数据库maximum-pool-size是最容易拍脑袋的参数。单实例 10 个连接,四个实例就是 40,数据库的max_connections如果只留了 50,压测时必然出现「拿不到连接」。connection-timeout设成 3 秒是为了快速失败,默认值往往长得多,接口超时了还在等连接,日志里什么都看不出来。Redis 的前缀在不同 Boot 大版本之间有变化,早期是spring.redis.*,后来迁到spring.data.redis.*,照抄网上配置之前先确认自己引入的依赖版本,这是踩过才知道的。
整合权限那一节,Shiro 和 Spring Security 选一个深入就够。Shiro 上手快、概念少,适合中小项目;Spring Security 跟 Spring 生态贴得紧,OAuth2 和 SSO 那块能力更完整。两个都学个皮毛,不如把其中一个的过滤器链吃透。
3.2 缓存与消息队列:先跑通一条链路再谈选型
路线图把 Redis 拆成五大类型、事务、消息通知、管道、持久化、集群,把 memcache 放在旁边做对照。这个拆法很实用,说明作者默认你是要拿它干活而不是应付面试。学 Redis 的正确起点不是背命令,是选一个真实场景把链路跑通,比如「商品详情查询」:先查缓存,命中就返回;未命中查库,回填缓存并设过期时间;更新时先改库再删缓存。这条链路跑通,五大类型、过期策略、缓存穿透和雪崩才有附着点。
环境我一般用 compose 一把起,省得在安装上耗时间:
services: redis: image: redis:7-alpine ports: - "6379:6379" command: ["redis-server", "--appendonly", "yes"] # 开 AOF,重启后数据还在 rabbitmq: image: rabbitmq:3-management-alpine ports: - "5672:5672" # 应用连接端口 - "15672:15672" # 管理台 environment: RABBITMQ_DEFAULT_USER: demo RABBITMQ_DEFAULT_PASS: demo123--appendonly yes决定重启后数据是否还在,做实验时开着更省心,但要知道 AOF 会带来额外的写放大。RabbitMQ 的管理台端口是 15672,业务端口是 5672,这两个搞混是新手常见的翻车点。
消息队列这一节,路线图列了 RocketMQ、RabbitMQ、Kafka 三个。选型不用纠结太久:业务解耦和延迟消息看 RabbitMQ,高吞吐日志和流式处理看 Kafka,国内业务场景里 RocketMQ 的事务消息和顺序消息用得比较多。真正要练的不是装三个中间件,是搞清三个问题——消息丢了怎么办(生产确认 + 持久化 + 消费确认)、重复消费怎么办(幂等键)、积压了怎么办(扩消费者还是加分区)。这三个问题在面试里几乎是必问,在线上也是真会出事的地方。
RPC 那一段列了 Dubbo、gRPC、Thrift 和 Netty。Netty 建议单独拎出来学,它是很多 RPC 框架的底座,理解 Reactor 模型和 ByteBuf 之后,再看 Dubbo 的协议设计会轻松很多。
3.3 微服务组件按「对」来记:一个能力配一个最小验证
微服务这块名词最密,硬背必忘。我的办法是按「能力 → 组件 → 最小验证」三列做表,每学一个能力就填一行,填不出来的说明还没学透:
| 能力 | 路线图里的组件 | 最小验证方式 |
|---|---|---|
| 服务注册与发现 | Eureka、Consul、Zookeeper、Nacos | 起两个实例,停掉一个,看调用方多久感知 |
| 网关 | Zuul、Gateway | 配一条路由,验证转发和鉴权过滤器顺序 |
| 服务调用与负载 | Ribbon、Feign | 两个提供者实例,观察请求是否轮询 |
| 熔断降级 | Hystrix | 提供者人为sleep,看调用方是否快速失败 |
| 配置中心 | Config、Apollo、Nacos | 改配置不重启,验证是否动态生效 |
| 分布式事务 | Seata、TCC、SAGA、LCN | 造一个跨服务扣减,验证异常时是否回滚 |
表格里「最小验证方式」这一列才是重点。注册中心不看「能注册」,看的是「宕机后多久摘除」,默认的心跳和剔除时间往往比你预期的长,压测时会造成一部分请求打到死实例上。熔断组件不看「能配」,看的是超时和线程池隔离有没有生效,很多线上雪崩就是因为熔断配了但阈值设得离谱。
分布式事务那几种模式,2PC 和 3PC 属于理论,XA 是数据库层面的实现,TCC 要自己写三个方法,SAGA 靠补偿,LCN 靠代理连接。真要动手,挑 Seata 的 AT 模式跑一个跨服务扣减的例子,比把六种模式的概念背一遍有用得多。跑完你会明白为什么大多数团队最后的选择是「尽量别用分布式事务,能拆成最终一致就拆」。
4. 落地排查:照着路线图学最容易翻车的五件事
4.1 翻车一:把「收藏」当成「学会」
现象:PDF 存了三个版本,每份都翻了前二十页,问起来知识点都能说出名字,写代码时一个都用不上。
原因:脑图给的是压缩后的结论,缺少推导过程。看结论的成本极低,大脑会误判为已掌握,实际调用的路径根本没建立起来。
解决:强制产出。每学完一个模块,写一段能跑的代码或者一篇三百字的笔记,要求是「不看资料能讲出来」。第 5 章那张周计划表就是干这个用的,每周必须留下一个可运行的东西。
4.2 翻车二:一上来就啃 JVM 调优
现象:刚学完基础语法就去找 GC 调优的文章,参数改了十几个,服务该卡还是卡。
原因:调优的前提是有可观测的故障现象和对照基线。没有真实压力、没有监控数据,改参数全靠猜,越调越乱。
解决:先补并发和监控。用jstat和jstack观察一个自己写的线程池程序,看线程数、队列长度、GC 频率怎么变。有现象再谈参数,第 2 章那组命令就够用。
4.3 翻车三:Spring 版本和教程对不上,类找不到
现象:照着资料敲代码,javax.servlet.http.HttpServletRequest爆红,或者启动时报找不到自动配置类。
原因:Spring Boot 大版本之间存在包名迁移和配置前缀调整,javax.*和jakarta.*分属不同代际,spring.redis.*和spring.data.redis.*也是不同版本的事。资料不会标注版本,照抄必踩。
解决:新建工程时锁死一条版本线,用官方脚手架生成骨架而不是手写 POM;遇到类找不到先看依赖树里实际引入的是哪个坐标,mvn dependency:tree比搜索引擎快。
4.4 翻车四:事务在同类方法自调用时静默失效
现象:saveOrder里调了本类的deductStock,deductStock上标了@Transactional,抛异常后库存没回滚。
原因:Spring 的事务靠代理实现,自调用走的是this引用,绕过了代理对象,注解等于没写。
解决:把方法拆到另一个 Bean 里,或者注入自身代理,或者用TransactionTemplate显式控制。这三种方案各有代价,我一般选第一种,结构上也更干净。排查时打开事务日志,看有没有真的开启事务,比盯着注解猜快得多。
4.5 翻车五:中间件装了一堆,一条链路都没跑通
现象:本地起了 Redis、RabbitMQ、Elasticsearch,每个都能连上,但业务代码里一个都没用起来,问起来只能说「环境搭好了」。
原因:把「安装」当成了「掌握」。中间件的价值在链路里,脱离业务场景单独跑几条命令,记不住也用不上。
解决:一次只跑一条完整链路。缓存就做「查询 → 未命中 → 回源 → 回填 → 更新失效」,消息队列就做「下单 → 发消息 → 消费扣减 → 幂等校验」。一条链路打通,比装五个中间件有用。
5. 把路线图压进 12 周:时间盒、周产出与自测口径
路线图最大的问题是没有时间维度,十一大块平铺在那儿,看久了会产生「永远学不完」的压迫感。我的做法是把它折叠成 12 周,每周只对一个主题,且必须留下一个能跑的东西。周产出不为难人,一个类、一段脚本、一份压测记录都算,关键是「能拿出来给别人看」。
5.1 周粒度排布:每周对应一条可验收的产出
| 周次 | 主题 | 周产出(可验收) |
|---|---|---|
| 第 1 周 | 语法、面向对象、异常 | 一个带自定义异常的 CLI 小工具 |
| 第 2 周 | 容器与泛型 | 手写简化版 HashMap,能说明扩容过程 |
| 第 3 周 | 反射与注解 | 第 2 章那个路由扫描器,加参数解析 |
| 第 4 周 | 并发基础与线程池 | 一份线程池压测记录,含队列长度变化曲线 |
| 第 5 周 | JUC 与并发容器 | 用 CountDownLatch 编排的多任务汇总程序 |
| 第 6 周 | JVM 内存与 GC | 一次 OOM 复现 + jmap 分析笔记 |
| 第 7 周 | 数据结构与算法 | 三十道题的手写笔记,按题型归类 |
| 第 8 周 | 网络与数据库 | 一个带索引优化的慢查询改造案例 |
| 第 9 周 | Spring IoC/AOP/MVC | 不依赖 Boot 的纯 Spring Web 小服务 |
| 第 10 周 | Spring Boot 整合 | 整合数据库、Redis、事务的完整 CRUD |
| 第 11 周 | 缓存与消息队列 | 一条带缓存和异步解耦的下单链路 |
| 第 12 周 | 微服务组件 | 两个服务 + 注册中心 + 网关的最小集群 |
这张表可以直接挪,但有两处要按自己情况改。一是算法那周,如果你目标岗位对算法要求不高,可以压到半周,把时间让给数据库和框架;二是第 11、12 周,如果手上没有合适的项目,就用一个电商下单场景自己造,别去追什么「高并发秒杀」,场景越简单越容易跑完。
排期还要留缓冲。12 周里有 2 周必然会因为工作加班或者卡在某个问题上停滞,这是常态,不用因为落后就推翻整张表。我一般把周末的半天固定成「补课时间」,专门处理这周没搞定的部分。
5.2 自测口径:从「我看过」到「我能讲清」
每周结束前问自己三个问题,回答得磕巴就说明没过。第一,这个知识点解决什么问题,没有它会怎样?第二,它的边界在哪,什么场景下不适用?第三,如果线上出了相关故障,你的排查顺序是什么?
第三个问题最能筛出「假会」。比如学完线程池,能不能说出「接口变慢、CPU 不高、线程数打满」这种情况该先看什么?答案应该是先看队列长度和拒绝策略的触发次数,再看下游依赖的响应时间,最后才怀疑线程数不够。答不上来,说明只是记住了参数名。
再补一个笨办法:找个不懂技术的朋友,用五分钟把某个概念讲给他听,讲不通的地方就是你的知识缺口。这招在 JVM 和分布式事务上特别管用,因为这两块最容易背结论。
5.3 项目经历与面试考点的映射
路线图里的知识点和简历上的项目描述应该是一一对应的,不对应就说明有块内容你只是「学过」而不是「做过」。做法很简单:把项目里每个技术决策写一行,后面跟上「为什么选它、替代方案是什么、出过什么问题、怎么解决的」。面试官追问的永远是后两问。
举个例子,简历写「使用 Redis 缓存热点数据」,后面应该能接出:为什么用 Redis 不用本地缓存(多实例一致性)、缓存key怎么设计(业务前缀 + 版本号,方便批量失效)、更新策略是什么(先更库后删缓存 + 过期时间兜底)、出过什么问题(缓存击穿导致数据库瞬时压力)。这四问能答顺,比在简历上堆十个中间件名字管用。
6. 进阶用法:给路线图建一张反向索引
正着学是建立结构,反着查才是留住知识。路线图十一大块,学到后面一定会忘前面,这时候别重头再翻,建一张反向索引:每次遇到问题,记一行「现象 → 定位到的知识点 → 属于哪个模块」,攒够三个月,你就有了自己的故障字典。
具体做法是建一个纯文本文件放在笔记目录,每行一条,格式固定成三段。比如「接口偶发超时,线程栈里大量 WAITING 在连接池 → HikariCP 连接数配置 → 后端框架/数据库」,或者「Full GC 频繁,老年代增长快,dump 里大量未释放的集合 → 大对象与引用泄漏 → JVM」。写的时候不用追求措辞,追求的是下次能用关键词搜到。
# 按关键词反查历史记录,快速定位属于哪个模块 grep -rniE "OOM|Full GC|连接池|死锁" ~/notes/incident.log | head -20 # 统计哪类问题出现最多,作为下一阶段的复习重点 awk -F'→' '{print $3}' ~/notes/incident.log | sort | uniq -c | sort -rn第二条命令是我自己加的,-F'→'按箭头切段,取第三段做频次统计,跑出来的结果经常打脸——你以为自己薄弱的是微服务,实际高频出现的是数据库和并发。索引跑满三个月,复习计划就不用再拍脑袋了,哪块记录多就回哪块。
这套习惯是被一次线上事故逼出来的。当时一个 Full GC 问题查了整整两天,最后发现半年前看过几乎一样的案例,笔记里也记过,但存在另一个文件夹里,搜的时候没想到关键词。从那以后我每次排查完都强制往索引里补一行,哪怕只写半句,也一定写。希望帮到你。
本文还有配套的精品资源,点击获取