简介:这份资源是韩顺平「循序渐进学Java零基础」课程的完整笔记PDF,面向零基础或刚入门Java编程的学习者,帮助其系统建立Java知识体系并了解实际就业方向。笔记按三个阶段组织:阶段一讲解Java语法、数据类型、控制结构与类对象等基础;阶段二深入异常处理、多线程、I/O流与集合框架;阶段三延伸至数据库操作、网络编程与面向对象设计原则,并配有SSM、Android、Hadoop等开发场景说明。资源包共1个PDF文件,大小约59.36MB,内容以章节笔记形式呈现,目录结构清晰,便于按模块查阅与复习。目前已有2715人学习下载,适合希望从零起步、循序渐进掌握Java核心概念并了解其应用领域的读者作为配套学习资料使用。
1. 零基础学 Java 的第一道坎:为什么你照着视频敲完,还是写不出一个能跑的小工具
很多人第一次接触 Java,是从一套叫“循序渐进学 Java 零基础”的完整笔记开始的。视频里老师敲一行,你跟着敲一行,编译通过,控制台打印出结果,那一刻觉得自己会了。可关掉视频,让你独立写一个“从键盘读三个数,输出最大值”的小程序,手就停在Scanner那一行,不知道下一步该写什么。这不是你笨,而是“跟着敲”和“自己写”之间,隔着一条叫“知识结构化”的沟。这套笔记的价值,不在于它讲了多少语法,而在于它把 Java 零基础到能写小项目的路径拆成了可复现的台阶。这篇文章要做的,就是把这些台阶重新铺一遍,告诉你每一步踩在哪里、参数怎么设、哪里最容易翻车,让你从“看懂了”走到“写得出”。
2. 把 JDK、IDE 和第一个类跑通:环境配置的四个硬指标
2.1 JDK 版本怎么选:LTS 是底线,不是建议
零基础最容易在第一步就卡住:JDK 到底装哪个版本。网上搜一圈,有人说 8,有人说 17,有人说 21。我的血泪经验是:只选 LTS 版本,且优先选当前主流的 LTS。LTS 是长期支持版,意味着语法稳定、教程兼容、企业里跑得最多。截至我写这篇笔记时,主流选择是 JDK 17 或 JDK 21。如果你跟的笔记里用的是 JDK 8 的语法,那用 17 跑也完全没问题,因为 Java 向下兼容做得很好。但反过来,用 8 去跑 17 的新特性(比如record、switch表达式)就会直接编译失败。
安装时有一个参数必须确认:JAVA_HOME。Windows 上很多人装完 JDK,java -version能跑,但javac报“不是内部或外部命令”,就是因为只把bin目录加进了Path,没设JAVA_HOME。正确做法是:
# 以 JDK 17 为例,安装路径假设为 C:\Java\jdk-17 # 设置 JAVA_HOME 指向 JDK 根目录,不是 bin JAVA_HOME=C:\Java\jdk-17 # Path 里追加两个条目,顺序不能反 Path=%JAVA_HOME%\bin;%Path%逻辑说明:JAVA_HOME是给其他工具(Maven、Gradle、Tomcat)定位 JDK 用的,Path是给命令行定位java和javac用的。只配Path不配JAVA_HOME,后面学构建工具时必翻车。验证命令:
java -version javac -version两条命令输出的版本号必须一致。如果java是 17 而javac是 8,说明系统里存在多个 JDK,Path顺序错了。
2.2 IDE 选哪个:新手别碰 Eclipse,直接上 IntelliJ IDEA 社区版
零基础阶段,工具越少越好。Eclipse 需要手动配构建路径、装插件、调编码格式,每一步都是坑。IntelliJ IDEA 社区版免费,开箱即用,新建项目时自动帮你生成src目录和模块配置。安装时只有一个注意点:不要勾选“Add launchers dir to the PATH”,除非你确定自己需要命令行启动 IDE。这个选项会把 IDEA 的启动脚本塞进系统路径,后面如果卸载不干净,命令行里会残留一堆无效命令。
新建第一个项目时,IDEA 会让你选 JDK。这里直接指向你刚才设的JAVA_HOME路径。项目结构长这样:
my-first-java/ ├── src/ │ └── Main.java └── my-first-java.imlsrc是源码根目录,所有.java文件放这里。.iml是 IDEA 的模块配置文件,不用手动改。
2.3 第一个类:从main方法到控制台输出
零基础笔记里第一个程序通常是HelloWorld。但我要你多写一步:把HelloWorld改成一个能接收输入并做判断的小程序。因为只输出字符串,你练不到Scanner、变量、条件判断这三个核心点。
import java.util.Scanner; public class Main { public static void main(String[] args) { // 创建 Scanner 对象,绑定标准输入流 Scanner scanner = new Scanner(System.in); System.out.print("请输入第一个整数:"); int a = scanner.nextInt(); // 阻塞等待用户输入一个整数 System.out.print("请输入第二个整数:"); int b = scanner.nextInt(); // 三元运算符判断最大值 int max = (a > b) ? a : b; System.out.println("最大值是:" + max); scanner.close(); // 关闭输入流,释放资源 } }逻辑说明:Scanner是 Java 标准库里的输入工具类,nextInt()方法会阻塞程序,直到用户在控制台输入一个整数并回车。如果输入的不是整数(比如字母),程序会抛InputMismatchException,这是新手最常见的运行时错误之一。参数说明:System.in是标准输入流,对应键盘;System.out是标准输出流,对应控制台。scanner.close()不是必须的,但养成习惯,后面学文件 IO 时不会忘。
运行方式:在 IDEA 里右键Main.java,选Run 'Main.main()'。控制台会依次提示输入,输入两个整数后输出最大值。如果你在命令行里跑,先javac Main.java编译,再java Main运行。
注意:类名必须和文件名完全一致,包括大小写。
Main.java里只能有一个public class Main。这是 Java 的硬性规定,不是风格建议。
3. 变量、循环、数组:把语法练成肌肉记忆的三个训练法
3.1 变量作用域:为什么你的for循环里定义的变量在外面用不了
零基础笔记讲到变量时,通常会列一堆数据类型:int、double、boolean、char、String。但真正让新手翻车的不是类型本身,而是作用域。看这段代码:
public class ScopeDemo { public static void main(String[] args) { for (int i = 0; i < 3; i++) { int sum = i * 2; System.out.println("循环内 sum = " + sum); } // 下面这行会编译报错:找不到符号 sum // System.out.println("循环外 sum = " + sum); } }逻辑说明:sum定义在for循环的花括号{}内部,它的生命周期只在这个块里。出了这个块,变量就被销毁了。这是 Java 块级作用域的基本规则。新手常犯的错是:在循环里累加一个变量,却把变量定义在循环内部,导致每次循环都重新创建,累加结果永远是最后一次的值。正确做法是把累加变量定义在循环外面:
int total = 0; // 定义在循环外 for (int i = 1; i <= 100; i++) { total += i; // 累加 } System.out.println("1 到 100 的和是:" + total);参数说明:total的作用域是整个main方法,循环内外的代码都能访问。i的作用域仅限于for循环,循环结束后i不可见。这个区别在写嵌套循环时尤其重要。
3.2 循环的三个必调参数:初始值、终止条件、步长
for循环的语法是for (初始值; 终止条件; 步长)。零基础阶段,90% 的循环 bug 都出在这三个参数上。我一般会让学生做这个练习:打印 1 到 100 之间所有能被 3 整除但不能被 5 整除的数。
public class LoopPractice { public static void main(String[] args) { int count = 0; // 计数器,统计符合条件的个数 for (int i = 1; i <= 100; i++) { // 初始值 1,终止条件 <=100,步长 +1 if (i % 3 == 0 && i % 5 != 0) { // 能被 3 整除 且 不能被 5 整除 System.out.print(i + " "); count++; if (count % 5 == 0) { // 每输出 5 个数换一行 System.out.println(); } } } System.out.println("\n符合条件的数共有:" + count + " 个"); } }逻辑说明:i % 3 == 0判断整除,i % 5 != 0判断不整除,&&是逻辑与,两个条件同时成立才执行输出。count % 5 == 0用来控制换行,每输出 5 个数字换一行,避免所有数字挤在一行看不清。参数说明:初始值i = 1不能写成i = 0,因为 0 能被任何非零整数整除,会多出一个不符合预期的结果。终止条件i <= 100不能写成i < 100,否则会漏掉 100。步长i++不能写成i += 2,否则会跳过所有偶数,漏掉像 6、12 这样能被 3 整除的偶数。
3.3 数组:从声明到遍历,三个必须记住的边界
数组是零基础阶段第一个“批量处理数据”的工具。声明数组有三种写法:
int[] arr1 = new int[5]; // 声明并分配空间,默认值全为 0 int[] arr2 = {1, 2, 3, 4, 5}; // 声明并初始化,长度由元素个数决定 int[] arr3 = new int[]{1, 2, 3}; // 完整写法,不常用但要知道逻辑说明:new int[5]在堆内存里分配了 5 个连续的int空间,每个元素默认值是 0。{1, 2, 3, 4, 5}是简化写法,只能在声明时使用,不能先声明再赋值。参数说明:数组的长度用arr.length获取,注意没有括号,不是arr.length()。这是新手最容易写错的地方之一。
遍历数组有两种方式:
int[] scores = {85, 92, 78, 96, 88}; // 方式一:普通 for 循环,能拿到索引 for (int i = 0; i < scores.length; i++) { System.out.println("第 " + (i + 1) + " 个成绩:" + scores[i]); } // 方式二:增强 for 循环,只能拿值,拿不到索引 for (int score : scores) { System.out.println("成绩:" + score); }逻辑说明:普通for循环的终止条件是i < scores.length,不是i <= scores.length。写成<=会抛ArrayIndexOutOfBoundsException,因为数组索引从 0 开始,最大索引是length - 1。增强for循环底层也是普通for,但语法更简洁,适合只读遍历。如果你需要在遍历时修改数组元素,必须用普通for,因为增强for里的变量是值的拷贝。
注意:数组一旦创建,长度不可变。
arr.length是属性,不是方法。想动态扩容,需要用ArrayList,那是后面集合框架的内容。
4. 避坑:零基础学 Java 最容易翻车的五个场景
4.1 现象:javac报“编码 GBK 的不可映射字符” → 原因:源文件编码和编译编码不一致 → 解决:统一用 UTF-8
Windows 中文环境下,记事本保存的.java文件默认是 GBK 编码,而javac默认用平台编码编译。如果文件里有中文注释或中文字符串,就会报这个错。解决方式有两种:一是把文件编码改成 UTF-8(IDEA 默认就是 UTF-8),二是编译时指定编码:
javac -encoding UTF-8 Main.java我一般会直接在 IDEA 里把File -> Settings -> Editor -> File Encodings全部设为 UTF-8,包括 Global Encoding、Project Encoding、Default encoding for properties files。这样新建的文件不会再有编码问题。
4.2 现象:java Main报“找不到或无法加载主类” → 原因:类名和文件名不一致,或者包路径不对 → 解决:检查public class后面的名字
Java 要求public类的类名必须和文件名完全一致。Main.java里写public class main会编译失败。另外,如果类在包里(比如package com.example;),运行时必须用完整类名:
java com.example.Main而且要在包结构的根目录下运行,不是进到com/example/里面运行。这是新手用命令行跑 Java 时最常见的错误。
4.3 现象:Scanner输入字符串时跳过,直接执行下一行 → 原因:nextInt()后残留了换行符 → 解决:用nextLine()吃掉换行
看这段代码:
Scanner scanner = new Scanner(System.in); System.out.print("请输入年龄:"); int age = scanner.nextInt(); // 用户输入 25 后按回车 System.out.print("请输入姓名:"); String name = scanner.nextLine(); // 这里会直接跳过,name 为空字符串原因:nextInt()只读取整数部分,回车符\n还留在输入缓冲区里。nextLine()一执行,立刻读到这个残留的换行符,返回空字符串。解决方式是在nextInt()后面加一行scanner.nextLine()把换行符吃掉:
int age = scanner.nextInt(); scanner.nextLine(); // 吃掉残留的换行符 String name = scanner.nextLine(); // 这次能正常读取4.4 现象:整数除法结果永远是整数,小数部分被丢弃 → 原因:两个int相除,结果自动向下取整 → 解决:把其中一个操作数转成double
int a = 7; int b = 2; System.out.println(a / b); // 输出 3,不是 3.5 System.out.println((double) a / b); // 输出 3.5逻辑说明:Java 的整数除法规则是:两个整数相除,结果还是整数,小数部分直接截断(不是四舍五入)。想得到小数结果,必须把至少一个操作数转成浮点类型。(double) a / b先把a转成double,然后double / int自动把b也提升为double,结果就是double。
4.5 现象:==比较字符串时,两个内容相同的字符串返回false→ 原因:==比较的是引用地址,不是内容 → 解决:用.equals()方法
String s1 = "hello"; String s2 = new String("hello"); System.out.println(s1 == s2); // false,地址不同 System.out.println(s1.equals(s2)); // true,内容相同逻辑说明:s1指向字符串常量池里的对象,s2指向堆内存里新创建的对象,地址不同,所以==返回false。.equals()是String类重写的方法,比较的是字符序列的内容。零基础阶段记住一条:字符串比较一律用.equals(),不要用==。除非你明确知道自己在比较引用地址。
5. 从能跑到能改:用一个小项目把零散语法串起来
5.1 项目目标:控制台版学生成绩管理器
这个项目不涉及数据库、不涉及界面,只用你前面学的变量、循环、数组、Scanner和条件判断。功能需求:
| 功能编号 | 功能描述 | 涉及知识点 |
|---|---|---|
| F1 | 录入 5 个学生成绩 | 数组、Scanner、for循环 |
| F2 | 打印所有成绩 | 数组遍历 |
| F3 | 计算平均分 | 累加、除法、类型转换 |
| F4 | 找出最高分和最低分 | 条件判断、循环 |
| F5 | 统计及格人数(>=60) | 条件判断、计数器 |
5.2 代码实现:一个类,五个方法
import java.util.Scanner; public class ScoreManager { // 常量:学生人数,方便统一修改 static final int STUDENT_COUNT = 5; public static void main(String[] args) { double[] scores = new double[STUDENT_COUNT]; Scanner scanner = new Scanner(System.in); // F1:录入成绩 for (int i = 0; i < STUDENT_COUNT; i++) { System.out.print("请输入第 " + (i + 1) + " 个学生的成绩:"); scores[i] = scanner.nextDouble(); } // F2:打印所有成绩 System.out.print("所有成绩:"); for (double score : scores) { System.out.print(score + " "); } System.out.println(); // F3:计算平均分 double sum = 0; for (double score : scores) { sum += score; } double average = sum / STUDENT_COUNT; System.out.printf("平均分:%.2f%n", average); // F4:找最高分和最低分 double max = scores[0]; double min = scores[0]; for (int i = 1; i < scores.length; i++) { if (scores[i] > max) max = scores[i]; if (scores[i] < min) min = scores[i]; } System.out.println("最高分:" + max); System.out.println("最低分:" + min); // F5:统计及格人数 int passCount = 0; for (double score : scores) { if (score >= 60) passCount++; } System.out.println("及格人数:" + passCount); scanner.close(); } }逻辑说明:STUDENT_COUNT用static final修饰,是常量,全大写命名。这样改学生人数时只需要改一个地方。scores数组用double类型,因为成绩可能有小数。printf的%.2f表示保留两位小数,%n是跨平台换行符。找最高分和最低分时,初始值设为scores[0],循环从索引 1 开始,避免自己和自己比较。参数说明:scanner.nextDouble()读取浮点数,如果输入非数字会抛异常。实际项目中需要加try-catch做输入校验,但零基础阶段先跑通主流程。
5.3 验证方法:三组测试数据覆盖边界
跑通代码只是第一步,验证才是关键。用这三组数据测试:
| 测试组 | 输入数据 | 预期结果 | 验证点 |
|---|---|---|---|
| T1 | 60, 60, 60, 60, 60 | 平均 60,最高 60,最低 60,及格 5 | 边界值全部及格 |
| T2 | 59, 59, 59, 59, 59 | 平均 59,最高 59,最低 59,及格 0 | 边界值全部不及格 |
| T3 | 0, 100, 50, 75, 88 | 平均 62.6,最高 100,最低 0,及格 3 | 混合数据 |
如果 T1 的及格人数输出 5,T2 输出 0,说明>= 60的判断正确。如果 T3 的平均分输出 62.6 而不是 62,说明double类型和printf格式化都正确。任何一组不符合预期,就回到对应的方法里检查循环边界和条件判断。
注意:
scanner.nextDouble()在输入整数时也能正常读取,比如输入60会当成60.0。但输入abc会直接抛InputMismatchException,程序崩溃。这是零基础阶段可以接受的,但你要知道这个边界在哪里。
5.4 一个具体技巧:用printf对齐输出,让控制台结果像表格
零基础阶段很多人输出成绩时用字符串拼接,结果长短不一,控制台看起来乱七八糟。printf可以指定宽度和对齐方式:
System.out.printf("%-10s%-10s%n", "姓名", "成绩"); System.out.printf("%-10s%-10.2f%n", "A同学", 85.5); System.out.printf("%-10s%-10.2f%n", "B同学", 92.0);逻辑说明:%-10s表示左对齐,占 10 个字符宽度,不足补空格。%-10.2f表示左对齐,占 10 个字符宽度,保留两位小数。%n是换行。这样输出的表格列是对齐的,看起来舒服很多。参数说明:-表示左对齐,不加-默认右对齐。10是总宽度,包括小数点和小数部分。如果实际内容超过 10 个字符,宽度会自动扩展,不会截断。
我自己的习惯是:只要输出涉及多列数据,一律用printf,不用字符串拼接。这个习惯从零基础阶段就养成,后面写日志、输出报表时能省很多对齐的功夫。希望帮到你。
本文还有配套的精品资源,点击获取