Java 入门实战:从零搭建环境到写出第一个可用的命令行工具
Java 这门语言已经存在了近三十年,但它在企业级后端、金融系统、Android 开发中的地位至今没有被撼动。学习 Java 的意义不在于追逐热点,而在于它能帮你建立扎实的编程基本功:强类型系统、面向对象思想、JVM 运行机制,这些概念在后续学习任何语言或框架时都会反复用到。
这篇文章不走"罗列语法"的老路,而是按照真实开发的流程展开:装环境、写代码、踩坑、排错、完成一个能跑的小工具。跟着做一遍,你对 Java 的理解会超过看十篇语法教程。
一、环境搭建:JDK 不是 JRE
很多人入门第一步就栽在环境上。先把概念理清楚:
- JDK(Java Development Kit) :开发工具包,包含编译器
javac、运行时 JRE 和一堆工具(javadoc、jstack等)。写代码必须装 JDK。 - JRE(Java Runtime Environment):只负责运行,不含编译器。
- JVM(Java Virtual Machine):字节码的运行载体,是"一次编译,到处运行"的核心。
三个是包含关系:JDK ⊃ JRE ⊃ JVM。
安装步骤(Windows)
- 从 Adoptium 或 Oracle 官网下载 JDK 17(LTS 长期支持版,企业主流选择)。
- 安装后配置环境变量
JAVA_HOME指向安装目录,如C:\Program Files\Java\jdk-17。 - 在
Path中添加%JAVA_HOME%\bin。
验证安装:
powershell
java -version
javac -version
两个命令都输出版本号才算成功。常见错误是只配了 java 没配 javac,或者机器上装了多个 JDK 导致版本打架------用 where java 查一下实际生效的是哪个。
IDE 选择
- IntelliJ IDEA(社区版免费):智能提示和重构能力业界第一,推荐。
- VS Code + Java 插件:轻量,适合多语言混编的开发者。
入门阶段建议先用记事本 + 命令行编译跑通一个程序,理解 javac 编译、java 运行的过程,再切换到 IDE。否则容易形成"点一下绿色三角就能跑"的黑盒认知。
二、第一个程序:看懂每一行
新建 Hello.java:
java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, Java");
}
}
编译运行:
powershell
javac Hello.java # 生成 Hello.class 字节码文件
java Hello # JVM 加载字节码并执行,注意这里不带 .class 后缀
逐行拆解:
public class Hello:Java 是彻底的面向对象语言,所有代码必须写在类里。文件名必须与 public 类名一致,这是很多新手编译报错的第一个坑。public static void main(String[] args):程序入口方法,签名一个字母都不能改。static意味着 JVM 调用它时不需要先创建对象。System.out.println:System类的静态字段out是一个PrintStream对象,println是它的方法。
对比 Python 的 print("hello"),Java 确实啰嗦。但这种啰嗦换来的是明确的类型约束和结构边界------项目变大后你会感激这一点。
三、语法核心:别死记硬背,理解设计意图
3.1 基本类型与引用类型
Java 有 8 种基本类型:byte、short、int、long、float、double、char、boolean。其余一切都是引用类型(对象)。
需要记住的实际只有几个要点:
int占 4 字节,范围约 ±21 亿。金额计算别用double(浮点精度问题),用long存最小单位(分)或BigDecimal。- 字符串是
String类,不是基本类型。比较内容必须用equals,不能用==:
java
String a = new String("abc");
String b = new String("abc");
System.out.println(a == b); // false,比较的是内存地址
System.out.println(a.equals(b)); // true,比较的是内容
== 对引用类型比较的是"是不是同一个对象",这是面试和实际 Bug 的双重重灾区。
3.2 集合:工作中 80% 的数据结构操作都靠它
数组长度固定、API 贫瘠,实际开发中用得最多的是集合框架。入门掌握三个就够:
java
import java.util.*;
// List:有序、可重复,按索引访问
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
System.out.println(names.get(0)); // 张三
// Map:键值对,按 key 快速查找
Map<String, Integer> scores = new HashMap<>();
scores.put("张三", 90);
scores.put("李四", 85);
System.out.println(scores.get("李四")); // 85
// Set:去重
Set<String> tags = new HashSet<>();
tags.add("java");
tags.add("java"); // 重复元素不会加入
System.out.println(tags.size()); // 1
选型原则:
| 需求 | 选择 |
|---|---|
| 按顺序存取、按下标访问 | ArrayList |
| 频繁增删中间元素 | LinkedList(实际很少用) |
| 键值对查找 | HashMap |
| 键值对 + 有序 | TreeMap / LinkedHashMap |
| 去重 | HashSet |
HashMap 是 Java 面试中出现频率最高的知识点,入门阶段先会用,进阶再去研究它的哈希冲突、扩容机制、JDK 8 的红黑树优化。
3.3 异常处理:程序健壮的底线
异常不是错误,是"可预期的意外"。文件不存在、网络超时、用户输入格式错误,这些都该用异常机制处理,而不是让程序直接崩溃。
java
import java.io.*;
public static String readFirstLine(String path) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(path));
return reader.readLine();
} catch (FileNotFoundException e) {
return "文件不存在: " + path;
} catch (IOException e) {
return "读取失败: " + e.getMessage();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ignored) {}
}
}
}
上面的 finally 关流写法很丑,Java 7 引入的 try-with-resources 可以自动关闭资源:
java
public static String readFirstLine(String path) {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
} catch (IOException e) {
return "读取失败: " + e.getMessage();
}
}
规则:实现了 AutoCloseable 接口的对象,放在 try 的小括号里,用完自动关闭。 这是现代 Java 的标准写法。
检查异常(checked exception)必须捕获或声明抛出,运行时异常(NullPointerException、ArrayIndexOutOfBoundsException 等)属于代码 Bug,应该通过修复逻辑而不是 try-catch 来掩盖。
四、面向对象:从"把代码装进类"到"用类设计程序"
OOP 三大特性:封装、继承、多态。概念背下来没用,关键是理解它们解决什么问题。
用一个简单的银行账户例子说明:
java
public class BankAccount {
// 封装:字段私有,外部不能直接改余额
private String owner;
private double balance;
public BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = Math.max(initialBalance, 0);
}
// 通过方法控制对数据的合法操作
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("存款金额必须大于0");
}
this.balance += amount;
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
this.balance -= amount;
return true;
}
return false;
}
public double getBalance() {
return this.balance;
}
}
封装的本质是保护数据不被随意破坏 。如果 balance 是 public 的,任何代码都能 account.balance = -9999,业务逻辑就形同虚设。
继承和多态的典型场景是"同一操作,不同实现":
java
abstract class Shape {
abstract double area();
}
class Circle extends Shape {
private double radius;
Circle(double r) { this.radius = r; }
@Override
double area() { return Math.PI * radius * radius; }
}
class Rectangle extends Shape {
private double width, height;
Rectangle(double w, double h) { this.width = w; this.height = h; }
@Override
double area() { return width * height; }
}
// 调用方不需要关心具体是哪种形状
public static void printArea(Shape s) {
System.out.println("面积: " + s.area());
}
printArea 接受 Shape 类型,传入圆就按圆算,传入矩形就按矩形算------这就是多态。以后新增一个三角形,printArea 一行都不用改。这就是"对扩展开放,对修改关闭"的开闭原则。
五、实战:写一个命令行记账工具
学了语法不练等于没学。下面这个不到一百行的程序综合了上面讲的所有知识点:类设计、集合、异常处理、文件读写。
功能:记录收支,程序退出时保存到文件,下次启动自动读取。
java
import java.io.*;
import java.util.*;
public class ExpenseTracker {
// 记录类:一笔账
static class Record {
String item;
double amount; // 正数收入,负数支出
Record(String item, double amount) {
this.item = item;
this.amount = amount;
}
// 存成 "餐饮,-35.5" 这种格式
String toLine() {
return item + "," + amount;
}
static Record fromLine(String line) {
String[] parts = line.split(",", 2);
return new Record(parts[0], Double.parseDouble(parts[1]));
}
}
private static final String DATA_FILE = "records.txt";
private final List<Record> records = new ArrayList<>();
public void add(String item, double amount) {
records.add(new Record(item, amount));
}
public void printSummary() {
double total = 0;
System.out.println("===== 账单明细 =====");
for (Record r : records) {
System.out.printf("%-12s %+.2f 元%n", r.item, r.amount);
total += r.amount;
}
System.out.printf("结余: %.2f 元%n", total);
}
public void save() {
try (PrintWriter writer = new PrintWriter(new FileWriter(DATA_FILE))) {
for (Record r : records) {
writer.println(r.toLine());
}
System.out.println("已保存到 " + DATA_FILE);
} catch (IOException e) {
System.err.println("保存失败: " + e.getMessage());
}
}
public void load() {
File file = new File(DATA_FILE);
if (!file.exists()) return; // 第一次运行没有数据文件,正常
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
records.add(Record.fromLine(line));
}
} catch (IOException | NumberFormatException e) {
System.err.println("读取历史数据失败: " + e.getMessage());
}
}
public static void main(String[] args) {
ExpenseTracker tracker = new ExpenseTracker();
tracker.load();
Scanner scanner = new Scanner(System.in);
System.out.println("记账工具 | 输入格式: 项目 金额 (负数为支出),输入 q 退出");
while (true) {
System.out.print("> ");
String input = scanner.nextLine().trim();
if (input.equalsIgnoreCase("q")) break;
String[] parts = input.split("\\s+", 2);
if (parts.length != 2) {
System.out.println("格式错误,示例: 午餐 -35.5");
continue;
}
try {
tracker.add(parts[0], Double.parseDouble(parts[1]));
} catch (NumberFormatException e) {
System.out.println("金额必须是数字");
}
}
tracker.printSummary();
tracker.save();
}
}
编译运行:
powershell
javac ExpenseTracker.java
java ExpenseTracker
这个程序故意用了最简单的 CSV 格式存数据。你可以继续迭代它:给账目加日期、按类别统计、改用 JSON 存储(引入 Jackson 或 Gson 库,顺便学会用 Maven 管理依赖)。每一步迭代都是真实开发中会遇到的问题。
六、避坑清单:新手最常犯的几个错误
- 文件名和 public 类名不一致 → 编译错误。
- 字符串用
==比较 → 逻辑错误,用equals。 - 空指针(NPE) :调用方法前没判断对象是否为
null。养成习惯:方法入参先校验,返回值可能为 null 时考虑用Optional。 switch忘记break→ 穿透到下一个 case。JDK 14+ 的新语法switch ->天然避免了这个问题。- 改了代码忘记重新编译 → 跑的还是旧的
.class文件。用 IDE 后基本不会遇到,但命令行阶段容易中招。 - 包名乱写 → 类找不到。包名全小写,通常是域名倒写,如
com.example.app。
七、后续路线:入门之后往哪走
Java 的知识体系很大,建议按这个顺序推进,每一步都配一个小项目:
| 阶段 | 内容 | 练手项目 |
|---|---|---|
| 1. 核心语法 | 本文全部内容 | 命令行记账工具 |
| 2. 进阶特性 | 泛型、Lambda、Stream API、多线程基础 | 多线程文件下载器 |
| 3. 工程化 | Maven/Gradle、单元测试 JUnit、日志 SLF4J | 给记账工具加测试和依赖管理 |
| 4. Web 开发 | Servlet → Spring Boot、MySQL、REST API | 个人博客后端 |
| 5. 深入 JVM | 类加载、内存模型、GC、JVM 调优 | 分析一次 OOM 事故 |
几个值得收藏的资源:
- 《Java 核心技术 卷I》:公认的入门经典,讲原理不讲黑话。
- Oracle 官方教程(docs.oracle.com/javase/tutorial):免费、权威、有练习。
- 《Effective Java》:进阶必读,等写完两三个项目再看,否则体会不到条目背后的痛点。
结语
学 Java 最大的误区是把时间花在"看"上:看视频、看教程、看别人的笔记。真正有效的路径只有一个------写代码、报错、查文档、改代码、再跑。语法三天能过完,但把 ExpenseTracker 这样的小工具从零写到能用的过程,才是真正在脑子里建立编程直觉的过程。
把上面的代码敲一遍,别复制粘贴。敲错的地方,就是你没学会的地方。