恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Java入门踩坑笔记:环境配置到Spring Boot实战与蓝桥杯刷题
首页
资讯中心
/
Java入门踩坑笔记:环境配置到Spring Boot实战与蓝桥杯刷题
Java入门踩坑笔记:环境配置到Spring Boot实战与蓝桥杯刷题
发布时间:2026/10/5 4:30:25
从上一篇笔记写完到今天我又在 Java 这条路上摸爬滚打了三周。不吹不黑这段时间可以说是整个入门阶段最痛苦的时期环境配好了又崩、崩了又配数组越界报错看得一头雾水对照教程敲的 Spring Boot 项目根本跑不起来。但恰恰是这些坑让我对很多基础概念的理解从好像懂了变成了原来如此。这篇第二弹笔记我不打算搬概念只想把那些真正踩过、修过、想明白的东西整理出来包括环境配置的细节、面向对象的理解方式、排序和字符串处理在蓝桥杯里的实战玩法以及 Spring Boot MyBatis 这类项目到底该从哪块肉下嘴。如果你也是个 java 小白正在背基础、刷题、写项目这篇记录应该能帮你少熬几个夜。1. 从环境搭建到第一个能跑的程序——小白必须先补的基础课1.1 JDK、JRE、JVM 的关系以及 Windows 11 环境变量正确配置方式很多教程上来就让你装 JDK、配环境变量但从来不讲为什么要配。结果就是配错了不知道错在哪配好了也不知道自己在干嘛。我用一个特别土的比喻给你捋清楚JVM 是机器它负责把字节码翻译成当前操作系统能懂的指令JRE 是运行车间里面装着 JVM 和一堆运行时要用的类库JDK 是整条流水线除了包含 JRE还额外提供了 javac 编译器、调试工具、打包工具这些开发用的家伙什儿。所以你只运行别人写好的 jar 包装个 JRE 就够你要是自己写代码、编译代码必须装 JDK。配置环境变量这件事理论上一句话就能说清让系统在任何目录下都能找到 java 和 javac 这两个命令。但新手配的时候容易犯三个错。第一个是只改 PATH 不设 JAVA_HOME很多框架和工具比如 Maven、Tomcat启动时是要靠 JAVA_HOME 找 JDK 的不设的话后面全是坑。第二个是把 JDK 装在了带空格或中文的路径下虽然现在新版 JDK 对中文路径容忍度提高了但保不齐哪个老项目就崩给你看装到C:\Java\jdk-17这类干净路径是最稳妥的。第三个是在命令行里配了临时变量当时能用一关窗口就失效。正确做法是在 Windows 11 的系统环境变量里新建 JAVA_HOME值填 JDK 安装根目录比如C:\Java\jdk-17然后在 PATH 里新增一条%JAVA_HOME%\bin。注意一定要用相对引用不要直接写成绝对路径不然以后换了 JDK 版本还得回头改 PATH。配完以后新开一个终端输入java -version和javac -version两个都能正常打印版本号才算真正配好。这里还有个很阴间的细节如果你之前装过其他版本 JDKPATH 里可能有多个 java 相关路径Windows 是按顺序找的先找到哪个就运行哪个所以版本不对时先去检查 PATH 顺序而不是急着卸载重装。1.2 Java 的八种基本数据类型和命名规则——看似简单但常被扣分的细节数据类型这块内容基本所有教材都会列个表让你背但真正该记的是什么时候用哪个、转类型会出什么问题。Java 的基本数据类型总共八种byte、short、int、long 管整数float、double 管小数char 管单个字符boolean 管真假。日常写代码用 int 和 double 占绝大多数long 在涉及时间戳、金额这类大数值时才需要float 反而很少直接用因为精度容易出幺蛾子金融类场景我见过不少团队直接用 BigDecimal 替代。新手最容易踩的坑是类型转换。小范围转大范围是自动的比如 int 赋值给 long没问题但大范围转小范围就得强转比如long a 300L; int b (int) a;一旦数值超过 int 上限强转出来的结果直接是个负数而且不报错。我门初学阶段就干过这种事用 int 存两个 int 相乘的结果算完发现负值我还以为是逻辑写错了查了半天才意识到是溢出了。这个问题的正确姿势是凡是可能超过 21 亿的中间结果先用 long 接着别嫌麻烦。再就是标识符命名规则。类名用大驼峰UserInfo方法和变量用小驼峰getUserInfo、userInfo常量全大写加下划线MAX_SIZE包名全小写。这是规范不遵守不会编译报错但面试官和看代码的人会给你扣印象分。还有个连老手都可能翻车的点标识符可以用$和_开头比如_name合法但没人建议你这么做因为下划线开头在有些编码规范里被保留给特殊用途老老实实字母开头最安全。另外goto和const是 Java 的保留字不能用很多教材会特意提但新手往往记不住的是true、false、null也是不能当变量名的。1.3 让第一个项目真正跑起来启动失败的典型场景新手第一次写 Java 代码报错很难看懂编译错误和运行错误是两回事。编译错误发生在 javac 阶段通常是语法不对、类名文件名不匹配JVM 根本不会运行这个程序运行错误发生在 java 阶段程序已经跑起来了只是中途炸了。用大白话说编译错误等于车还没出厂运行错误是路上撞了护栏。最常见的启动失败场景有三个。第一个是类名和文件名不一致比如文件名是Hello.java但 public class 名是HelloWorldjavac 直接报错。这个错新手最容易犯因为它不影响阅读代码但编译器只认文件名里的公开类名。第二个是 main 方法签名写错比如漏了static、把String[] args写成String args[]都能跑但把整个签名改成public void main(String[] args)就不行JVM 找不到入口自然报错。这里顺带提醒一句main 方法是程序入口不是语法规则它只是 JVM 的一个约定所以你写十个类但只有一个 main 方法时运行的是有 main 的那个类。第三个常见错误是编码问题。Windows 默认编码是 GBK而很多编辑器默认用 UTF-8 保存文件编译时如果不指定编码中文字符串直接乱码甚至编译失败。解决方案是编译命令显式加上-encoding UTF-8IDE 里也统一把项目编码设成 UTF-8这个习惯最好第一天就养成等以后接手别人项目时你会发现编码混乱是比业务复杂更让人崩溃的事。2. 核心语法进阶面向对象、数组异常和枚举这些硬骨头2.1 封装、继承、多态——用生活中的例子把 OOP 讲明白面向对象对新手最大的障碍是看不见摸不着不像变量和方法那么直观。我后来是用点餐的例子才真正转过弯来。封装就是服务员只给你菜单不让你进后厨随便翻调料你只管下单至于菜怎么炒、煤气灶怎么开那是后厨的事。对应到代码里就是类内部的数据用 private 藏着对外只提供 public 的方法去操作。为什么非要藏因为如果所有字段都公开外面一个不小心就能把数据改成一坨垃圾以后没法维护。合理的封装能让你改内部实现而不影响外部调用这是工程项目最看重的特性之一。继承就好理解了子类继承父类的属性和方法相当于富二代自带家产还能再置办自己的新财产。但这里有个被很多人误解的点继承并不是复用代码的最好方式过度继承会导致类层次又深又脆改父类一嗓子吼整个系统都要跟着振。实际工程里组合把一个对象放进另一个对象里往往比继承更灵活新手可以先用继承感受便利但心里要有根弦能用组合优先用组合。多态是这三个概念里最抽象的。一句话版本同一个行为在不同对象上有不同的表现。比如让动物叫猫是喵喵喵狗是汪汪汪。代码里的经典写法是父类引用指向子类对象然后调用同一个方法实际执行的是子类重写的版本。好处是写业务逻辑时不用一堆 if-else 判断类型直接面向父类编程新类型加入时老代码不用动。这个概念后面学 Spring Boot 时会反复遇到因为框架大量依赖接口和多态来解耦所以这一关不过后面会很痛苦。2.2 数组越界、空指针、深浅拷贝——新手绕不开的三个坑数组越界和空指针这两个异常基本能占据新手报错总量的半壁江山。数组越界的核心原因是想当然地把长度当成了最后一个索引。长度为 5 的数组有效下标是 0 到 4但新手经常拿 5 去访问最后一个位置于是ArrayIndexOutOfBoundsException就来了。看报错信息时别只盯着异常名字后面那行Index 5 out of bounds for length 5才是关键。日常避免这块最好养成遍历时用 i arr.length 的习惯而不是 i arr.length。空指针异常NullPointerException更让人头疼因为报错信息在旧版本 JDK 里经常只给个行号具体哪一行哪个变量是 null得自己去一行行找。JDK 17 之后报错信息变友好了会把哪个对象为 null 直接说出来所以能用新版本就别守着老版本。空指针的成因九成是一个方法返回了 null你没检查就直接调用了它的方法或字段。我自己的习惯是对象可能为空时要么用 if 判空要么用Objects.requireNonNull提前暴露问题要么用 Java 8 的Optional封装返回值别让 null 在代码里到处流窜。深浅拷贝这块概念不复杂但坑很深。赋值复制的是引用两个变量指向同一个对象改一个另一个跟着变想真正复制一份内容可以用clone()或者手动 new 新对象。问题是clone()默认是浅拷贝对象里面如果套着另一个对象拷贝完两个对象内部的子对象还是同一个。所以写业务代码时会遇到我明明拷贝了但修改数据却影响了原对象的诡异现象十有八九就是浅拷贝的锅。解决方案是拷贝对象后把内部的可变引用字段也单独拷贝一遍或者干脆用序列化方式实现深拷贝又或者直接调用那些已经设计好深拷贝逻辑的工具方法别嫌麻烦这种坑排查起来的时间成本远比写代码时多花五分钟要高。2.3 枚举类型实战比常量和 if-else 好用在哪如果没有枚举新手描述状态时通常会写一堆常量int STATUS_SUCCESS 1; int STATUS_FAIL 0;然后业务里到处都是if (status 1)这种魔法数字。不仅可读性差而且看代码的人完全不知道 1 代表什么。枚举本质上是一种受控的常量它让状态变成一个明确定义的类型并且把相关的行为也能打包进去。我举个例子。订单状态有待支付、已支付、已发货、已完成定义成一个枚举public enum OrderStatus { PENDING_PAYMENT(0, 待支付), PAID(1, 已支付), SHIPPED(2, 已发货), FINISHED(3, 已完成); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } }使用的时候直接OrderStatus.PAID.getCode()既不会写错数字又可以直接拿到描述文字。如果以后要加一个已取消只在枚举里加一项就行不用去翻老代码改一堆魔数。更高级点的用法是让枚举自己管行为比如每种状态定义自己的处理方法然后用switch (status)一行分发代码整洁不少。面试聊到枚举时能说出枚举本质上就是类可以有构造方法、字段和方法这句话就已经赢过一部分没深究过的人了。3. 算法能力与竞赛入门从冒泡排序到蓝桥杯3.1 冒泡排序手写及优化——面试和竞赛的第一道门槛冒泡排序是算法入门第一个正儿八经的排序算法虽然实际开发没人用它但面试官爱问、竞赛入门也绕不过因为它能考察你对循环嵌套交换的基础掌握程度。基础版本长这样public static void bubbleSort(int[] arr) { int len arr.length; for (int i 0; i len - 1; i) { for (int j 0; j len - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } } }写对只是第一步能讲清楚原理才合格。外层循环控制的是第几趟排序每趟会把当前未排序区间的最大值冒到最右边所以内层循环的边界要减去 i因为最后 i 个位置已经排好了不用再比较。追求更高的分数就得说优化如果某一趟下来没有任何交换说明数组已经有序直接 break这就是提前终止优化。public static void optimizedBubbleSort(int[] arr) { int len arr.length; for (int i 0; i len - 1; i) { boolean swapped false; for (int j 0; j len - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped true; } } if (!swapped) { break; } } }进阶一点的版本是双向冒泡鸡尾酒排序一次从左往右找最大值一次从右往左找最小值效率略微提升。但说真的刷竞赛题时别纠结冒泡排序本身你真正该练的是理解和手写它因为很多排序类题的暴力解法都会用到它而它能保证你在没思路时有个保底方案可用。同时也要知道工程里排序直接用Arrays.sort()或者Collections.sort()底层是更高效的排序算法Java 的官方实现已经优化得相当好。3.2 字符串与字符判断常用库函数和正则蓝桥杯的入门题里数字和字符串的题目占比不小判断字符串里是否只有字母和数字这类需求几乎是常客。如果从头写判断逻辑得遍历每个字符手动判断它是不是数字、字母代码啰嗦且容易漏。Java 给我们准备了不少现成的库函数我记得最常用的就是Character类。String s abc123; for (int i 0; i s.length(); i) { char c s.charAt(i); if (!Character.isLetterOrDigit(c)) { System.out.println(包含非字母数字字符); break; } }isLetterOrDigit一次搞定字母加数字的判断isDigit管纯数字isLetter管纯字母。再偏一点的场景比如需要判断是否包含中文字符可以用Character.UnicodeScript.of(c)判断脚本类型但更常用的还是直接上正则表达式比如String.matches([a-zA-Z0-9])一行代码完成整个判断。正则表达式的学习曲线确实陡但它是字符串处理绕不开的利器蓝桥杯里有不少题看似复杂用正则拆解后就是几行代码的事。这里给个建议刷题时不要只会用库函数还要理解函数背后的实现思路。比如String的split方法用正则切字符串非常方便但面试官偶尔会追问不用 split 你怎么实现这时候临时手写就会慌。我自己刷题时会把常用的字符串方法挨个在本地写一遍charAt、substring、indexOf、contains、replace、split每个都搞清楚返回什么、参数是什么、边界情况怎么处理后面写代码时基本不用翻文档。3.3 蓝桥杯数字类题目怎么破进制、质数与数字统计蓝桥杯省赛的编程题很大一部分落在数字处理上这类题难在思维不在代码量。举几个典型类型说一下路。第一种是进制转换题目给你一个十进制数让你转成二进制、十六进制或者反过来转。Java 里可以直接用Integer.toBinaryString(n)、Integer.toHexString(n)速度快但不明原理。想靠竞赛拿分得会手写短除法不断除以目标进制余数从下往上读。我个人建议竞赛代码用库函数但备考时一定要推演一遍原理因为有些变种题比如任意进制互转库函数就不够用了得自己写映射表。第二种是质数题目比如判断素数、打印质数表。最朴素的写法是循环到n/2判断余数但效率差稍微优化一点循环到√n复杂度骤降。再往上走可以用埃氏筛一次性把某个区间内的所有质数全部筛出来这个算法我强烈建议新手背下来它既经典又实用在竞赛里的出场率极高。第三种是数字统计类比如统计 1 到 n 里某个数字出现多少次。很多新手第一反应是转成字符串再遍历思路没错但数据量大时会超时。更稳的解法是逐位分析对每一位算清楚这一位上目标数字出现多少次利用数学关系来统计。这类题本质是考数学思维平时刷题要多琢磨能不能不用暴力枚举因为 Java 的优势不在极致的运行速度而在写清楚、写标准所以能用数学优化就尽量用数学优化。再补充一点竞赛经验蓝桥杯和很多算法比赛不同它更看重你能否写出能拿部分分的代码。就算完全没思路也别空着暴力枚举、打表这些小聪明能用就用。哪怕只能过部分测试点也比 0 分强太多。比赛时先通读所有题目挑会做的拿稳分别在一道题上耗到没时间看别的题我在模拟赛里吃这个亏吃了好几次。4. 从基础到工程Spring Boot、MyBatis 与面试快速通关4.1 Spring Boot MyBatis 多商户商城项目到底怎么拆解学到一定程度就会想碰项目Spring Boot MyBatis 是 Java 后端最常见的技术组合很多开源商城项目的源码结构也是基于这套。新手第一次拿到这类项目源码最常犯的错是层层点开文件越点越懵。我建议换个姿势先看整体分层再追一条请求链路。Spring Boot 项目的典型分层是 Controller、Service、MapperDao。Controller 负责接收 HTTP 请求翻译参数、返回 JSONService 装业务逻辑比如下单时要减库存、算价格、生成订单号Mapper 负责和数据库打交道通过 MyBatis 的 XML 文件或注解把 SQL 和 Java 方法绑定。像开源的多商户商城项目多出来的部分一般是多了一个商户维度比如商品要区分是哪家商户的订单也要归属到商户那么 Mapper 层 SQL 里就会多一个merchant_id条件Service 层也有一个模块专门管理商户的入驻、上下架、结算这些逻辑。我拿用户下单这个场景举例你觉得应该先看哪几个文件答案是先找到 Controller 里的下单接口入口看它调用了哪个 Service 方法再顺着 Service 方法看它调用了哪些 Mapper 方法最后看 Mapper 的 XML 里 SQL 是怎么写的。这一条链走通整个项目的套路基本就摸透了。新手最容易忽略的是实体类POJO和数据库表的对应关系比如tb_order表对应Order类字段映射在 XML 的resultMap里定义如果不看resultMap你会觉得代码里明明调用了某个字段数据库里却没有其实只是没映射上。还有一个掏心窝子的建议不要下载了源码就看看完就忘。动手改点东西哪怕只是把某个商品上架流程里限制数量从 10 改成 20再重新跑起来看效果都比读十遍源码有用。如果能把项目里的一个功能模块报错、排查、修复的过程完整记录一遍那这项目才算真正开始属于你。4.2 定时任务、行级权限和数据一致性——新手如何理解工程问题读项目源码时会撞见一些平时自学碰不到的工程概念其中定时任务、行级权限和数据一致性这仨我认为新手必须有个基本认知。定时任务说白了就是到点了让程序自己去干活典型场景是每天凌晨对账、定期清理过期数据。Spring Boot 里做定时任务最简单的方式是Scheduled注解在方法上标个Scheduled(cron 0 0 2 * * ?)到每天凌晨两点就自动执行。要是任务再复杂点比如要支持集群部署后同一个任务在多个节点上只执行一次就得引入分布式任务调度框架Quartz 那套东西就派上用场了。新手看到框架两个字容易慌但你只要理解核心就够定时任务 定时策略 任务执行逻辑 防止重复执行的锁。行级权限这词听起来高级实际意思就是不同的人看同一张表只能看到属于自己的数据。比如商户商城系统里商户 A 登录后只能操作自己的商品和订单不能看到商户 B 的这就是典型的行级权限。实现思路通常是在 SQL 里自动追加权限过滤条件MyBatis 可以通过拦截器在执行 SQL 时动态拼上where merchant_id 当前登录用户也可以简单地写死每个查询条件。理解这个概念的意义在于你会明白不是所有权限问题都要靠复杂的框架解决有时候一个where条件就够了。数据一致性就是多个数据别打架。最经典的场景是转账A 扣钱和 B 加钱必须同时成功或同时失败不能 A 扣了 B 没加那就乱套了。Java 后端保证一致性最常靠数据库事务Spring 里用个Transactional注解把方法包起来里面任何一个操作抛异常整个方法数据回滚。新手看到这个注解以为加上就万事大吉其实还有个大坑如果方法内部自己捕获异常吞掉不抛出事务根本不会回滚。这个知识点面试很喜欢考做题背八股文的时候一定要把这个场景想明白光背事务是保证一致性的机制这句话没卵用得能讲出回滚的原理和异常处理的关系才行。4.3 从免费入门网站到面试八股文怎么把学到的知识串起来很多刚学 Java 的朋友会问网上资源那么多到底该看哪个这个问题我自己的回答是资源不重要路线才重要。先把 Java 基础语法过完然后学面向对象、集合框架、异常处理、I/O 这些核心库再上手数据库和 SQL跟着做一两个简单的 Web 项目最后才轮得到 Spring Boot。这个顺序走完再去看免费入门网站或者视频教程时你会发现自己能判断哪些内容有货、哪些纯属扯淡了。免费资源这块B 站上有很多成体系的 Java 入门视频直接搜 Java 入门就能找到文档类的话菜鸟教程适合快速检索它的覆盖广度和例子清晰度对新手来说足够友好再进阶一点去看 Oracle 官方文档和 Java API 文档刚开始会觉得生涩但它是所有信息的一手来源长期收益很大。我建议的学习组合是视频建立整体认知文档查漏补缺代码实操验证理解四者缺一不可。Java 八股文这个词是面试圈的黑话指的是那些高频出现的理论问题比如 HashMap 底层结构、JVM 内存区域、线程池参数含义等等。新手看八股文容易陷入一个误区觉得背下来就能面试过关结果一被追问细节就露馅。正确的打开方式是把一个八股文问题当成一个知识锚点向上理解它背后的原理向下写一段能验证它的代码。比如背到HashMap 在链表长度超过 8 时转红黑树就去写代码往 HashMap 里塞几十个相同哈希值的对象观察结构变化这样知识就不只是嘴里背的而是亲手验过的东西。把面试题当成检验学习效果的工具而不是学习的终点这个心态能让你少走很多弯路。5. 常见问题与排错速查表——新手实战记录5.1 环境、版本和编码问题速查我给这段时间遇到的坑做个速查表解决报错知道看哪里的问题报错/现象常见原因排查顺序java: 找不到符号类名写错了 / 变量名不一致先看报错行再核对大小写和拼写编译通过但运行时报NoClassDefFoundErrorclass 文件缺失或 classpath 不对检查编译输出目录确认产物生成位置端口被占用上一次程序没停干净或者其他服务占了端口要么换端口启动要么找到占用进程结束掉中文乱码文件编码和编译/运行编码不一致统一 UTF-8 编码编译时加-encoding UTF-8JDK 版本和项目要求不一致PATH 里多个 JDK或者 IDE 没切换 JDK先java -version看默认版本再看 IDE 项目设置我另外补一个很实用但少有人提的技巧看报错别只看最后一行。堆栈日志是从上往下读的真正出问题的代码位置通常在堆栈靠近顶部的位置最后一行反而是外部调用链。新手最容易干的事是拿着最后一行去百度搜半天也不对症正确姿势是把顶部那行带项目包名路径的代码位置打开看看那大概率就是爆炸现场。5.2 接口自动化测试、调试和常用工具写代码只是开发的一部分调试和测试才是每天花时间最多的地方。新手学会用断点调试比什么都重要。在 IDE 里先在疑似出问题的行左边打个断点然后以 Debug 模式运行程序会在断点处暂停这时你能看到当前所有变量的值、调用栈、线程状态。我之前调试数组越界时就是靠断点发现循环变量多算了一次如果只靠打印大法可能得猜半天。接口自动化测试这个东西听着工程味十足其实核心就是用脚本代替人反复点接口。Java 生态里常用的工具包括 Postman接口调试、JUnit单元测试、RestAssured接口自动化。新手阶段不必每个都精通但至少要把 Postman 玩熟因为不管以后做项目还是面试聊项目都绕不开我怎么验证自己的接口是通的这个问题。写测试用例这件事我的建议是不要等到项目做完再写写一个方法就顺手写对应的测试虽然前期速度会慢一点但后面改代码时你会感激自己。调试过程中我最想强调的习惯是改完代码先想想会不会影响其他地方再去运行。很多新手陷入改一行跑一次、报错再改一行的恶性循环根本原因是没搞清程序依赖关系就开始瞎试。正确做法是先读代码把逻辑理顺推测可能的问题点改完以后至少在心里过一遍调用链再动手运行长期下来效率高得多。5.3 我的 30 天 Java 自学路线和时间安排复盘最后复盘一下我这三周的实际时间分配。第一周主要用来补环境基础JDK 安装、环境变量、IDE 配置、Hello World外加数据类型和运算符这些纯语法。第二周开始啃面向对象封装继承多态这三个概念花了很多时间之后练数组和集合框架把常见异常排错练到肌肉记忆。第三周开始接触算法题从冒泡排序、字符串处理入手同时抽空看了 Spring Boot 的入门视频虽然项目没跑通但对 Controller、Service、Mapper 这套分层有了大致的画面。我踩过最大的坑是贪多。看到网上推荐的 Java 学习路线图特别全心急想一天看完一部分结果啥都记不住。后来我把每天的任务拆得很具体比如今天只搞懂 HashMap 的 put 流程、今天手写一个整数字符串互转目标越小越容易落地。另一个教训是手和脑必须同步看视频时跟着敲代码哪怕就是复制一遍也比纯看强十倍因为敲的过程中你会注意到很多视频里一晃而过的细节比如方法名字母大小写、括号匹配这些。如果让我给刚开始学 Java 的人一句话总结这套路先把基础语法用代码全部验证一遍再写十几个算法题打底然后带着问题去看 Spring Boot 项目源码最后用面试题查漏补缺。这套顺序不一定适合所有人但至少能让你在每个阶段都知道自己在学什么、为什么学。我还在路上这篇第二弹记录写给正在看文章的每个同样焦灼又坚持的初学者也写给我自己下一弹再见时希望我分享的已经是更硬核的东西了。