如果你刚开始学Java,恰好卡在第二天,这篇文章就是给你准备的。第一天通常是把环境搭起来,写了第一个Hello World,认识了变量和基本数据类型。到了第二天,很多人的第一个问题是:接下来学什么?继续写几个小例子?还是直接去看面向对象?我的建议是,先别急,把程序的控制权拿回来。所谓控制权,就是让代码能够根据条件做选择、根据次数做循环、把重复逻辑收拢成方法、把一批数据装进数组。这才是从“会写几行代码”过渡到“能写出有条理的小程序”的关键一步。
这篇文章会按我平时带新手比较认可的节奏来:先明确第二天的学习边界,然后逐个搞定选择结构、循环结构、数组、方法,最后用一个小项目把当天知识点串起来。整个过程中我会把新手最容易踩的坑直接指出来,并解释背后的原理。适合所有正在学Java基础、恰好学到第二天的读者,也适合想回炉巩固基础的人。读完你不仅能照做,还能明白为什么要这么做。
1. 第二天的任务清单:从“会写几行代码”到“能控制程序走向”
先别急着看知识点,我们先把第二天的学习地图画出来。很多时候学不下去,不是不努力,而是不知道今天到底要走到哪里。
1.1 第一天结束时你应该已经掌握的东西
按正常节奏,第一天的成果是三件事:JDK装好了、第一个Java程序跑通了、变量和基本数据类型有了初步概念。具体来说,就是能用int、double、boolean、char声明变量,能做加减乘除,能知道System.out.println和System.out.print的区别。
如果你的第一天还没覆盖这些,我建议先花半小时补一下。别小看这些内容,第二天的判断条件、循环计数、数组下标,全都建立在变量和类型的基础上。比如说,后面你会经常用arr.length作为循环上限,这个length返回的是int类型,如果我把它当成其他类型来用,程序的运行结果就会非常奇怪。
有一个实操小技巧:当你怀疑环境没配好时,不要在IDE里纠结,直接在命令行敲java -version和javac -version。如果这两个命令都能输出版本号,说明环境基本没问题;如果提示找不到命令,多半是路径配置的问题,和代码本身无关。先把这个卡点排除,再谈学习进度。
1.2 为什么第二天先补控制流,而不是直接冲面向对象
我知道很多人的心态是:Java是面向对象的语言,那我应该赶紧学类、对象、继承。但以我带人的经验来看,过早学面向对象,往往最后一个简单的类都写不顺。原因很简单:类里面写的是什么?是方法和字段。方法怎么定义、参数怎么传、数据怎么用数组装,这些恰恰是第二天的内容。控制流、数组、方法是面向对象的“素材”,先把素材准备好,后面看class、public、private这些概念时才不会一头雾水。
你可以类比学开车。第一天你学会了点火、踩油门、看仪表盘,现在第二天应该练的是低速打方向盘、刹车减速这些基本功,而不是直接上高速。面向对象就好比高速驾驶规则,等你在普通路上能稳稳控制车辆了再去学,才真正有效果。
1.3 第二天结束时的验收标准
学习最怕“看完了”而不是“学会了”。我给第二天的验收标准设成三件事,全部能独立写出来才算过:
- 写一个程序,根据一个分数输出对应的等级(优秀、良好、及格、不及格)。
- 用至少一种循环,算出1到100的和。
- 定义一个有返回值的方法和一个没有返回值的方法,并在
main里调用它们。
如果这三件事做完了,时间还有富余,再挑战一个进阶目标:用数组存一批分数,遍历数组并输出每个分数是否及格。这个目标一完成,第二天就算超额达标。这几个验收标准有一个共同特点:它们是写代码,不是看代码。只有亲手敲出来,知识才是你的。
2. 选择结构:if/else 与 switch,三个新手最容易踩的坑
什么是选择结构?简单说就是让程序根据条件走不同的分支。Java里最常用的是if/else,其次是switch。这块概念本身不复杂,复杂的是在不同条件下,代码执行顺序怎么安排。
2.1 if/else 的执行顺序:分支的顺序决定了结果
看一段最常见的成绩等级判断:
int score = 85; if (score >= 90) { System.out.println("优秀"); } else if (score >= 80) { System.out.println("良好"); } else if (score >= 60) { System.out.println("及格"); } else { System.out.println("不及格"); }这段代码逻辑是:从上到下依次判断,只要某个条件为true,就执行对应的代码块,然后整个if/else链结束,后面的分支不再判断。所以85分走的是score >= 80这个分支,输出“良好”。
这里有一个新手特别容易犯的错:把分支顺序写反,比如先写score >= 60再写score >= 90。这样90分的人会先命中“及格”分支,永远走不到“优秀”。从语法上看代码没问题,编译也能过,但结果完全错了。这种错误最难排查,因为它不报错,只是输出不符合预期。我的建议是:涉及范围比较时,要么从高到低排,要么从低到高排,并且养成写代码时在注释里标清楚每个分支的区间。比如“>=90优秀,80~89良好,60~79及格,<60不及格”,写清楚后再动手,能省大量调试时间。
2.2 switch 的 case 穿透与 break 的必要性
switch适合做等值判断。比如根据月份判断季节:
int month = 3; switch (month) { case 12: case 1: case 2: System.out.println("冬季"); break; case 3: case 4: case 5: System.out.println("春季"); break; default: System.out.println("月份有误"); break; }很多新手第一次写switch都栽在break上。所谓“case穿透”,是指一旦某个case匹配成功,程序不会自动停下来,而是会继续往下执行后面的所有case,直到遇到break或者switch结束。上面这个例子就充分利用了穿透:12、1、2三个月都归到冬季,所以中间不用写break,统一在春季前收口。反过来,如果你在case 3后面忘了break,那么3月会输出“春季”之后继续判4月、5月,甚至跑进default,输出一堆不相干的内容。
我的习惯是:写switch时先把每个case的结尾break写好,再写中间的判断逻辑。哪怕暂时不需要break的合并写法,也宁可先多写,后面再优化。顺便提一句,新版本的Java里有switch表达式和箭头写法,代码更简洁,但基础阶段先用传统switch把“穿透”这两个字刻在脑子里,后面再看新语法会理解得更透彻。
2.3 三个易错点:赋值等号、浮点比较、悬垂 else
这几个坑每个都值得单独说。
第一个坑是if条件里误用赋值符号。if (score = 85)在Java里通常是编译错误,因为=是赋值,==才是比较。但有些场景下赋值表达式也有值,能编译通过,结果就变得极其隐蔽。我的建议是:条件判断里只写比较写法,看到单个=出现在if里,直接判死刑重新写。
第二个坑是浮点数比较。比如0.1 + 0.2 == 0.3在Java里大概率是false,因为浮点数在计算机里是用二进制近似表示的,不是精确小数。新手很容易在这里怀疑人生。正确的做法是判断两个浮点数之差的绝对值是否足够小,比如Math.abs(a - b) < 1e-9,或者等到学BigDecimal时再统一处理精确计算。
第三个坑叫“悬垂else”。看这段代码:
if (a > 0) if (b > 0) System.out.println("both positive"); else System.out.println("a <= 0");从缩进看,else好像是对应if (a > 0)的,但实际上Java语法规定else会匹配最近的、没有配对的if,所以它匹配的是if (b > 0)。这就导致你想表达“a不大于0”时,程序偏不这么走。解决办法也简单:永远为if和else加上花括号,哪怕只有一行代码也加。多写两个字符,省掉一晚上调试,划算得很。
2.4 三元运算符:适合简单二选一,别嵌套
选择结构还有一个简写叫三元运算符,格式是条件 ? 值1 : 值2。经典场景是求两个数里的最大值:
int max = a > b ? a : b;这行代码等价于:
int max; if (a > b) { max = a; } else { max = b; }三元运算符的优点是简短,缺点是一旦嵌套起来,可读性会急剧下降。我见过有人写出三层嵌套的三元表达式,看得人头疼。我的经验是:只有“二选一、表达式简短”时才用三元,但凡分支里有多行逻辑,老老实实用if/else。基础阶段甚至可以完全不用三元,先把if/else写熟,以后再看别人代码里的三元也不会懵。
3. 循环结构:for、while、do-while 怎么选,死循环怎么排查
循环是第二天最核心的内容之一。它解决的问题是:让同一段代码反复执行,直到某个条件不再满足。Java里有三种循环写法,各有各的适用场景。
3.1 for 循环三要素:初始化、条件、迭代的真实执行顺序
先看最标准的写法:
for (int i = 1; i <= 10; i++) { System.out.println(i); }for后面括号里三个部分,很多人背下来但不懂执行顺序。真实顺序是这样的:第一步执行初始化int i = 1,只执行一次;第二步判断条件i <= 10,为真就进入循环体;第三步执行完循环体后,执行迭代语句i++;第四步再次判断条件……如此反复,直到条件为假退出循环。
为什么要强调这个顺序?因为新手常犯的错是在循环体内部又写了一遍i++:
for (int i = 0; i < 5; i++) { System.out.println(i); i++; // 多余 }这会导致i每次循环被加两次,输出结果为0、2、4,而且循环次数比预期少。虽然程序能跑,但结果完全不对。我的建议是:用for循环时,循环变量的更新只交给括号里的第三部分,循环体内不要再改循环变量。如果你需要跳过某些值,用continue,后面会讲。
3.2 while 与 do-while:先判断还是先执行
while和do-while的区别一句话就能说清:while先判断条件,条件成立才进入循环体,如果条件一开始就不成立,循环体一次都不执行;do-while先执行一次循环体,再判断条件,所以至少执行一次。
// while版本 int i = 1; while (i <= 10) { System.out.println(i); i++; } // do-while版本 int j = 1; do { System.out.println(j); j++; } while (j <= 10);表面看两者输出一样,但换个场景就不同了。比如你写一个“猜数字”小游戏,玩家无论如何都要先猜一次,这时候do-while就比while合适,因为“至少执行一次”正好符合需求。反过来,如果程序要根据用户在文件里输入的配置决定是否执行某段逻辑,那通常用while更合理,因为可能配置文件为空,一次都不该执行。
三种循环的取舍我的经验是:
| 场景 | 推荐循环 |
|---|---|
| 知道确定循环次数,比如遍历数组 | for |
| 不知道次数,只知道结束条件,比如读取输入直到结束 | while |
| 至少需要执行一次,再判断是否继续 | do-while |
3.3 break、continue 和带标签的跳出
循环里有两个控制语句要掌握:break和continue。break会立即终止整个循环,continue会跳过本次循环剩余代码,直接进入下一次迭代。
for (int i = 1; i <= 10; i++) { if (i == 5) { break; // 到5就停 } System.out.println(i); }这段代码只会输出1到4,遇到5就跳出循环。
for (int i = 1; i <= 10; i++) { if (i == 5) { continue; // 跳过5 } System.out.println(i); }这段代码输出1、2、3、4、6、7、8、9、10,5被跳过。
新手容易忽略的是:在嵌套循环里,break只能跳出离它最近的那层循环。如果需要跳出外层循环,使用带标签的break:
outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { break outer; } System.out.println(i + "," + j); } }标签的写法就是在循环前加一个标识符:,break outer表示跳出外层循环。这个技巧不是每天都会用到,但遇到“从多层循环中提前结束”的场景时,它能让你少写大量标志变量。我建议先知道有这个东西,用的时候回来查一下语法即可。
3.4 死循环排查的两板斧:打印变量 + 检查迭代语句
死循环是第二天最容易遇到的现象,表现是程序运行后控制台刷个不停,或者直接卡死。最常见的原因就两个:循环条件永远为真,或者循环变量没有朝着退出条件方向更新。
最简单的死循环:
int i = 0; while (i < 10) { System.out.println(i); // 忘了 i++ }因为i始终是0,条件i < 10永远成立。解决办法是补上i++,或者直接改用for循环,让循环变量更新放在括号里,至少减少漏写的概率。
还有一种是更隐蔽的死循环:continue的位置不对。
int i = 0; while (i < 10) { if (i == 5) { continue; } System.out.println(i); i++; }当i等于5时,continue会跳过后面的i++,直接回到条件判断。结果i永远停在5,死循环出现。这种情况用for循环往往能规避,因为continue后for的迭代语句仍然会执行。我的排查方法很简单:先在循环体里加一行System.out.println("当前i=" + i),看变量到底有没有变化;再检查条件里涉及的每个变量,看它们的更新语句是否可能被continue跳过。大多数死循环,靠这两板斧五分钟内就能定位。
4. 数组入门:下标、遍历、内存模型与两个高频异常
数组解决的问题是:把一堆同类型的数据存到一起,用同一个变量名统一管理。比如说一个班50个学生的成绩,你不会想定义50个独立的变量,而是用一个长度为50的数组。
4.1 从变量到数组:内存里发生了什么
先看定义:
int[] arr = new int[5];这行代码在内存里做了两件事:第一,在堆内存中创建一块能存放5个int型数字的连续空间,每个元素默认值是0;第二,在栈内存中创建一个叫arr的变量,它保存的是那块空间的地址,也就是引用。
你可以把一个数组理解成一排连续的储物柜,柜子编号从0开始。arr变量相当于你手里拿着的钥匙串,上面记录了第一个柜子的位置。你要取第几个柜子,就用arr[下标]。所以长度为5的数组,可用的下标是0、1、2、3、4,没有arr[5]。
这也能帮你理解另一个新手常问的问题:为什么数组长度是固定的?因为JVM在创建数组时,已经根据长度分配了连续的内存块,后面想加长就得重新创建新数组,把旧数据拷贝过去。这不是Java设计偷懒,而是连续内存分配的特性决定的。
4.2 数组的声明、初始化和三种遍历
声明数组推荐写法是int[] arr,也就是类型加方括号。初始化有两种常见方式:
// 方式一:直接给出元素 int[] scores = {78, 85, 90, 66, 88}; // 方式二:先指定长度,再逐个赋值 int[] arr = new int[5]; arr[0] = 10; arr[1] = 20;遍历数组有三种方式。第一种是按下标遍历,适合需要读写下标或修改元素的情况:
for (int i = 0; i < scores.length; i++) { scores[i] = scores[i] + 1; // 给每个成绩加1分 }第二种是增强for,也叫foreach,适合只读取、不需要下标的场景:
for (int score : scores) { System.out.println(score); }第三种有点偷懒但很常用,就是用Arrays.toString直接打印数组内容:
import java.util.Arrays; System.out.println(Arrays.toString(scores));小提醒:如果想用foreach修改数组元素,写for (int score : scores)时改的是临时变量score,对原数组没有任何影响。要修改元素,必须用下标遍历。这个坑我在很多新手代码里见过不止一次。
4.3 两个高频异常:越界和空引用
第二天写数组,几乎每个人都会遇到这两个异常。别怕,遇到是好事,说明你对“内存边界”开始有感觉了。
第一个是ArrayIndexOutOfBoundsException,翻译过来就是数组下标越界。典型写法:
int[] arr = new int[3]; System.out.println(arr[3]); // 异常!数组长度是3,合法下标是0到2,访问3就超界了。排查方式很简单:检查循环条件是不是写成了i <= arr.length,正确写法是i < arr.length。很多越界都来自这个多出来的等号。
第二个是NullPointerException,空指针异常。典型写法:
int[] arr = null; System.out.println(arr.length);null表示引用没有指向任何对象,你拿着一个空的钥匙串去开储物柜,自然报错。排查思路是:检查数组到底有没有被初始化,或者初始化语句是否被某些条件分支跳过了。这两个异常在后面的编程生涯里会反复出现,建议现在就形成条件反射——看到ArrayIndexOutOfBoundsException先看下标范围,看到NullPointerException先查引用是否为空。
4.4 二维数组:数组的数组
第二天有余力的话,可以顺带看一眼二维数组。定义:
int[][] matrix = new int[3][4];理解它的关键是“数组的数组”:matrix是一个长度为3的一维数组,它的每个元素又是一个长度为4的一维数组。用双层循环可以遍历:
for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); }注意内层循环的长度用的是matrix[i].length,因为每一行作为一个独立的数组,长度都可能不同。这正好呼应了前面说的:数组长度固定,但二维数组不同行的长度可以不一样。这个概念再往后到集合框架时,还会遇到变体,所以先混个脸熟。
5. 方法:参数传递、重载、递归的第一轮接触
如果一个程序只有main方法,写什么都堆在一起,那代码很快就没法看。方法(也叫函数)就是把一段有独立功能的逻辑封装起来,给它起个名字,需要时反复调用。这是第二天最重要的编程思维训练。
5.1 方法的定义结构:修饰符、返回值、参数、方法体
先看一个完整的方法:
public static int add(int a, int b) { return a + b; }拆开来看有四个部分。public是访问修饰符,表示方法可以被外部访问;static表示静态方法,属于类本身,不需要创建对象就能调用;int是返回值类型,表示这个方法算完后返回一个整数;括号里的int a, int b是参数列表,调用者需要传入两个整数。方法体是大括号里的部分,return会把结果返回给调用方。
为什么main方法长这样?
public static void main(String[] args)关键在于JVM启动时,不需要创建对象就能直接调用main,所以它必须是static;void表示没有返回值;String[] args是命令行参数数组,可以留到以后需要传参时再用。基础阶段你只需要知道:在main里调用其他方法时,被调的方法为了简单起见也写成static,等学到面向对象时再改成实例方法也不迟。
调用方法时,实参传给形参的过程是值复制。看这个例子:
int result = add(3, 5); // 实参3和5分别赋值给形参a和b返回值可以直接赋值给变量,也可以直接打印。把逻辑拆进方法的最大好处是:你只需要关心“方法能完成什么”,不需要每次都重新写一遍内部实现。
5.2 值传递还是引用传递:Java里其实只有一种传递
这个知识点算是个经典争论了。先说结论:Java只有值传递。基本类型传的是值的副本,引用类型传的是引用地址的副本。
public static void main(String[] args) { int a = 10; changeInt(a); System.out.println(a); // 输出10,a没变 int[] arr = {1, 2, 3}; changeFirst(arr); System.out.println(arr[0]); // 输出100,arr[0]变了 } public static void changeInt(int x) { x = 100; } public static void changeFirst(int[] nums) { nums[0] = 100; }为什么一个变一个没变?因为changeInt修改的是形参x的副本,不影响外面a;而changeFirst虽然也是复制引用,但复制的引用和外面的引用指向同一个数组对象,所以通过这个引用修改数组内容,外面自然能看到变化。
理解这个的最好办法是画内存图:变量a和形参x各占一块独立空间;而arr和形参nums的内容都是堆中数组对象的地址,两个地址指向同一块区域。画几遍之后,后面学类的对象传参就不会被绕晕。
5.3 方法重载:同一个方法名,编译器替你选择
重载是指同一个类里,方法名相同,但参数列表不同。比如:
public static int max(int a, int b) { return a > b ? a : b; } public static double max(double a, double b) { return a > b ? a : b; }一个叫max的方法既能处理整数又能处理浮点数。调用时编译器会根据实参类型自动选择合适的方法:传两个int进去就走整数版本,传两个double进去就走浮点版本。
新手最容易困惑的是:重载能不能只改返回值类型?答案是不能。比如:
public static int max(int a, int b) public static double max(int a, int b) // 报错,不能只改返回类型因为调用max(1, 2)时,编译器无法区分你到底要哪个版本。重载的本质是“让编译器依据参数列表区分方法”,和返回值无关。基础阶段记住这个结论,踩一两次坑自然明白。
5.4 递归的一见钟情与栈溢出警告
递归就是方法调用自己。最经典的入门例子是阶乘:
public static long factorial(int n) { if (n == 1) { return 1; } return n * factorial(n - 1); }递归有两个关键要素:终止条件和递归步骤。n == 1时返回1,这是出口;否则返回n * factorial(n - 1),把问题规模缩小。如果你忘了写出口,比如写成return n * factorial(n - 1)而没有if判断,那程序会无限调用自己。
为什么无限调用会崩溃?因为每次调用方法都会在栈内存里占用一块空间,叫栈帧。递归层级太深,栈内存会被耗尽,最终抛出StackOverflowError。这个错误名字很直白:栈溢出。第二天不需要深入理解递归的算法复杂度,但可以提前感受“自己调用自己 + 出口”的节奏。后面学链表、树时递归会大量出现,现在留个印象就行。
6. 收尾实战:用循环、数组、方法拼一个成绩统计程序
学完整天的知识点,建议停下来做一个小项目。项目不需要大,但要能把选择结构、循环、数组、方法全用上。我提供一个很经典的练习:成绩统计。
6.1 练习要求与拆解思路
假设有一个班的若干成绩,存放在数组里。需求是输出四项内容:总分、平均分、最高分、每个分数是否及格。
先别急着写代码。先把需求拆成几个独立的功能:
- 求数组元素的总和,这需要循环遍历数组。
- 根据总和和元素个数算平均分,注意整数除法问题。
- 找最大值,需要一个临时变量依次比较。
- 判断每个成绩是否及格,这是典型的选择结构。
- 最后用
main方法把这些功能串起来。
这种拆解思路就是“方法化思维”:先想清楚程序分成哪几个功能块,每个功能块对应一个方法,再往方法里填细节。你会发现,把大的问题拆成小问题后,每个小问题都不难。
6.2 参考实现:从方法拆分到代码落地
下面是完整参考代码,建议自己先写一遍,再对照看差异:
public class ScoreStat { public static void main(String[] args) { int[] scores = {78, 85, 90, 66, 88, 59}; int total = sum(scores); double avg = average(total, scores.length); int max = maxScore(scores); System.out.println("总分: " + total); System.out.println("平均分: " + avg); System.out.println("最高分: " + max); printPassOrFail(scores); } public static int sum(int[] arr) { int s = 0; for (int i = 0; i < arr.length; i++) { s += arr[i]; } return s; } public static double average(int total, int count) { return (double) total / count; } public static int maxScore(int[] arr) { int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } public static void printPassOrFail(int[] arr) { for (int i = 0; i < arr.length; i++) { if (arr[i] >= 60) { System.out.println("第" + (i + 1) + "个分数" + arr[i] + ":及格"); } else { System.out.println("第" + (i + 1) + "个分数" + arr[i] + ":不及格"); } } } }sum方法用下标遍历数组累加;average方法里有个关键点是(double) total / count,因为total和count都是int,如果直接写total / count,结果会被截断成整数。比如总分是466,人数是6,直接除会得到77而不是77.66。强转成double后,除法结果才是浮点数。这种整数除法陷阱,新手第一周至少会遇到三次,当场记下来不亏。
maxScore方法里,我用arr[0]作为初始最大值,然后从下标1开始比较。这里有一个前提:数组不能是空的。如果你传一个长度为0的数组进来,arr[0]就会越界。基础阶段可以先做这个假设,后面学到判空技巧再完善。
6.3 试着扩展一下:把成绩变成随机生成
如果上面代码顺利跑通,时间还有剩,可以试着扩展两个方向。
第一个方向:把写死的成绩数组改成随机生成。Java里可以用Random类:
import java.util.Random; Random r = new Random(); int[] scores = new int[30]; for (int i = 0; i < scores.length; i++) { scores[i] = r.nextInt(101); // 生成0到100的随机数 }这个扩展能让你接触数组长度与循环的结合,也能让每次运行结果都不一样,练习起来更有趣。
第二个方向:在printPassOrFail之外,再加一个统计不及格人数的功能。这只需要在循环里加一个计数器,条件满足就加一。做这个扩展时,你会更熟练地使用if和循环。
做完这两个扩展,到这一步,第二天的内容你已经不是“看过”,而是真正“练过”了。
7. 第二天心态最容易崩的三个瞬间,和处理办法
最后想聊几句心态问题。技术博客通常只管讲知识,但我觉得第二天恰恰是很多人放弃Java的临界点,所以把最常见的三个崩溃瞬间和处理办法写下来。
第一个瞬间:照着教程敲没有任何问题,一关掉教程自己写就卡住。这不是你笨,而是“看懂”和“会写”本来就是两码事。处理办法很笨但很有效:把当天的每一个示例代码都关掉答案,自己重新写一遍,卡住的地方看一眼,再关上继续写。写三遍,代码和你的手就熟了。
第二个瞬间:编译报错一大堆,红字刷屏,心态直接崩。实际上编译器报错并不可怕,可怕的是你想一次性解决所有错误。正确做法是只看第一个错误,按行号定位,改完后重新编译。很多错误是连锁反应,第一个解决了,后面的自然消失。把“读错误信息”当成一项技能来练,哪怕第一天看不懂,坚持一周后你会发现报错信息其实是很好的老师。
第三个瞬间:第二天学完感觉什么都没记住。这其实很正常,基础阶段不是靠背记住的,是靠反复使用建立条件反射。我建议从第三天开始,每天学习新内容前先花十分钟,把第二天的练习程序重新敲一遍。两周之后你再看,这些内容已经像呼吸一样自然。
最后再分享一个小小的实操习惯:每学一个知识点,用三句话给自己做个笔记,这三句话是“它是什么”“它用来解决什么问题”“它最容易出错的点在哪”。不一定要写多长,甚至写到便利贴上就行。第二天的内容看起来琐碎,但控制流、数组、方法这三大块,是后面所有Java代码的地基。地基稳了,上楼才不慌。