零基础起步:把第一个 Java 程序跑起来
假设你什么都没写过。6 个关卡,每一步都告诉你「做什么 / 敲什么 / 你应该看到 / 报错怎么办」。全程约 40 分钟。
走完这 6 关,你就能
- 能用 javac 编译、java 运行一个类
- 能声明变量、做类型转换、用 printf 格式化输出
- 能写 if-else、for、while,会用数组
- 能定义类、写构造方法、创建对象并调用方法
- 能用 ArrayList 与 HashMap 存数据
- 能用 BufferedReader/Writer 读写文本文件
关卡目录
第 0 关 · 装 JDK、验证环境(5 分钟)
前提:已安装 JDK(没有的话先装 JDK 8 或以上,一路下一步即可)
1① 验证 Java 环境
🎯 打开命令行(Windows 按 Win+R 输入 cmd),执行:
java -version
javac -versionjava version "1.8.0_301" Java(TM) SE Runtime Environment (build 1.8.0_301-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode) javac 1.8.0_301
两条命令都能输出版本号,说明 JDK 装好了、环境变量也对。
'javac' 不是内部或外部命令
→ 只装了 JRE 没装 JDK,或环境变量没配:把 JDK 的 bin 目录加进 PATH。'java' 不是内部或外部命令
→ JAVA_HOME 与 PATH 都没配好,重装 JDK 并勾选设置环境变量。
2② 认识 JDK / JRE / JVM
🎯 三者的关系是面试常问、也是理解“为什么 Java 能跨平台”的关键:
JDK ⊃ JRE ⊃ JVM
JDK = 开发工具(javac、jar)+ JRE
JRE = JVM + 核心类库
JVM = 执行 .class 字节码的虚拟机(这是一张关系说明,不需要执行)
开发装 JDK,只运行装 JRE;跨平台靠的是“字节码 + 各平台的 JVM”。
3③ 建一个练习目录
🎯 后面所有代码都放这个目录,路径别带中文和空格(避免各种奇怪问题)。
mkdir D:\java\demo
cd D:\java\demo(命令行提示符变成 D:\java\demo>)
路径里的反斜杠是 Windows 写法;Linux/Mac 用 /home/yourname/demo。
第 1 关 · 第一个程序:编译与运行(8 分钟)
前提:JDK 可用(第 0 关)
1① 写 HelloWorld.java
🎯 新建文件 HelloWorld.java(注意大小写,文件名必须和类名完全一致)。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}(保存文件,目录下出现 HelloWorld.java)
main 是程序入口,格式固定:public static void main(String[] args)。
2② 编译成字节码
🎯 javac 把 .java 编译成 .class(字节码),JVM 才能执行。
javac HelloWorld.java(没有任何输出,目录下多了一个 HelloWorld.class)
没输出就是成功。Java 的规矩:编译通过不打印任何东西。
错误: 类 HelloWorld 是公共的, 应在名为 HelloWorld.java 的文件中声明
→ 文件名和类名不一致(大小写也算),改成一致即可。错误: 需要 ';'
→ 这一行少了分号——Java 每条语句都必须以 ; 结尾。错误: 找不到符号
→ 变量或方法名拼错、没导入包、或作用域不对。
3③ 运行
🎯 用 java 命令运行类(不带 .class 后缀)。
java HelloWorldHello, Java!
看到这行输出,说明整条链路通了。
错误: 找不到或无法加载主类 HelloWorld
→ 当前目录不对(用 cd 进到 .class 所在目录),或 java 后面多写了 .class。错误: 在类 HelloWorld 中找不到 main 方法
→ main 写错了:拼写、参数、static、void 任一不对都不行。
第 2 关 · 变量、类型与输出(6 分钟)
前提:第 1 关完成
1① 声明不同类型的变量
🎯 Java 是强类型语言:变量必须先声明类型、后使用。
public class VarDemo {
public static void main(String[] args) {
int age = 20; // 整数
double score = 89.5; // 小数
char sex = '男'; // 字符,单引号
boolean pass = true; // 布尔
String name = "张三"; // 字符串,双引号
System.out.println(name + " " + age + " " + score);
System.out.printf("%s 的成绩是 %.1f%n", name, score);
}
}张三 20 89.5 张三 的成绩是 89.5
println 拼接字符串;printf 用 %s/%d/%.1f 占位,%n 是换行。
错误: 不兼容的类型: double 无法转换为 int
→ 把小数赋给 int。要么改类型,要么强制转换 (int)89.5。字符用了双引号
→ char 用单引号 '男',String 用双引号 "男"。
2② 类型转换
🎯 小范围能自动转大范围,反过来必须强制转换(可能丢精度)。
int a = 10;
double b = a; // 自动转换
int c = (int) 9.99; // 强制转换,结果是 9
System.out.println(c);9
强制转换是“截断”不是“四舍五入”。
3③ 从键盘读一个数
🎯 交互式程序必备(需要 import java.util.Scanner)。
import java.util.Scanner;
public class InputDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入年龄:");
int age = sc.nextInt();
System.out.println("你明年 " + (age + 1) + " 岁");
}
}请输入年龄:20 你明年 21 岁
nextInt 读整数,nextDouble、nextLine 分别读小数与整行。
Exception in thread "main" java.util.InputMismatchException
→ 输入的不是数字。要么输数字,要么用 nextLine 再自己解析。
第 3 关 · 流程控制与数组(7 分钟)
前提:第 2 关完成
1① if-else 判断
🎯 按成绩分档,这是最常用的分支结构。
int score = 85;
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}及格
条件从上往下判断,命中一个就不再往下走。
2② for 循环 + 数组
🎯 数组长度固定,用下标访问(从 0 开始)。
int[] scores = {85, 92, 78, 60};
int sum = 0;
for (int i = 0; i < scores.length; i++) {
sum += scores[i];
}
double avg = (double) sum / scores.length;
System.out.println("总分 " + sum + ",平均 " + avg);总分 315,平均 78.75
scores.length 是长度,不是 length()。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
→ 下标越界:循环条件写成 i <= length 就会多跑一次,改成 i < length。
3③ while 与 break/continue
🎯 不定次数的循环用 while;break 跳出、continue 跳过本次。
int i = 1;
while (i <= 5) {
if (i == 3) { i++; continue; }
System.out.print(i + " ");
i++;
}1 2 4 5
3 被 continue 跳过了。死循环是 while 最常见的事故,记得改循环变量。
第 4 关 · 类与对象(8 分钟)
前提:第 3 关完成
1① 写一个 Student 类
🎯 属性私有(封装),通过 getter/setter 访问,构造方法负责初始化。
public class Student {
private String name;
private int age;
private double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() { return name; }
public double getScore() { return score; }
public String toString() {
return name + "(" + age + "岁, " + score + "分)";
}
}(编译通过:javac Student.java)
this.name 用来区分成员变量与参数名。
2② 创建对象并调用方法
🎯 用 new 创建对象,通过对象名.方法 调用。
public class TestStudent {
public static void main(String[] args) {
Student s1 = new Student("张三", 20, 89.5);
Student s2 = new Student("李四", 21, 92);
System.out.println(s1);
System.out.println(s2.getName() + " 的成绩:" + s2.getScore());
}
}张三(20岁, 89.5分) 李四 的成绩:92.0
new 在堆里创建对象,变量里存的是引用地址。
NullPointerException
→ 对象还没 new 就调用方法,先创建对象。错误: 无法将类 Student 中的构造器应用到给定类型
→ 参数个数或类型对不上,检查构造方法签名。
3③ 对象数组
🎯 多个同类对象用数组存起来,配合循环批量处理。
Student[] arr = {
new Student("张三", 20, 89.5),
new Student("李四", 21, 92),
};
double sum = 0;
for (Student s : arr) sum += s.getScore();
System.out.println("平均分:" + sum / arr.length);平均分:90.75
for (类型 变量 : 数组) 是增强 for 循环。
第 5 关 · 集合与文件读写(8 分钟)
前提:第 4 关完成
1① ArrayList 存一组对象
🎯 ArrayList 是长度可变、有顺序的集合(相比数组更常用)。
import java.util.ArrayList;
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三", 20, 89.5));
list.add(new Student("李四", 21, 92));
list.add(new Student("王五", 19, 78));
System.out.println("共 " + list.size() + " 人");
for (Student s : list) System.out.println(s);共 3 人 张三(20岁, 89.5分) 李四(21岁, 92.0分) 王五(19岁, 78.0分)
<Student> 是泛型,限定集合里只能放 Student。
错误: 需要 ';' / 不兼容的类型
→ 忘了导包import java.util.ArrayList;。
2② HashMap 按键取值
🎯 需要“按名字查成绩”这种场景,用 Map。
import java.util.HashMap;
import java.util.Map;
Map<String, Double> map = new HashMap<>();
map.put("张三", 89.5);
map.put("李四", 92.0);
System.out.println(map.get("李四"));
for (Map.Entry<String, Double> e : map.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}92.0 张三 -> 89.5 李四 -> 92.0
Map 的键唯一,重复 put 同一个键会覆盖。
3③ 把数据写到文件
🎯 用字符流 + 缓冲流写文本,不用手动 close(try-with-resources 会自动关)。
import java.io.*;
try (BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt"))) {
bw.write("张三,89.5");
bw.newLine();
bw.write("李四,92");
System.out.println("写入完成");
} catch (IOException e) {
e.printStackTrace();
}写入完成
文件出现在项目根目录(或当前工作目录)。
IOException: 系统找不到指定的路径
→ 文件所在目录不存在,先 mkdir 或用绝对路径。
4④ 读回来
🎯 用 BufferedReader 逐行读,这是最通用的读文本方式。
try (BufferedReader br = new BufferedReader(new FileReader("students.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}张三,89.5 李四,92
readLine() 读到文件末尾返回 null,所以循环条件这样写。
常见报错速查 Ctrl+F 搜报错关键字
| 报错 | 原因 | 解决 |
|---|---|---|
'javac' 不是内部或外部命令,也不是可运行的程序 | JDK 的 bin 目录没加进 PATH | 把 %JAVA_HOME%\bin 加到系统变量 Path,重开命令行 |
错误: 类 X 是公共的, 应在名为 X.java 的文件中声明 | 文件名与 public 类名不一致 | 把文件名改成与类名完全一致(含大小写) |
错误: 需要 ';' | 语句末尾漏了分号(中文分号也不行) | 补英文分号,检查是否用了中文标点 |
错误: 找不到符号 | 变量/方法名拼错,或没导入包,或作用域不对 | 检查拼写、import、变量是否在方法内声明 |
错误: 不兼容的类型 | 类型不匹配(如把 double 赋给 int) | 改类型或用强制转换 (int) |
错误: 找不到或无法加载主类 X | 当前目录不对,或加了 .class 后缀 | cd 到 .class 所在目录,java 后只写类名 |
错误: 在类 X 中找不到 main 方法 | main 签名写错 | 必须是 public static void main(String[] args) |
java.lang.NullPointerException | 对象还没 new 就调用方法/属性 | 先创建对象,或加 null 判断 |
java.lang.ArrayIndexOutOfBoundsException | 数组下标越界 | 循环条件用 i < arr.length |
java.util.InputMismatchException | 输入类型与读取方法不匹配 | 用 nextLine 读整行再自己转换 |
错误: 缺少返回语句 | 有返回值的方法在某些分支没 return | 保证每条路径都有 return |
错误: 未报告的异常错误 X; 必须对其进行捕获或声明以便抛出 | 受检异常没处理 | 用 try-catch 捕获,或在方法签名上 throws |
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver | JDBC 驱动 jar 没导入 | 把 mysql-connector-java.jar 加到项目的 Build Path |
java.lang.NoClassDefFoundError | 类在编译时存在、运行时找不到 | 检查 classpath、jar 是否随程序一起部署 |
完成自检清单
- ☐ 能用 javac 编译、java 运行一个类
- ☐ 能声明变量、做类型转换、用 printf 格式化输出
- ☐ 能写 if-else、for、while,会用数组
- ☐ 能定义类、写构造方法、创建对象并调用方法
- ☐ 能用 ArrayList 与 HashMap 存数据
- ☐ 能用 BufferedReader/Writer 读写文本文件
- ☐ 看到异常信息能定位到哪一行、什么原因