一套给零基础同学的「能翻、能练、能记住」的手册。 配套视频逐字稿与可运行源码见各节「配套」;完整版与答疑领取方式在文末。
写在前面:这本讲义怎么用
你可能听过一句话:人人都能学编程。但真去试的时候,常常卡在第一步——不是因为你笨,是因为传统学法把"地基、钢筋、水泥"先讲半年,却始终不让你看到楼长什么样。
我们这门课换一种学法,叫 案例贯穿 · 逆向拆解 · 螺旋递进:
- 先看成品:每一阶段先让你看到一个能跑的东西,建立"这玩意儿我也能做"的信心;
- 再倒着学:不从地基讲起,而是从成品里缺什么、补什么,学的每个知识点当场就知道用在哪儿;
- 系统长大:同一个记账本,从控制台一点点长成完整全栈(后面 P2 加数据库、P3 加后端、P4 加网页)。
这本讲义的用法:
- 先粗读一节的目标,知道"学完能干什么";
- 跟着知识点和代码片段在电脑上敲一遍;
- 做"动手练习",做不出来再去翻"常见坑";
- 用"一句话记住"自检。
全套知识点地图
| 节 | 主题 | 你学完能做什么 | 对应 demo |
|---|---|---|---|
| L0 | 开胃:看成品 | 看懂一个完整记账本长啥样 | AccountBook.java(终态) |
| L1 | 变量与数据类型 | 用变量存下一笔账并打印 | L1_Variables.java |
| L2 | 类与对象 | 用"类"把一笔账打包成模板,new 出对象 | L2_AccountClass.java |
| L3 | 集合 | 用 ArrayList 装很多笔账并遍历 | L3_Collection.java |
| L4 | 流程控制+输入 | 做出能反复操作的菜单(while/if/switch/Scanner) | L4_Menu.java |
| L5 | 方法拆分 | 把功能抽成方法,main 只调度 | L5_Methods.java |
| L6 | 字符串与格式化 | 用 printf 把账目打印成对齐表格 | L6_Format.java |
| L7 | 异常处理 | 输错了也不崩(try-catch) | L7_Exception.java |
| L8 | 文件持久化 | 关掉程序账还在(读写文件) | L8_Persistence.java(终态见 AccountBook.java) |
| L9 | 复盘+AI 协作 | 用 AI 扩展功能,并读懂它生成的代码 | L9_AI_Extension.java |
第 0 步:环境准备(只做一次)
-
安装 JDK 11 或更高版本(推荐 17)。装完在命令行输入
java -version,能看到版本号就成功了。 -
把讲义配套的
demo/文件夹下载到本地。 -
运行单文件 demo(JDK 11+ 支持直接跑单个
.java):cd demo/steps java L1_Variables.java如果 JDK 版本较旧,先编译再运行:
javac L1_Variables.java && java L1_Variables。
配套:环境装不上、命令报错?这些是公开版之外的高频问题,完整版里有"从零装环境"录屏 + 一对一报错的入口,见文末。
L0 · 开胃:先看到你能做出的东西
本节能做什么:不写代码,先建立动机——你已经知道我们要做出一个能录入、查看、统计、存盘的记账本。
成品长什么样(运行 AccountBook.java):
- 菜单:
1.录入 2.查看 3.统计 4.保存 5.退出 - 一笔账的字段:
id / 类型(收入·支出) / 金额 / 分类 / 备注 / 日期 - 统计:总收入、总支出、结余,以及每个分类花了多少
- 保存:账本写入
accountbook.txt,关掉再开数据还在
为什么这样学:传统路线先讲语法半年,你不知道学来干嘛;我们反过来,先让你看到"楼",再一层层拆"砖"。后面每一节,都是往这个记账本上添一块砖。
一句话记住:先见楼,再搬砖;跟着做,你也能写出来。
L1 · 变量与数据类型 —— 存下第一笔钱
本节能做什么:用变量把"一笔账"的字段分别存起来,并把它打印出来。
知识点
- 变量:给数据起个名字、存到内存里,以后用名字就能找到它。类比"带名字的盒子"。
- 基本类型:
double(小数,钱必须用这个)、int(整数)、boolean(真假)。 - 引用类型:
String(文字/字符串)。 - 字面量:直接写出来的数据,如
"支出"、35.5、2026。 - 打印:
System.out.println("内容" + 变量),用+把文字和变量拼起来。
代码片段(L1_Variables.java 核心)
String type = "支出"; // 文字用 String
double amount = 35.5; // 钱用 double(小数)
String category = "餐饮";
String note = "午饭";
int year = 2026; // 整数用 int
System.out.println("金额:" + amount + " 元");
double budget = 100.0;
System.out.println("午饭占日预算:" + (amount / budget * 100) + "%"); // 变量能参与运算
常见坑
- ❌ 把钱存成
int:35.5会被丢掉小数变成35,账就记错了。钱一律用double。 - ❌ 把
String当数字运算:"35" + 1得到"351"(拼接),不是36。需要数字就用double/int。
动手练习:定义几个变量,记录一笔你今天的账(比如奶茶 18 元),把它打印出来。
一句话记住:变量是带名字的盒子;钱用 double,文字用 String,整数用 int。
L2 · 类与对象 —— 把一笔账变成模板
本节能做什么:用"类"把一笔账的字段打包成一个模板,每记一笔就 new 一个对象,不再用五六个零散变量。
知识点
- 类(class):一张"记账模板",上面画好格子(字段)。代码里就是
Account。 - 对象(object):按模板填出来的一张单子,
new Account(...)就是填一张。 - 字段 + 构造器:字段是模板上的格子;构造器是"创建时一次性填好"的方法。
- 封装(private + getter):字段写成
private,外面不能直接改,要看通过getType()这类 getter 方法。刚开始觉得麻烦,程序变复杂后你会感谢它。 - this:区分"模板里的字段"和"传进来的参数",同名时用
this.xxx指字段。
代码片段(L2_AccountClass.java 核心)
static class Account {
private String type;
private double amount;
private String note;
// 构造器:创建对象时一次性赋值
public Account(String type, double amount, String note) {
this.type = type; this.amount = amount; this.note = note;
}
// getter:封装后通过方法读字段
public String getType() { return type; }
public double getAmount() { return amount; }
}
// new 出两笔账(对象)
Account a1 = new Account("支出", 35.5, "午饭");
Account a2 = new Account("收入", 8000, "工资");
System.out.println(a1.getType() + " " + a1.getAmount());
常见坑
- ❌ 直接
a1.type访问私有字段会报错——记住用 getter(a1.getType())。 - ❌ 构造器参数顺序和字段对不上,导致数据填错格子。
动手练习:给 Account 再加一个 category(分类)字段,写对应的构造器和 getter,再创建两笔不同的账打印出来。
一句话记住:类是模板,对象是按模板填的单子;private 字段用 getter 看。
L3 · 集合 —— 装下很多笔账
本节能做什么:用 ArrayList 把很多笔账攒到一起,并用循环把它们一条条列出来。
知识点
- 为什么不用数组:数组长度固定,定好装 10 笔,第 11 笔就放不进,不灵活。
- ArrayList(集合):会自动变长的"袋子",
add(对象)往里放,size()看数量。 - 泛型
<Account>:告诉集合"只装 Account 对象",避免混装、取出来还不用强转。 - for-each 遍历:
for (Account a : accounts)把袋子里每笔账依次取出来。
代码片段(L3_Collection.java 核心)
List<Account> accounts = new ArrayList<>();
accounts.add(new Account("支出", 35.5, "午饭"));
accounts.add(new Account("支出", 12.0, "地铁"));
accounts.add(new Account("收入", 8000, "工资"));
for (Account a : accounts) { // 遍历
System.out.println(a.getType() + " " + a.getAmount() + " " + a.getNote());
}
System.out.println("共 " + accounts.size() + " 笔账");
常见坑
- ❌ 用数组硬存导致"放不下"。记账笔数不确定,一律用 ArrayList。
- ❌ 遍历时空指针:集合里混入了
null。本阶段保证只add真实对象即可。
动手练习:用集合存 5 笔账,遍历打印,并输出总共有几笔。
一句话记住:数组长度固定不灵活,装很多对象用 ArrayList;for-each 一条条取。
L4 · 流程控制 + 输入 —— 做成交互菜单
本节能做什么:让程序"能用起来"——你输入什么它就记什么,而不是把数据写死在代码里。
知识点
- while 循环:
while (true)让菜单一直转,等你操作,直到你选退出。 - if / else if / else:按条件走不同分支(也可用
switch,见 L5)。 - switch:根据"选择"走对应分支,比一堆 if 更清晰。
- Scanner:程序和键盘的桥梁,
sc.nextLine()读一行文字,Double.parseDouble(...)把文字转成数字。 - break:跳出循环(退出菜单)。
代码片段(L4_Menu.java 核心)
Scanner sc = new Scanner(System.in);
while (true) { // 一直转,直到退出
System.out.println("\n1.录入 2.查看 0.退出");
System.out.print("请选择:");
String choice = sc.nextLine();
if (choice.equals("1")) {
System.out.print("金额:");
double amount = Double.parseDouble(sc.nextLine()); // 文字→数字
accounts.add(new Account("支出", amount, "午饭"));
} else if (choice.equals("0")) {
break; // 跳出循环
}
}
常见坑
- ❌ 忘了
break,选了退出还继续跑(if 写法)或贯穿到下一个 case(switch 写法)。 - ❌
Double.parseDouble收到非数字会崩——这个坑 L7 用异常解决。 - ❌ 用
==比较字符串(如choice == "1"),字符串要用.equals()。
动手练习:写出菜单骨架——显示 1 录入 / 2 查看 / 0 退出,用 while 包住、用 if 或 switch 分流,分支里先打印一句话占位。
一句话记住:while 让程序反复等指令;Scanner 读输入;比较字符串用 equals。
L5 · 方法拆分 —— 让 main 变干净
本节能做什么:把"录入""查看"各自抽成独立方法,main 只负责调度,代码不再一大坨。
知识点
- 方法(method):一个个独立的小工具。录入是一把螺丝刀,查看是一把扳手。
- 定义:
static void add()中void表示"不返回值";要返回结果就用具体类型(如static int sum())。 - 参数:传给方法的数据(如要录入的这笔账);返回值:方法跑完交给你的结果。
- 调用:在
main里写add();、list();即可使用。 - 代码组织:功能拆开,改"查看"直接找查看方法,不用全篇翻。
代码片段(L5_Methods.java 核心)
static void add() { // 录入方法(无返回值用 void)
System.out.print("金额:");
double amount = Double.parseDouble(sc.nextLine());
accounts.add(new Account("支出", amount, "午饭"));
System.out.println("录入成功!");
}
static void list() { // 查看方法
for (Account a : accounts) System.out.println(a.getType()+" "+a.getAmount());
}
public static void main(String[] args) {
while (true) {
String choice = sc.nextLine();
switch (choice) {
case "1": add(); break; // 调用方法
case "2": list(); break;
case "0": return;
}
}
}
常见坑
- ❌ 把全部逻辑堆在
main,导致几百行挤一起、难读难改。 - ❌ 方法需要返回结果却写成
void,拿不到值。
动手练习:把上节课的菜单骨架里三个占位分支,各补成一个真正的方法(录入/查看/退出),main 只做调用。
一句话记住:方法是独立小工具;void 不返回,要返回就写类型;main 只调度。
L6 · 字符串与格式化 —— 账目更好看
本节能做什么:把杂乱的输出排成对齐的表格,金额统一两位小数、日期规整。
知识点
- String 常用:拼接(
+)、isEmpty()判断空、equals()比较。 - printf 格式化输出:规定宽度与小数位,让数字像列队一样对齐。
%-6s:左对齐、占 6 格的字符串%-10.2f:左对齐、占 10 格、保留 2 位小数的小数%n:换行(跨平台)
- LocalDate.now():取当前日期,配合
DateTimeFormatter格式化成yyyy-MM-dd。
代码片段(L6_Format.java 核心)
System.out.printf("%-6s %-10s %-12s %s%n", "类型", "金额", "日期", "备注");
for (Account a : accounts) {
System.out.printf("%-6s %-10.2f %-12s %s%n",
a.getType(), a.getAmount(), a.getDate(), a.getNote());
}
常见坑
- ❌ 用
println拼、不对齐,数字长短不一很难看。 - ❌ 忘记
.2f导致金额出现一长串小数(如35.500000)。
动手练习:把查看方法改成用 printf 输出对齐表格,金额统一两位小数、右对齐。
一句话记住:printf 管对齐;%-宽s 字符串、%-宽.2f 金额;%n 换行。
L7 · 异常处理 —— 输错了也不崩
本节能做什么:用户输入非数字或负金额时,程序温和提示重输,而不是直接甩红字崩溃。
知识点
- 异常(exception):程序运行时的"意外状况",如把
"abc"当数字读。 - try-catch:把"可能出错的代码"放
try,出错后怎么处理写catch。 - NumberFormatException:数字格式不对(文字转数字失败)时抛出。
- 防御式编程:假设用户会犯错,提前堵好路(校验负值、捕获异常)。
代码片段(L7_Exception.java 核心)
System.out.print("金额:");
try {
double amount = Double.parseDouble(sc.nextLine());
if (amount <= 0) { System.out.println("金额必须大于 0"); return; }
accounts.add(new Account("支出", amount, "午饭"));
} catch (NumberFormatException e) { // 输入的不是数字
System.out.println("金额必须是数字,录入失败。");
}
常见坑
- ❌ 不处理异常,用户手滑输字母,整个程序崩掉。
- ❌
catch里空着什么都不做,出错后无声无息,更难排查。
动手练习:给金额录入加 try-catch:输非数字或负数时提示重输,而不是直接崩溃。
一句话记住:try 包可能出错的代码,catch 接住异常;把用户输入当"会犯错"来处理。
L8 · 文件持久化 —— 关掉再开还在
本节能做什么:退出前把账目写入文件,启动时读回,数据"活过重启"。
知识点
- File:代表磁盘上的文件本身。
- 写:
BufferedWriter/FileWriter,逐行把一笔账写成逗号分隔的文本。 - 读:
BufferedReader/FileReader,逐行读回、用split(",")拆开还原成对象。 - try-with-resources:
try (...) { }写法,用完后自动关闭流,避免数据没真正写进去或资源被占。 - 持久化:把内存里的数据落到磁盘,重启不丢。
代码片段(L8_Persistence.java 核心:保存与读取;完整终态见 AccountBook.java)
// 保存:try-with-resources 自动关流
private void save() {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("accountbook.txt"))) {
for (Account a : accounts) {
bw.write(String.join(",", a.getType(), String.valueOf(a.getAmount()), a.getNote()));
bw.newLine();
}
} catch (IOException e) { System.out.println("保存失败:" + e.getMessage()); }
}
// 读取
private void load() {
File f = new File("accountbook.txt");
if (!f.exists()) return;
try (BufferedReader br = new BufferedReader(new FileReader("accountbook.txt"))) {
String line;
while ((line = br.readLine()) != null) {
String[] p = line.split(",", -1);
accounts.add(new Account(p[0], Double.parseDouble(p[1]), p[2]));
}
} catch (IOException e) { System.out.println("读取失败:" + e.getMessage()); }
}
常见坑
- ❌ 用完流不关闭,可能数据没写入或占着系统资源——用 try-with-resources 最省心。
- ❌ 读取时
split长度不够导致数组越界——先判断p.length。
动手练习:给之前的程序加存盘:退出时把集合按行写入文件,启动时读回。跑通后你就拥有一个真正能日常用的小工具。
一句话记住:数据落盘才不丢;写用 Writer、读用 Reader;try-with-resources 自动关。
参考:
L8_Persistence.java是只加了持久化的单步版本;AccountBook.java是完整终态(额外含 id、分类、3.统计)。
L9 · 综合复盘 + AI 协作 —— 你会用 AI 写代码了
本节能做什么:回看 L1→L8 这个记账本怎么一节节长大,并学会用 AI 给它加功能、还能读懂 AI 的代码。
回顾这一路(同一案例螺旋长大)
- L1 只有一个变量 → L2 有了类 → L3 装进集合 → L4 能交互 → L5 拆成方法 → L6 变整齐 → L7 不崩 → L8 能存盘。
核心认知:AI 是放大器,不是替代品
- 不懂基础时,你只能含糊地说"帮我加个统计",AI 给的代码你看不懂、改不动;
- 懂了集合、类、方法后,你能下精准指令:"用 ArrayList 里每笔账的日期,按月份分组算每月总支出"——AI 生成的代码你也能读、能改、能验证。
- 基础越牢,给 AI 的指令越准,放大效果越强。
参考实现:L9_AI_Extension.java 给出了「按月统计 / 导出 CSV」的参考代码——这就是你给 AI 下精准指令后,可能得到的产出的样子。
动手练习:给 AI 提一个精准扩展需求,比如"导出 CSV"或"按月统计",把它的代码跑通,并试着读懂每行在干什么。
一句话记住:AI 放大你的能力;基础越牢,指令越准,放大越强。
附录 A · 配套 demo 文件清单
| 文件 | 对应节 | 说明 |
|---|---|---|
demo/steps/L1_Variables.java | L1 | 只有变量,存一笔账并打印 |
demo/steps/L2_AccountClass.java | L2 | 加 Account 类,new 出对象 |
demo/steps/L3_Collection.java | L3 | 加 ArrayList,存多笔+遍历 |
demo/steps/L4_Menu.java | L4 | 加 while+if/Scanner 交互菜单 |
demo/steps/L5_Methods.java | L5 | 把录入/查看拆成方法 |
demo/steps/L6_Format.java | L6 | 加 printf 对齐表格 |
demo/steps/L7_Exception.java | L7 | 加 try-catch 防崩 |
demo/steps/L8_Persistence.java | L8 | 加文件持久化(单步增量,菜单 4 保存 / 5 退出) |
demo/steps/L9_AI_Extension.java | L9 | AI 扩展参考:按月统计 / 导出 CSV |
demo/AccountBook.java | 终态 | 完整成品:在 L8 之上加 id / 分类 / 3.统计(即 L0 成品) |
教学时按 L1→L8 顺序放代码,学生能亲眼看到"同一个记账本一点点长大"。
附录 B · 常见错误速查
| 现象 | 原因 | 解决 |
|---|---|---|
| 金额变整数(35.5→35) | 用 int 存钱 | 改用 double |
比较字符串用 == 永远不对 | 字符串要比内容 | 用 .equals() |
| 输字母程序崩 | 没捕获数字转换异常 | L7 的 try-catch |
| 菜单退出后还跑/串分支 | 漏写 break | 补 break / 用 return |
| 文件写了重启没数据 | 流没关闭 | 用 try-with-resources |
println 数字对不齐 | 没用格式化 | L6 的 printf |
附录 C · 下一步去哪(P2–P5 预告)
- P2 数据库:把 ArrayList 换成 MySQL,
Account变成数据库里的表,账不再只存在本地文件。 - P3 Spring Boot 后端:把菜单逻辑搬进 Controller/Service,提供 REST API。
- P4 前端 Vue:把控制台界面换成网页,AJAX 调后端。
- P5 安全与部署:登录鉴权(JWT)、事务、Docker 打包、Linux 上线。
- 同一记账本,会从"控制台小工具"一路长成"完整全栈应用"。
怎么领:wx:Trdfyl,qq:2356245930,备注「资料」,我把完整版和源码发你,并拉你进答疑群。
