1. 入门前的准备:先搞清楚 Java 到底是什么
很多人学 Java 是被那句“Java 是世界上使用最广的语言之一”吸引进来的,但真到上手时,连 JDK 和 JRE 都分不清,环境变量一配就崩。这篇教程我就按自己带新人的经验,把 Java 入门这条路上最容易被卡住的地方一个个拆开讲。不管你是刚接触编程的零基础小白,还是想从 Python、前端转过来的开发者,按这个顺序走都会轻松很多。
先说清楚一个概念。Java 是一门编译型加解释型混合的语言,源码写在一个 .java 文件里,通过 javac 编译成字节码,再交给 JVM(Java 虚拟机)解释执行。这个设计带来的最大好处是跨平台:同一份字节码,只要有对应系统的 JVM,就能直接跑。Windows 上写好编译出来的 class 文件,扔到 Linux 服务器上,只要那边装了 JDK 或者 JRE,就能运行。这也是 Java 在企业后端、大数据、Android 开发中称霸这么多年的根本原因。
学习 Java 最核心的一条主线,就是围绕 JVM 展开的。你在入门阶段不需要深入了解 JVM 源码,但至少要知道:Java 代码是怎么从源文件变成可运行程序的,堆和栈是什么,垃圾回收大概在做什么。这些概念后边都会被面试官反复问到,越早建立认知越好。
网上现在流行一个词叫“Java 八股文”,说的是面试常考的那些固定知识点。很多初学者会陷入一个误区,觉得学 Java 就是背面试题。我不建议零基础的人一上来就奔着面试题去,原因很简单:八股文能帮你过关,但过不了试用期。真正的 Java 学习应该以“能写、能调、能部署”为目标,面试题只是最后阶段用来查漏补缺的工具。所以这篇教程的每一部分,我都会把“学”和“考”两条线都点一下,学的时候知道考点在哪,但重心还是放在动手能力上。
2. 搭建开发环境:JDK 安装与环境变量配置
2.1 JDK 版本怎么选,IDE 用哪个
先解决工具问题。目前最主流的 JDK 版本是 Java 8 和 Java 17,很多公司老项目还在用 Java 8,新项目逐渐往 17 甚至 21 迁移。对于入门学习,我建议直接装 JDK 17,因为 8 太老,很多新特性用不上,而 17 是长期支持版本,社区资料也足够多。
下载地址直接去 Oracle 官网或者使用开源的 OpenJDK 发行版,比如 Eclipse Temurin、Adoptium,都是正规渠道。国内网络环境下,部分官网链接下载速度可能比较慢,也可以选择国内云厂商提供的镜像站,搜索“JDK 镜像”就能找到。安装过程没什么特别,Windows 下就是一路 Next,但要注意安装路径最好不要带空格和中文,我习惯统一装在D:\Java\jdk-17这样的路径下。
IDE 的选择上,新手我推荐 IntelliJ IDEA Community Edition,免费且功能够用。如果你电脑配置比较低,也可以用 Eclipse,但现在很多公司都在用 IDEA,早点熟悉操作习惯没坏处。VSCode 配合 Java 插件也能写,但调试体验和项目管理不如 IDEA 顺手,入门阶段不建议折腾。
2.2 环境变量配置的完整步骤与常见误区
JDK 安装完成之后,最重要的就是配置环境变量,这一步能卡住至少一半的新手。配置的核心是三个东西:JAVA_HOME、PATH 和 CLASS_PATH。
JAVA_HOME 是一个约定的变量名,指向 JDK 的安装目录。很多框架和工具(比如 Maven、Tomcat)启动时会去读这个变量,所以必须配。PATH 的作用是让系统在任意目录下都能直接找到 javac 和 java 命令。至于 CLASS_PATH,老版本的 JDK 需要手动指定类搜索路径,但 JDK 5 以后已经不需要配置它了,网上很多旧教程还在教你配,属于典型的“历史包袱”。
Windows 下具体操作步骤:右键“此电脑” -> 属性 -> 高级系统设置 -> 环境变量。在系统变量里新建 JAVA_HOME,变量值填 JDK 安装路径,比如D:\Java\jdk-17。然后在系统变量里找到 Path,双击编辑,新建一行%JAVA_HOME%\bin,注意不要写成%JAVA_HOME%\bin\带多余分号,那是 Windows 老版本的做法,现在新建条目即可。
Linux 和 Mac 下的配置思路一样,只不过改的是 shell 配置文件。以 Ubuntu 为例,在~/.bashrc末尾追加两行:
export JAVA_HOME=/usr/lib/jvm/jdk-17 export PATH=$JAVA_HOME/bin:$PATH然后执行source ~/.bashrc让它立即生效。
配完环境变量后,打开一个新的命令行窗口,输入java -version,如果能看到类似openjdk version "17.0.5"的输出,说明配置成功。这里有个我见过很多次的坑:环境变量配置完成后,如果你是在旧窗口里执行命令,永远看不到效果,因为环境变量是在进程启动时读取的。新开一个终端窗口再试,或者干脆重启一次电脑,问题就解决了。
第一个 Java 程序没必要搞复杂,就写一个 HelloWorld 验证环境。用记事本或者 IDE 创建一个Hello.java,内容如下:
public class Hello { public static void main(String[] args) { System.out.println("Hello, Java!"); } }然后在命令行里进入文件所在目录,依次执行:
javac Hello.java java Hello注意java命令后面不要加.class后缀,这是新手最常犯的错误。第一次能在这个黑窗口里看到输出,恭喜你,Java 的大门已经打开了。
3. 语法地基:数据类型、运算符与常用类
3.1 基础语法速览与数据类型详解
Java 的基础语法和大部分 C 系语言类似,但有几个地方需要特别留心。首先是数据类型,Java 分为基本类型和引用类型两大类。基本类型有八种:byte、short、int、long、float、double、char、boolean。初学者最容易忽略的是它们的取值范围和默认值。比如 int 在 32 位下最大值是 2147483647,如果计算金额时超过这个数还不换 long,就会出现数据溢出,结果变成一个莫名其妙的负数。很多银行类面试题就喜欢拿这个做文章。
长期经验告诉我,新手写代码时最容易犯的几个数据类型错误:
- 两个整数相除,结果赋值给 double,以为会得到小数。比如
double x = 5 / 2;,结果不是 2.5 而是 2.0,因为 5 和 2 都是 int,先做整数除法再去转换。正确写法是double x = 5.0 / 2;。 - 用 float 表示精确小数,导致精度丢失。金额计算永远用 BigDecimal,这个坑等你做财务系统时会理解得特别深刻。
- 字符串拼接时,char 类型变量和数字混在一起,结果完全超出预期。
运算符这一块,Java 的运算符按照优先级从高到低大致排列如下表:
| 优先级 | 运算符类型 | 举例 |
|---|---|---|
| 高 | 后缀运算符 | expr++expr-- |
| 递减 | 一元运算符 | ++expr--expr!~ |
| 乘法类 | 算术运算符 | */% |
| 加法类 | 算术运算符 | +- |
| 关系运算 | 大小比较 | <><=>=instanceof |
| 相等运算 | 值比较 | ==!= |
| 逻辑与 | 逻辑与 | && |
| 逻辑或 | 逻辑或 | || |
| 较低 | 赋值运算 | =+=-=等 |
这个优先级表至少要能背下来前几行,因为面试里考察运算结果是很常见的题型。给你一道高频题目:执行完下面的代码,a 和 b 分别是多少?
int a = 10; int b = a++ + ++a;如果不熟练,很容易算错。实际过程是:a++ 先返回 10,此时 a 变成 11;++a 先把 a 变成 12,再返回 12,所以 b 等于 22,a 最终是 12。这类题没什么技巧,就是靠对“先取值后自增”和“先自增后取值”的理解。
3.2 字符串与常用类:String、StringBuilder、包装类
Java 里的 String 是引用类型,但它有个特殊之处:不可变。每次对 String 进行拼接、替换操作,本质上是创建了一个新对象。这就导致一个典型的性能陷阱,在循环里做字符串拼接,会产生大量临时对象。我见过有人用+在 for 循环里拼了上万次,程序直接卡到令人抓狂。正确做法是使用 StringBuilder:
StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 10000; i++) { sb.append("第").append(i).append("条\n"); } String result = sb.toString();面试里经常追问一个问题:StringBuffer 和 StringBuilder 有什么区别?答案是前者线程安全、性能略低,后者非线程安全、性能更高。单线程环境下无脑选 StringBuilder 就对了。
常用类里,包装类(Integer、Double 等)也是面试常客。Java 的自动装箱和拆箱机制让基本类型可以直接和包装类互相转换,但这里面藏着一个缓存机制:Integer 默认缓存了 -128 到 127 之间的对象。所以Integer a = 100;和Integer b = 100;,a == b 是 true;但改成 200 就变成 false 了。原因就是两个 200 各自 new 了不同对象。用 equals 比较对象值才是安全的做法。这道题几乎每个 Java 面试题库里都有,属于入门必须掌握的细节。
3.3 流程控制与数组:冒泡排序背后的循环思维
控制流程没有太多可讲的,就是 if、for、while、switch,语法上跟 C 基本一致。但数组这块需要单独提一下,因为 Java 的数组声明方式有两种风格,int[] arr和int arr[],官方推荐前者,可读性更好。数组一旦创建,长度就不能改变,这个特性决定了 Java 中“动态数组”需要用 ArrayList 来实现。
冒泡排序是热词里出现的高频考点,也是很多人的第一个排序算法。它的核心思想是反复交换相邻元素,每一轮把未排序部分的最大值“冒”到最后面。代码实现如下:
public static void bubbleSort(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { boolean swapped = false; for (int j = 0; j < n - 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; } } }注意这里加了一个 swapped 标志位,如果某一轮没有任何交换发生,说明数组已经有序,直接结束。这个优化能让最佳情况下的时间复杂度降到 O(n),也是面试官喜欢追问的细节。初学者看到双层循环容易懵,我的理解方法是:外层循环控制“排好了几个数”,内层循环控制“比较到哪个位置”。每轮结束后,末尾的元素位置就锁定了,所以内层循环的边界要减掉 i。
4. 面向对象:封装、继承、多态与常用集合
4.1 类与对象的设计思路
Java 是一门面向对象的语言,这意味着写 Java 代码不能像写 C 那样单纯堆函数,而是要先思考“这个系统里有哪些对象,每个对象有什么属性和行为”。初学者最困难的就是这个思维转变。我经常打一个比方:类就是图纸,对象就是按图纸造出来的汽车。一张图纸可以造一万辆汽车,每辆车的颜色、车牌号可能都不同,但基本结构一样。
接触面向对象的第一步是定义一个简单的类。比如写一个学生类:
public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void introduce() { System.out.println("我叫" + name + ",今年" + age + "岁"); } }注意这里我把属性都用 private 修饰,外部无法直接访问,必须通过构造方法赋值、通过 getter 取值。这个设计就是封装:隐藏内部细节,暴露有限接口。很多新手不理解为什么搞这么麻烦,直接 public 多省事。但等到项目规模变大,你就能体会到封装的好处:比如 Student 的 age 字段以后要增加“必须大于 0”的校验,如果所有地方都能直接赋值,改动会非常痛苦;而封装在 setter 里改一处代码就够了。
4.2 继承、多态的理解
继承是面向对象的第二个核心特性。子类用 extends 继承父类,可以获得父类的字段和方法,同时可以重写(override)父类的方法。Java 是单继承的,一个类只能继承一个父类,这是为了降低复杂度,避免 C++ 多继承引发的菱形继承问题。
多态则是建立在继承之上的能力:父类引用指向子类对象,调用同一个方法时表现出不同的行为。举个例子:
Animal dog = new Dog(); Animal cat = new Cat(); dog.speak(); cat.speak();如果 Dog 和 Cat 都重写了 Animal 的 speak 方法,运行时会根据实际对象类型决定调用谁的方法。这就是“编译看左边,运行看右边”这句口诀的由来。多态的价值在于写代码时可以面向父类编程,新增动物类型不需要修改原有逻辑,符合开闭原则。
如果你想深入学习 Java 必考的设计模式,动态代理和接口会是一个绕不开的话题。热词里出现的“Java 动态代理”常出现在 Spring AOP 的底层实现里,入门阶段可以亲手写一个简单例子来理解原理。JDK 动态代理基于接口实现,核心类只有两个:InvocationHandler 和 Proxy。代码如下:
interface HelloService { void sayHello(); } class HelloServiceImpl implements HelloService { @Override public void sayHello() { System.out.println("Hello, 我是真是的业务类"); } } class LogHandler implements InvocationHandler { private final Object target; public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Exception { System.out.println("调用前记录日志: " + method.getName()); Object result = method.invoke(target, args); System.out.println("调用后记录日志: " + method.getName()); return result; } }使用代理的入口代码:
HelloService service = new HelloServiceImpl(); HelloService proxy = (HelloService) Proxy.newProxyInstance( service.getClass().getClassLoader(), service.getClass().getInterfaces(), new LogHandler(service) ); proxy.sayHello();运行后你会发现,调用 sayHello 方法的同时,前后都打印了日志。在这个例子中,我们没有改动 HelloServiceImpl 的一行代码,却给它的方法增加了额外功能。这就是 AOP(面向切面编程)的思想雏形。学到这里,你再去理解 Spring 的事务管理、日志切片、权限拦截,就会轻松很多。
4.3 集合框架:面试八股文里的主战场
Java 集合框架是面试中出题最多、区分度最大的知识板块。整体先记住一张宏观图景:Collection 接口下面有 List、Set、Queue,Map 接口单独是一棵大树。List 的特点是元素有序、可重复,主要实现类有 ArrayList 和 LinkedList。Set 的特点是元素不可重复,常见实现类有 HashSet 和 TreeSet。Map 是键值对结构,常见实现类有 HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap。
面试常考的差异点,我整理如下:
- ArrayList 底层是动态数组,查询快、增删慢,LinkedList 底层是双向链表,增删快、查询慢。
- HashSet 底层是 HashMap,只用了键的部分;TreeSet 底层是红黑树,元素会按顺序排列。
- HashMap 的底层在 JDK 7 及以前是“数组 + 链表”,JDK 8 之后变成“数组 + 链表 + 红黑树”,链表长度超过 8 且数组长度大于等于 64 时,链表转红黑树。
- HashMap 是非线程安全的,多线程并发写会造成数据覆盖或死循环(JDK 7 中的经典问题);Hashtable 是线程安全的但性能差;ConcurrentHashMap 采用分段锁或 CAS 机制,是高并发场景下的首选。
有人会问:为什么不直接用数组,还要用 ArrayList?这就涉及到“容器”这个概念。热词里有个“Java 容器”,其实就是指集合框架这一整套处理对象组的技术。核心价值是帮你把常用的数据结构都封装好了,不用每次都从轮子开始造。
具体到遍历,现在最推荐的是增强 for 循环和 stream 流。热词里有个java list.stream().toarray,这是一个很实用的用法,把 List 转成数组:
List<String> list = Arrays.asList("a", "b", "c"); String[] array = list.stream().toArray(String[]::new);这种写法链式风格清晰,后面学了 Lambda 和方法引用之后会常用。但要注意,Arrays.asList返回的 List 是一个固定大小的视图,不能执行 add 和 remove 操作,否则会抛 UnsupportedOperationException。很多新手在这里踩坑。
5. 进阶能力:并发入门、JVM 基础与常见异常排查
5.1 线程与并发:如何等待所有线程完成
Java 并发编程是面试中的重头戏,也是区分初级和中级的明显分界线。但入门阶段不需要一上来就啃 AQS、ReentrantLock 的源码,先掌握线程创建方式、生命周期、基本同步机制就够用。
线程创建的方式主要有三种:继承 Thread 类、实现 Runnable 接口、使用 Callable + FutureTask。推荐后面两种,因为 Java 是单继承,实现了 Runnable 之后还可以再继承其他类,灵活性更高。
热词里有个非常典型的场景:“Java 线程等待都完成”。这个需求在实际开发中特别常见,比如你启动了 10 个线程去下载文件,所有线程都结束之后再合并结果。最简单的实现是使用 CountDownLatch,原理是维护一个计数器,主线程调用 await 原地等待,每个工作线程完成时调用 countDown 减一,减到 0 的时候主线程继续向下执行。示例:
import java.util.concurrent.CountDownLatch; public class ThreadWaitDemo { public static void main(String[] args) throws InterruptedException { int taskNum = 5; CountDownLatch latch = new CountDownLatch(taskNum); for (int i = 1; i <= taskNum; i++) { final int no = i; new Thread(() -> { try { System.out.println("任务" + no + "开始执行"); Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } }).start(); } System.out.println("主线程等待所有任务完成..."); latch.await(); System.out.println("所有任务已执行完毕,继续处理"); } }这里有个很容易被忽略的细节:countDown 最好放在 finally 块里,否则某个线程抛出异常后计数器永远不会减到 0,主线程就会一直等待,造成程序挂死。还可以给 await 设置超时时间,比如latch.await(5, TimeUnit.SECONDS),作为兜底方案。
如果你觉得 CountDownLatch 不可复用,希望小伙伴们干活到一半还能重置计数器,可以用 CyclicBarrier。两者的核心区别是:CountDownLatch 是一个门闩,只能使用一次;CyclicBarrier 是循环栅栏,可以重复使用。这两个类面试必考,原理和区别都要能讲清楚。
5.2 JVM 内存区域与垃圾回收的入门认知
热词里那本书《深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第 3 版)》相信很多人听说过,这是 Java 进阶必读的经典。但入门阶段啃这本书容易劝退,我的建议是只先看第一部分的运行时数据区域和垃圾回收算法,后面的类加载、编译优化等内容等有经验再回来看。
JVM 内存区域划分,我用一句话概括:线程私有的有虚拟机栈、本地方法栈、程序计数器,线程共享的有堆和方法区。堆是存对象实例的地方,也是垃圾回收的主战场;虚拟机栈里每执行一个方法就创建一个栈帧,存放局部变量表、操作数栈等。实际调优时,最常见的是调整堆大小,参数是-Xms和-Xmx,分别代表最小堆和最大堆。生产环境一般把两者设成一样,避免运行时频繁开缩容堆,造成性能抖动。
垃圾回收算法的入门要点是一个口诀:“引用计数法太简单,可达性分析法最常用”。主流的 GC(Garbage Collection)判断对象是否存活,采用的是从 GC Roots 出发向下搜索,走不到的对象就是可以回收的。GC Roots 通常包括栈帧中引用的对象、静态属性引用的对象等。具体回收算法有复制算法、标记清除、标记整理,它们各有优缺点,面试官喜欢问“为什么老年代不用复制算法”,原因很简单:老年代对象存活率高,复制移动成本太高。
这一节的目的不是让你立刻变成 JVM 调优高手,而是建立一个思维框架。等你工作后遇到线上内存溢出(OOM)、CPU 飙升、频繁 Full GC 之类的故障,再回头翻阅那本紫书,你会感谢现在打下的基础。
5.3 高频面试题速览与常见异常排查
入门阶段最容易踩的异常细数下来就几个:
- NullPointerException(空指针异常):使用对象之前没有判空。
- ArrayIndexOutOfBoundsException(数组下标越界):访问了不存在的索引。
- ClassCastException(类型转换异常):强制类型转换时类型不匹配。
- NumberFormatException:把字符串转数字时格式不对。
- ConcurrentModificationException:遍历集合的同时修改集合结构。
排查异常的通用思路就三步:第一,看堆栈最底部的“Caused by”,这是根因;第二,定位到具体行号,看看这一行用到了哪个对象;第三,用 debug 模式在关键变量上打点,观察值为 null 的位置。千万别用catch (Exception e) { e.printStackTrace(); }之后就不管了,这种代码写多了,线上日志会被无意义堆栈刷爆。
热词里还有“java 线程等待都完成”“java list.stream().toarray”“冒泡排序 java”“java 动态代理”等内容,其实都可以在入门路线中找到自己的位置。我给新手一个明确的阶段性目标:第一阶段两到三周搞定环境与语法;第二阶段三周搞定面向对象与常用类;第三阶段两周搞定集合和异常;第四阶段接触 IO、线程和网络编程,并配合做一个小项目。学到这里,你已经有能力看那些“Java 面试大全”了。
6. 学习路线规划与实操总结
6.1 一份可执行的自学路线图
很多人问过我同样的一个问题:“Java 自学路线到底该怎么排?”其实答案并不复杂,但执行起来容易跑偏。我给一份自己总结的、成功率比较高的路线图:
基础语法(两周):变量、数据类型、运算符、流程控制、数组、方法。 面向对象(两周):类与对象、封装、继承、多态、抽象类、接口。 常用类与工具(一周):String、StringBuilder、包装类、日期时间、BigDecimal。 集合框架(两周):List、Set、Map 的用法,底层原理,遍历方式。 IO 流(一周):文件读写、字节流字符流、序列化。 并发基础(两周):线程创建、线程状态、同步、等待唤醒、JUC 下的常用工具类。 网络编程(一周):Socket、HTTP 原理、简单的客户端服务端。 项目实战(三周以上):必须做一个能部署的项目,比如基于 Spring Boot 的博客系统。
这套路线走下去,需要大概三个月。注意,每个阶段都要配上手中代码,不能只看视频抄代码。我见过太多人看视频时一切都懂,一关视频就一脸懵,这是因为没有形成肌肉记忆。正确的方式是看完一个知识点,合上书,凭自己的理解写一遍,写不出来的地方再去查。
如果你是前端开发者想转后端,顺序上可以调整:先跳过 GUI 和网页技术,直接学 Java 基础 + 集合 + 数据库 + Spring Boot,因为你有编程思维,不需要像零基础那样从变量一点点啃。你会更关心 Controller 怎么暴露接口、MyBatis 怎么写 SQL,这些和前端调接口的思维是对应的。
6.2 常见环境与工具问题速查表
入门阶段,除了语法,最耗时间的就是环境问题。我把自己见过的高频问题整理成一张速查表,建议收藏:
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
java不是内部或外部命令 | PATH 环境变量没配置好 | 检查 PATH 中是否包含%JAVA_HOME%\bin,新开终端 |
javac可以但java不行 | JAVA_HOME 与 PATH 不一致 | 确认 JAVA_HOME 指向 JDK 而非 JRE 目录 |
| VSCode 运行乱码 | 控制台编码与文件编码不一致 | 设置 IDEA 的 file encoding 为 UTF-8,VSCode 里调整终端编码为 UTF-8 |
ClassNotFoundException | 类路径中没有找到对应类 | 检查依赖是否导入,Maven 项目需重新刷新 |
Main method not found in class | main 方法签名写错 | main 方法必须是public static void main(String[] args),不能写错参数名 |
| 端口被占用 | 一个进程占用了你要用的端口 | 用 `netstat -ano |
VSCode 运行 Java 乱码这个问题,说起原因其实有点曲折。VSCode 默认的终端在 Windows 上是 GBK 编码,而 Java 源码是 UTF-8,两边的字符集不一致就会输出乱码。最简单的解决方法是配置终端编码,在 settings.json 里加入:
"terminal.integrated.profiles.windows": { "Command Prompt": { "path": "C:\\Windows\\System32\\cmd.exe", "args": ["/K", "chcp 65001"] } }这样每次打开终端都会先切换到 UTF-8 代码页。如果你用的是 IDEA,在设置里搜索 file encoding,把所有项目编码选项都改成 UTF-8,测试代码时把 VM options 里加一行-Dfile.encoding=UTF-8也能根治。
6.3 关于“八股文”的一点实在建议
结尾想多说一句关于八股文的事。热词里频繁出现“java 面试八股文”和“java 面试题库大全”,我知道很多自学者特别焦虑,恨不得把所有题目背下来。我的观点是这样的:如果基础语法、面向对象、集合、JVM 这些内容你已经能独立讲清楚,并且能现场写代码表达出来,那么八股文对你来说只是锦上添花;如果连 Spring 的 IOC 和 AOP 都还没搞清楚就去背那些底层原理题,就算背下来了,面试官多追问一个为什么就会露馅。
最好的面试复习方式,是在项目里找到每个知识点的用武之地。比如你做了一个 Web 项目,你会理解为什么需要用到 Spring 的事务、为什么 Redis 要做缓存、为什么接口要设计成幂等。这些理解比死记硬背有价值得多。技术这个行业,基本功永远是第一位的,八股文能帮你过笔试,但真正帮你拿到 offer 的,是你写过的项目、踩过的坑、还有面对新问题时的解决思路。
最后分享一个小技巧:准备一个自己的问题清单,每学完一个知识点,就问自己三个问题——它解决什么问题?它怎么解决?不引入它会有什么坏处?能把这三个问题用普通话讲清楚,这个知识点就真正属于你了。按这个思路学 Java,三个月后你回头看第一周的代码,会发现自己已经走了很远。