先说个结论:Java 这门语言,放到 2025 年来看依然是国内后端开发、大数据生态、Android 底层方向绕不开的主干技术。你要是完全零基础,想靠自学入行编程,或者在学校里刚接触编程课,Java 是试错成本很低、学习资料极多、就业面也够宽的一个选择。我见过不少从 Python 转 Java、从前端转 Java 的朋友,最后都靠一套成体系的学习路径走通了。这篇内容不聊虚的,就按我自己带新人、带实习生总结出来的路子,从环境搭建、语法基础、面向对象、集合并发,一直写到实战项目和面试准备,尽量把该踩的坑提前告诉你。
这篇文章适合几类人:刚进大学想认真学一门编程语言的计算机专业学生、想从其他行业转行做开发的自学者、以及公司里被迫从零转 Java 的后端运维或测试同学。读完你至少能知道每天该学什么、学到什么程度算过关、遇到报错怎么排查、面试大概会被问哪些东西。
1. 学习路线总览:先知道要去哪,再迈出第一步
很多人学 Java 失败,不是因为笨,是因为路线太乱。今天看两眼菜鸟教程,明天刷两道 LeetCode,后天又去折腾 Spring Boot,结果三个月下来连 HashMap 和 Hashtable 的区别都说不清楚。学习编程最忌讳的就是东一榔头西一棒槌,尤其 Java 这种生态庞大的语言,没有主线就是浪费时间。
1.1 Java 到底在解决什么问题
在开始动手写代码之前,你得先理解 Java 为什么能火二十多年。Java 的核心卖点其实就两个:跨平台和生态。
所谓跨平台,就是“一次编写,到处运行”。Java 源代码会被编译成字节码,也就是 .class 文件,然后在 Java 虚拟机(JVM)上解释执行。不同操作系统装好对应的 JVM 之后,跑的是同一份字节码,所以不需要像 C/C++ 那样针对 Windows、Linux、macOS 分别编译。你可以把 JVM 理解成一个翻译官,Java 程序说的是普通话,Windows 讲 Windows 方言,Linux 讲 Linux 方言,翻译官负责把普通话转成对方听得懂的话,两边都能沟通。
生态就更不用说了。后端有 Spring 全家桶,大数据有 Hadoop、Flink、Spark(它们底层和上层 API 大量使用 Java 或 JVM 系语言),中间件有 Kafka、Elasticsearch、ZooKeeper,Android 应用开发在 Kotlin 出现之前也是 Java 的天下。你学 Java 不只是学一门语言,而是在学一套可以延伸到无数技术栈的基础能力。
1.2 一条可以照着走的学习路径
以我带人的经验来看,零基础到能胜任初级开发岗,大概需要 4 到 6 个月每天投入 4 小时以上。这个周期听起来不短,但如果你把时间拉散成一年,每天只学一两小时,反而容易学完前面忘后面。学习路径大致分为五个阶段,每个阶段的终点都要有一个可检验的产出:
第一阶段是 Java SE 基础,也就是 Java 标准版。包含变量、数据类型、运算符、流程控制、数组、方法、面向对象、常用 API、集合框架、异常处理、IO 流、多线程、网络编程、反射、注解等。这个阶段的目标不是背语法,而是能独立写一个 500 行以上的小项目,比如图书管理系统、学生成绩管理系统。学完这个阶段你已经具备用 Java 解决基础编程问题的能力。
第二阶段是数据库和 SQL。Java 是后端语言,后端必定要跟数据打交道。把 MySQL 的增删改查、多表关联、索引、事务这些搞明白,再去学 JDBC 和数据库连接池。
第三阶段是 Java Web 基础。包括 HTTP 协议、Tomcat、Servlet、Filter、Listener、Cookie/Session、JSP 等。虽然现在企业里直接用 Servlet 的已经很少,但如果不理解请求和响应的底层流转,后面学 Spring 全家桶会很吃力。
第四阶段是主流框架。Spring、Spring MVC、MyBatis、Spring Boot,以及 Maven 和 Git。学会用这些工具把之前的 Java SE 和 Web 基础串起来,做一个前后端分离的实战项目,比如个人博客、商城后台。
第五阶段是针对性的就业或进阶方向。如果目标是大数据,就去学 HDFS、MapReduce、Hive、Flink;如果目标是后端业务开发,就去深入 JVM、并发编程、分布式、消息队列、Redis 等。
这条路线不是唯一的,但它是被无数人验证过最稳妥的一条。接下来我会把前两个阶段的细节展开讲,因为这是自学最容易放弃、也最容易埋雷的地方。
2. 环境搭建:一步一坑,一次配好
我见过太多人在环境搭建这一步被劝退了。JDK 装好了,环境变量配不对;环境变量配好了,IDE 又打不开;IDE 终于能跑了,第一个 Hello World 却中文乱码。这些东西单独看都不难,但堆在一起就让人崩溃。其实环境搭配合格的标准只有一个:在命令行里执行java -version和javac -version都能正确输出版本信息,IDE 能正常运行并输出一个简单程序。
2.1 JDK 版本怎么选
先说版本问题。目前企业里主流的生产环境还在用 Java 8 和 Java 11,但 Java 17 作为长期支持版本(LTS)已经全面普及,新项目用 Java 17 的很常见。Oracle JDK 和 OpenJDK 的区别不用纠结,功能上几乎一致,唯一需要注意的是商业授权问题。个人学习用 OpenJDK 或者 Eclipse Temurin 就好,完全免费,省心。
另外明确一个概念:JRE 已经被淘汰得差不多了,现在都是直接装 JDK,因为 JDK 里自带 JRE 功能。你从 Oracle 官网下载的安装包也是 JDK。注意别下载成 JRE 版本,不然后面编译 Java 文件会提示javac 不是内部或外部命令。
2.2 环境变量配置详解
环境变量配置这个环节是新手最容易出错的地方。以 Windows 11 为例,正确步骤是这样的:
首先安装 JDK 并记住安装路径,比如我装在D:\Java\jdk-17。然后打开系统属性,进入环境变量设置。在系统变量里新建一个JAVA_HOME,变量值填 JDK 的安装路径,比如D:\Java\jdk-17。然后在Path变量中新增两条:一条是%JAVA_HOME%\bin,另一条是%JAVA_HOME%\jre\bin。实际操作中,jre目录在 JDK 9 之后已经不再单独生成,所以第二条路径在新版本上是不存在的,加了也不会报错但没必要。真正需要加的只有%JAVA_HOME%\bin。
配好之后,关键是验证。一定不要打开旧的命令行窗口去测试,因为旧窗口不会重新读取最新的环境变量。你需要新开一个 CMD 或 PowerShell,输入java -version,如果看到类似java version "17.0.10"的信息就说明成功了。很多新手在旧窗口里输入命令,发现还是报错,以为自己配错了,其实只是窗口没刷新。
这里说一个我踩过的坑:Classpath这个环境变量,现在的 JDK 版本已经完全不需要手动配置了。网上很多老教程让你新建一个CLASSPATH,填.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar,这其实是 JDK 5 时代的产物。在 JDK 9 模块化之后,dt.jar和tools.jar已经不存在了,配了反而可能引发类加载异常。看到让你配 Classpath 的教程,直接跳过。
2.3 开发工具怎么选:IDEA 还是 Eclipse
环境层面的最后一个选择是 IDE,也就是集成开发环境。我的建议很直接:用 IntelliJ IDEA 社区版,不要犹豫。
Eclipse 是老牌工具,但它的默认配置和操作逻辑对于新手来说太别扭了。IDEA 的智能提示、快捷键、代码模板做得更符合直觉,尤其对命名习惯的提示、自动导入、重构功能,能帮你少写很多无用代码。IDEA 的 Community 版完全免费,功能足够前三个阶段的学习。不要一上来就找破解版,没必要也不安全,社区版最多了不起是不能用 Spring Initializr 创建项目,这个可以在官网生成项目再导入,市面上教程也很多。
装好 IDEA 之后,记得先把编码改成 UTF-8,这个很重要。默认编码在 Windows 上经常是 GBK,以后写代码一旦用了中文注释,放到 Linux 服务器上就是一片乱码。设置位置在File -> Settings -> Editor -> File Encodings,把 Global Encoding、Project Encoding、Default encoding for properties files 全部设置为 UTF-8,顺手把右下角的文件编码也改掉。
环境准备到这个程度就算完成了。接下来的任务只有一个:跑通第一个 Java 程序,然后开始正式学语法。
3. 语法基础与面向对象:建立起真正的程序思维
环境搭好之后,很多人会陷入一个误区:每天疯狂看视频、抄代码,但抄完就忘,完全没有自己的思考。语法阶段千万不能只当打字员,要边敲边问自己三个问题:这段代码的作用是什么?去掉某一行程序会变成什么样?如果让你独立写一个相似功能,该怎么写?
3.1 变量、数据类型、运算符与流程控制
这部分东西抽象,但和生活联系很紧密。变量就是一个存储数据的容器。你要存一个人的年龄,就定义一个int age = 25;,要存一个人的姓名,就定义一个String name = "张三";。Java 是强类型语言,每个变量必须声明类型,编译器会严格检查类型,这在一开始觉得很麻烦,但写大型项目时会发现这种约束能避免大量低级错误。
Java 的基本数据类型有八种:背包类型byte、short、int、long,浮点类型float、double,字符类型char,布尔类型boolean。你不需要背每个类型的取值范围,但要知道默认值,这跟后面学类加载和线程安全有关。引用类型则包括类、接口、数组、枚举等,最典型的就是String。
运算符和流程控制相对直觉。if-else是“如果……就……否则……”,switch是对着一个变量的多种取值做匹配,for循环和while循环是反复做同一件事直到条件不满足。初学者最容易犯的错误是死循环和边界判断错误。比如写一个从 1 加到 100 的程序,循环条件写错就会多算一次或少算一次。我建议新手拿到任何循环题目时,先在纸上把循环变量从初值到终值的每一次变化写出来,写几遍之后就形成肌肉记忆了。
public class SumExample { public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } System.out.println("1到100的和是:" + sum); } }上面这段代码如果你能不看答案自己敲出来,并且能解释为什么i <= 100而不是i < 100,说明流程控制这关就过了。
3.2 面向对象:从"做事情"到"定义事物"
面向对象(OOP)是 Java 最核心的思想,也是新手从"会写代码"到"会设计代码"的分水岭。很多人觉得类、对象、封装、继承、多态这些概念抽象,其实用生活中的例子很好理解。
类就是模板,对象就是根据模板造出来的具体实物。比如“学生”是一个类,它定义了一类事物的共同特征:姓名、学号、成绩,以及行为:考试、选课。然后“张三”和“李四”都是学生类的对象。你写代码时先定义学生这个模板,再创建具体的张三李四去使用,这就是类和对象的关系。
封装的意思是,对象的内部细节不对外暴露,只通过公共方法对外提供服务。比如一个银行账户类,余额这个字段应该设成private,外部不能直接修改,只能通过deposit()和withdraw()方法操作。这样就保证了余额不会被随意改成负数。对应到代码上就是用private修饰字段,用public修饰方法,再配合Getter/Setter。
继承和抽象是为了复用。把多个类共有的属性和方法提取到一个父类里,子类通过extends继承。多态则是指同一个方法在不同对象上有不同的行为。比如Animal类有一个sound()方法,狗叫是“汪汪”,猫叫是“喵喵”,调用animal.sound()时实际执行哪个版本,取决于这个引用到底指向哪个子类对象。
这一阶段学完,你需要能完成一个经典的练习:写一个宠物管理系统,有宠物基类和猫、狗子类,能添加、删除、按名字查找宠物,数据先存在数组或 ArrayList 里。能独立完成这个项目,面向对象的基础就算打牢了。
3.3 异常处理与常用 API
异常处理是写健壮程序的必要条件。新手写代码时最烦看到红字报错,其实异常信息就是程序在告诉你哪里出了问题,读完前两行基本就能定位。Java 的异常体系以Throwable为根,下面分Error和Exception。Error一般是 JVM 层面的严重问题,比如内存溢出,程序本身处理不了;Exception是代码可以捕获和处理的,比如除数为零、数组越界、文件找不到。
处理异常的关键是合理使用try-catch-finally,以及 Java 7 开始支持的try-with-resources。对于文件流、数据库连接这种必须手动关闭的资源,用try-with-resources可以自动关闭,省去finally里的样板代码。
常用 API 这块,重点掌握String、StringBuilder、ArrayList、HashMap、HashSet这些工具类。不需要背源码,但要知道它们的区别和适用场景。比如频繁拼接字符串要用StringBuilder而不是直接用+,因为String是不可变对象,每次拼接都会产生新对象,循环拼接成千上万次会产生大量垃圾对象。
4. 集合、IO 与多线程:从写功能到写性能
学完基础语法,很多程序已经能跑起来了,但一旦数据量变大、请求变多,程序就卡死、内存暴涨。这时候你开始接触真正的工程问题,也就是集合选型、资源管理和并发控制。这几个话题,也是 Java 面试里被问得最多的高频考点,对应热搜词里的"java集合"“java线程”“java动态代理”这些。
4.1 集合框架:不只是一个容器,而是用错就崩的容器
Java 集合框架的核心接口是Collection和Map。Collection下面主要分List、Set、Queue;Map是键值对结构。面试和工作中最高频的三个问题是:ArrayList和LinkedList怎么选,HashMap的底层实现和扩容机制,HashSet如何去重。
ArrayList底层是动态数组,查找快、增删慢;LinkedList底层是双向链表,增删快、查找慢。但实际情况里,业务代码写LinkedList的增删优势往往发挥不出来,因为随机访问的需求更常见,所以默认用ArrayList就行。
HashMap是集合框架里的重点和难点。JDK 8 之后底层是数组加链表加红黑树的结构。插入键值对时,先计算 key 的hashCode(),再通过扰动函数降低碰撞概率,定位到数组下标,如果该位置没有元素就直接放进数组,如果有元素就形成链表。链表长度超过 8 且数组容量超过 64 时,链表会转成红黑树,把查询时间从 O(n) 降到 O(log n)。当元素数量超过数组长度的 0.75 倍(负载因子)时,数组扩容为原来的两倍,所有元素要重新计算位置,这是一个相对耗时的操作。
学HashMap的时候一定要亲自去读一遍源码,不用全懂,重点看put()方法的前 50 行就够,你会看到从计算 hash 到判断 tab 为 null 再到 new 节点的完整流程。读源码是新手进阶的必经之路。
4.2 IO 流:读文件写文件的一整套学问
IO 指输入输出,主要分为字节流和字符流。字节流处理所有类型的文件,比如图片、视频;字符流专门处理文本文件,避免乱码。字节流的基类是InputStream和OutputStream,字符流的基类是Reader和Writer。日常开发中用得最多的是BufferedReader、BufferedWriter、FileInputStream、FileOutputStream。
这里必须提醒一个高频坑:用字符流读写文件时,读取一整行文本,readLine()返回的字符串是不包含换行符的。你要写入的时候自己补\n或者用newLine(),否则合并文件时内容会全部粘成一行。
另一个高频坑是资源泄漏。文件流打开之后不关闭,文件会一直被程序占用,Windows 上会出现文件删除失败。所以一定要用try-with-resources语法,让流自动关闭。
4.3 多线程:从"同时做两件事"到"等待所有任务完成"
线程是程序执行的最小调度单位。多线程编程的核心诉求就是让 CPU 在等待 IO 的时候不要闲着,同时处理多个任务。但多线程不是银弹,反而会引入竞态条件、死锁、线程安全问题。
初学者最容易上手的是实现Runnable接口或继承Thread类创建线程,以及使用ExecutorService线程池来管理线程。我强烈建议直接学线程池,因为手动 new 线程容易造成系统资源耗尽,线程池可以复用线程、控制最大并发数。
"java 线程等待都完成"这个需求在实际开发中非常常见。比如一个统计任务需要同时查三个数据源,三个查询可以并行,但最终要把三个结果合并。这时候可以用CountDownLatch,初始化时计数为 3,每个子线程完成后调用countDown(),主线程调用await()等待计数归零。
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class WaitAllTaskDone { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); ExecutorService pool = Executors.newFixedThreadPool(3); for (int i = 1; i <= 3; i++) { int taskId = i; pool.submit(() -> { try { Thread.sleep(1000); System.out.println("任务" + taskId + "完成"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } }); } latch.await(); System.out.println("所有任务都完成了,开始合并结果"); pool.shutdown(); } }这个例子里最关键的是finally里的countDown()一定不能漏。万一子线程抛出异常,计数不归零,主线程永远等下去,程序就卡死了。用finally保证无论如何都把计数减掉,是一种防御性编程思路。
4.4 动态代理:AOP 和框架的基石
很多新手学到 Spring 的时候看到 AOP(面向切面编程)一头雾水,百思不得其解为什么调用一个普通方法会自动执行日志、权限检查这些额外逻辑。这里面最核心的底层技术就是动态代理。
动态代理说白了就是,你调用的对象并不是你自己写的那个类,而是一个在运行期动态生成的代理对象。代理对象在调用目标方法之前和之后可以插入额外逻辑。JDK 自带动态代理基于接口实现,用Proxy.newProxyInstance()生成代理对象,传入的InvocationHandler决定方法的调用逻辑。CGLIB 动态代理基于继承实现,可以代理没有接口的类。Spring AOP 默认会优先用 JDK 动态代理,不行再用 CGLIB。
学习动态代理时,不用一开始就啃源码,先会照着写一个简单的日志代理就行。但一定要动手在调试模式下看一下代理对象的字节码,观察它长什么样,这样后面理解 Spring 的底层原理会贯通很多。
5. 实战项目:把零散知识串成一个体系
很多自学的人学到最后发现自己什么都会一点,但都串不起来。解决这个问题唯一的办法就是做项目。项目是知识的黏合剂,也是简历上最能证明能力的东西。这里我按难度给三个层次的练手方案。
5.1 第一个完整的练手项目:图书管理系统
功能要求:控制台程序,可以添加图书、删除图书、按书名查找、按价格区间筛选、显示所有图书。数据存储先用 ArrayList,然后升级到文件存储,再升级到 JDBC + MySQL。
这个项目不需要任何框架,纯 Java SE 就能写完。你要涉及的知识点包括:类设计(Book 类、Library 类、Main 类)、集合操作、IO 读写、异常处理。写完之后试着给自己提需求:图书库存不够了怎么办、同名的书怎么区分、查询结果为空时给了什么提示。需求越细,代码就越接近真实的业务逻辑。
核心代码大致就是下面这样,你可以体会一下类的职责划分:
public class Book { private String id; private String title; private double price; private int stock; public Book(String id, String title, double price, int stock) { this.id = id; this.title = title; this.price = price; this.stock = stock; } public String getTitle() { return title; } public double getPrice() { return price; } public int getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } @Override public String toString() { return "图书编号: " + id + ", 书名: " + title + ", 价格: " + price + ", 库存: " + stock; } }5.2 大数据方向怎么扩展
热搜词里出现"HDFS 编程实践"“MapReduce 编程实例”,这说明有不少人学 Java 的初衷是奔着大数据去的。Java 和大数据的关系非常紧密,Hadoop 生态的核心代码就是 Java 写的。如果你确定走大数据方向,Java SE 学扎实之后不需要死磕 Spring 全家桶,可以直接去学 HDFS、MapReduce、Hive、Flink、Spark。
这里有个很重要的认知:大数据框架并不要求你从零实现底层原理,而是要求你有 Java 功底能写数和处理逻辑。比如 MapReduce 里的 Mapper 和 Reducer 类,本质就是继承父类、重写方法,这正好用上前面学的面向对象基础。
import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; public class WordCountMapper extends Mapper<Object, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); @Override protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { String[] words = value.toString().split("\\s+"); for (String w : words) { word.set(w); context.write(word, one); } } }这是我当年写第一个词频统计程序时的代码,虽然现在用 Flink SQL 的人更多,但理解一个 Mapper 如何继承、重写、输出键值对,对你理解整个分布式计算框架的设计思路非常有帮助。
5.3 项目复盘:把"做完"变成"做懂"
做完项目不是终点,复盘才是。我的习惯是每完成一个项目,就问自己五个问题:这个项目用了哪些之前学过的知识点?哪些代码写得很烂,重构的话怎么改?如果用户量变成一百倍,系统哪里会先崩溃?如果要加一个新功能,动哪些类就能实现?别人会怎么实现这个功能,比我的好在哪?
项目只要带着这些问题做一遍,效果远超闷头刷一百道题。
6. 常见问题与面试准备:别再死背八股文了
最后这一章,既是对学习过程的总结,也是给准备找工作的读者的一份清单。网上"Java 面试题"“Java 面试八股文”这些热搜词热度很高,但我一直觉得,面试题不该靠背,应该靠理解。你真正把原理弄懂,题目再怎么变都能接得住。
6.1 新手高频报错速查表
我整理了一份自学阶段最常遇到的报错表格,按出现频率排序,你只要照着排查,能省下大量百度时间。
| 报错信息 | 大概率原因 | 解决办法 |
|---|---|---|
javac 不是内部或外部命令 | 环境变量配错或没新开命令行 | 检查JAVA_HOME和Path,重开窗口 |
ClassNotFoundException | 类路径里没有对应的类 | 检查 jar 包依赖和-classpath参数 |
Exception in thread "main" java.lang.NullPointerException | 调用了 null 对象的方法 | 在报错行前面打印该对象,确认是否为空 |
ArrayIndexOutOfBoundsException | 数组访问越界 | 检查循环边界,下标从 0 开始 |
UnsupportedEncodingException | 编码设置不对 | 代码和文件统一使用 UTF-8 |
OutOfMemoryError: Java heap space | 堆内存不够或代码产生大量对象 | 加大-Xmx参数,排查死循环和对象持有 |
ConnectException: Connection refused | 服务没启动或连接的端口不对 | 检查服务器进程和防火墙端口 |
遇到任何报错,第一步永远是看第一行异常信息的最后一个冒号后面的描述,第二步看堆栈里at com.example.xxx.Main.main(Main.java:20)这种行号,直接跳到对应代码行去检查。不要一上来就截图问别人,学会读异常堆栈是你独立开发的第一步。
6.2 面试高频考点:这样准备最有效
面试题不用背全部,但你至少要能回答这几个核心问题的"为什么":
HashMap底层为什么要用红黑树?因为链表在哈希碰撞严重时查询效率退化到 O(n),红黑树能维持在 O(log n)。那为什么不一开始就用红黑树?因为红黑树节点占空间比链表节点大,绝大多数场景链表长度根本到不了 8,没必要为极端情况浪费内存。
String为什么是 final 的?因为不可变对象天然线程安全,而且可以被缓存复用(字符串常量池),还能安全地用作 HashMap 的 key。如果一个 String 能被继承重写方法,哈希值就可能发生变化,HashMap 里的数据就再也找不到了。
线程池的核心参数有哪些?核心线程数、最大线程数、空闲存活时间、工作队列、拒绝策略。核心线程数满了任务进队列,队列满了开新线程直到最大线程数,再满了就触发拒绝策略。这些理解了,就不怕面试官深挖。
动态代理和静态代理的区别?静态代理是在编译期就写死代理类,一个接口对应一个代理类;动态代理在运行期根据需要动态生成代理类,可以统一处理一类逻辑。Spring AOP 之所以强大,就是因为它用动态代理把横切逻辑从业务代码中抽离出来。
这几个问题虽然看起来是"八股文",但背后全是真实的工程权衡。你理解了权衡,面试官问任何变体都难不倒你。
6.3 学习节奏与心态建议
最后说点实在话。学 Java 是一场马拉松,不是冲刺跑。我见过最快三个月从零基础到拿到 offer 的人,也见过学了一年半还没走出语法阶段的。差别不在智商,在三点:是否每天固定投入时间、是否动手敲代码而不是只看视频、是否敢于自己解决问题。
千万别做"教程收藏家"。今天收藏十个视频教程,明天下载五个 PDF 电子书,后端一个都没看完。我的建议是认定一个系列课程或一本书,从头到尾学完,中途不要频繁换教材。遇到不懂的概念换个角度查一下没问题,但主线教材不要变来变去。
另外,一定要加入一个技术社区或者找一个学习搭子。编程学习很孤独,卡在一个 bug 上半天出不来的时候,有个人在旁边帮你瞄一眼,可能一句话就点拨通了。这也是为什么现在很多学习社群这么火,本质是人类需要反馈和陪伴。
我的个人经验是,把大目标拆成周目标。第一周搞定环境搭建和 Hello World,第二周学完流程控制和数组,第三周搞定类和对象。每周末做一个十几行的小练习检验这周成果,比如用循环打印九九乘法表、写一个简易计算器、用 ArrayList 做通讯录。小成功能持续给你提供正反馈,让你撑过最难的前面两个月。
等你能独立写完一个带界面的图书管理系统,并且能把集合、IO、多线程这些知识点讲清楚的时候,基本上就可以投简历去市场上试试水了。后边的 Spring 全家桶、微服务、分布式,都是在这个地基上搭起来的。地基打稳了,上面盖多高都不慌。