用两周时间,真正入门 Java
这不是一本枯燥的教科书。这是一份精心设计的互动学习路径——每课都有可运行示例、即时反馈测验和动手练习,帮你从零基础到达大二计算机专业的 Java 水平。
渐进式学习
从变量到泛型,14 天循序渐进,每天聚焦一个核心主题。
即时反馈
每个知识点配有代码示例和输出预览,学完立刻测验巩固。
动手练习
每课结束有填空编程练习,在实战中检验你的理解。
Java 简介与第一个程序
了解 Java 的特点,写出你的第一行代码。
Java 是什么
Java 是一门 面向对象、跨平台 的编程语言。它的核心理念是 "一次编写,到处运行"(Write Once, Run Anywhere)——编译后的字节码可以在任何装有 JVM(Java 虚拟机)的平台上运行。
第一个程序:Hello World
每个程序员的起点。来看 Java 版的 Hello World:
public class HelloWorld {
// 程序的入口,main 方法
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
•
public class HelloWorld — 声明一个公开类,类名必须与文件名一致•
public static void main(String[] args) — 程序入口方法,固定写法•
System.out.println(...) — 向控制台输出一行文字•
// — 单行注释,编译器会忽略编译与运行
Java 是 编译型 语言。源代码 .java 文件需要先编译成 .class 字节码,再由 JVM 执行:
# 1. 编译源文件
javac HelloWorld.java
# 2. 运行字节码
java HelloWorld
输出方法对比
| 方法 | 效果 | 示例 |
|---|---|---|
System.out.println() | 输出后换行 | println("Hi") → Hi |
System.out.print() | 输出不换行 | print("Hi") → Hi |
System.out.printf() | 格式化输出 | printf("%.2f", 3.14) |
public class MyFirst {
public static void main(String[] args) {
System.out.("Java 很有趣!");
}
} 第 1 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
变量与数据类型
变量是存储数据的容器,数据类型决定了容器的大小和用途。
基本数据类型
Java 是 强类型语言,每个变量必须先声明类型。Java 有 8 种基本类型:
| 类型 | 关键字 | 大小 | 范围 / 示例 |
|---|---|---|---|
| 整数 | byte | 1 字节 | -128 ~ 127 |
| 整数 | short | 2 字节 | -32768 ~ 32767 |
| 整数 | int | 4 字节 | 约 ±21 亿(最常用) |
| 整数 | long | 8 字节 | 很大,加 L 后缀 |
| 浮点 | float | 4 字节 | 加 f 后缀 |
| 浮点 | double | 8 字节 | 默认浮点类型 |
| 字符 | char | 2 字节 | 单个 Unicode 字符 |
| 布尔 | boolean | 1 位 | true / false |
public class Types {
public static void main(String[] args) {
int age = 20;
double price = 19.99;
char grade = 'A';
boolean isStudent = true;
long population = 7900000000L;
float pi = 3.14f;
System.out.println("年龄: " + age);
System.out.println("价格: " + price);
System.out.println("等级: " + grade);
System.out.println("是学生: " + isStudent);
}
}
价格: 19.99
等级: A
是学生: true
long 值要加 L,float 值要加 f,否则编译报错。字符用 单引号 'A',字符串用 双引号 "Hi"。变量命名规则
Java 变量命名遵循 驼峰命名法(camelCase):
| 规则 | 合法示例 | 非法示例 |
|---|---|---|
| 字母/数字/下划线/$,不能数字开头 | userName | 2name |
| 不能是关键字 | myClass | class |
| 区分大小写 | age ≠ Age | — |
| 驼峰风格,见名知意 | studentCount | sc |
类型转换
小类型可以自动转大类型(隐式),大转小需要强制转换(可能丢失精度):
public class Cast {
public static void main(String[] args) {
// 自动转换:int → double
int i = 100;
double d = i;
System.out.println("自动转换: " + d);
// 强制转换:double → int
double pi = 3.99;
int n = (int) pi; // 直接截断小数部分
System.out.println("强制转换: " + n);
}
}
强制转换: 3
String 字符串
String 不是基本类型,而是 引用类型(类),但使用频率极高:
public class StringDemo {
public static void main(String[] args) {
String name = "张三";
String greeting = "你好";
// 字符串拼接用 +
System.out.println(greeting + ", " + name + "!");
// 常用方法
System.out.println("长度: " + name.length());
System.out.println("大写: " + "hello".toUpperCase());
System.out.println("连接: " + "a".concat("b"));
}
}
长度: 2
大写: HELLO
连接: ab
public class Vars {
public static void main(String[] args) {
count = 100;
active = true;
System.out.println(count + " " + active);
}
} 第 2 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
运算符与表达式
运算符是操作数据的符号,掌握它们是编程的基本功。
算术运算符
public class Arithmetic {
public static void main(String[] args) {
int a = 17, b = 5;
System.out.println("加: " + (a + b)); // 22
System.out.println("减: " + (a - b)); // 12
System.out.println("乘: " + (a * b)); // 85
System.out.println("除: " + (a / b)); // 3(整数除法,截断)
System.out.println("取余: " + (a % b)); // 2
}
}
减: 12
乘: 85
除: 3
取余: 2
17 / 5 结果是 3 而不是 3.4!两个整数相除,结果还是整数(直接截断)。若想得到小数,需转为 double:(double)a / b。自增自减
public class IncDec {
public static void main(String[] args) {
int i = 5;
System.out.println(i++); // 5(先使用,后加1)
System.out.println(i); // 6
System.out.println(++i); // 7(先加1,后使用)
}
}
6
7
i++ 先用后加,++i 先加后用。实际开发中为避免混淆,建议单独写 i++;。
关系与逻辑运算符
| 类别 | 运算符 | 含义 |
|---|---|---|
| 关系 | == | 等于 |
!= | 不等于 | |
> < | 大于 / 小于 | |
>= <= | 大于等于 / 小于等于 | |
| 返回 boolean 值 | ||
== 比较基本类型比值,比较对象比地址 | ||
| 逻辑 | && | 与(短路):两边都 true 才 true |
|| | 或(短路):一边 true 就 true | |
! | 非:取反 | |
public class Logic {
public static void main(String[] args) {
int score = 75;
// 逻辑与
boolean pass = score >= 60 && score <= 100;
System.out.println("及格: " + pass);
// 逻辑或
boolean holiday = false;
boolean weekend = true;
boolean canRest = holiday || weekend;
System.out.println("可以休息: " + canRest);
// 短路特性
int x = 0;
if (x != 0 && 10 / x > 1) {
System.out.println("不会执行到这里");
}
System.out.println("短路避免了除零错误");
}
}
可以休息: true
短路避免了除零错误
&& 如果左边为 false,右边不执行;|| 如果左边为 true,右边不执行。这可以用来避免空指针等错误。赋值与三元运算符
public class Assign {
public static void main(String[] args) {
int n = 10;
n += 5; // 等价于 n = n + 5
System.out.println("+= 后: " + n); // 15
// 三元运算符: 条件 ? 真值 : 假值
int age = 17;
String status = age >= 18 ? "成年" : "未成年";
System.out.println(status);
}
}
未成年
public class Ternary {
public static void main(String[] args) {
int score = 85;
String result = score 60 ? "及格" : "不及格";
System.out.println(result);
}
} 第 3 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
条件语句
让程序学会"做选择"——根据不同条件执行不同代码。
if-else 语句
条件从上到下依次判断,一旦匹配就执行对应分支,不再继续判断后面的条件。
public class IfElse {
public static void main(String[] args) {
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("不及格");
}
}
}
嵌套 if
public class Nested {
public static void main(String[] args) {
int age = 20;
boolean hasLicense = true;
if (age >= 18) {
if (hasLicense) {
System.out.println("可以开车");
} else {
System.out.println("成年了,但没驾照");
}
} else {
System.out.println("未成年,不能开车");
}
}
}
switch 语句
当需要将一个变量与多个固定值比较时,switch 比 if-else 更清晰:
public class Switch {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
default:
System.out.println("其他");
}
}
}
break,程序会"穿透"(fall-through)继续执行后面的 case,直到遇到 break 或 switch 结束。这通常是个 bug。switch 新语法(Java 14+)
Java 14 引入了增强 switch,使用箭头语法,自动不穿透:
public class SwitchNew {
public static void main(String[] args) {
int day = 3;
String name = switch (day) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
default -> "其他";
};
System.out.println(name);
}
}
public class ColorSwitch {
public static void main(String[] args) {
int color = 1;
switch (color) {
1:
System.out.println("红色");
;
default:
System.out.println("未知");
}
}
} 第 4 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
循环
让程序自动重复执行,告别复制粘贴。
for 循环
当你知道循环次数时,for 循环是首选:
for (初始化; 条件; 更新)——三个部分用分号隔开。条件为 false 时循环结束。
public class ForLoop {
public static void main(String[] args) {
// 打印 1 到 5
for (int i = 1; i <= 5; i++) {
System.out.println("第 " + i + " 次");
}
}
}
第 2 次
第 3 次
第 4 次
第 5 次
while 循环
当不确定循环次数,只关心条件是否成立时用 while:
public class WhileLoop {
public static void main(String[] args) {
int n = 1024;
int count = 0;
// 不断除以 2,直到 n 变为 0
while (n > 0) {
n = n / 2;
count++;
}
System.out.println("循环了 " + count + " 次");
}
}
do-while 循环
do-while 先执行一次再判断条件,保证 至少执行一次:
public class DoWhile {
public static void main(String[] args) {
int i = 10;
do {
System.out.println("i = " + i);
i++;
} while (i < 5); // 条件为 false,但已经执行了一次
}
}
break 与 continue
public class BreakContinue {
public static void main(String[] args) {
// break: 跳出整个循环
System.out.println("=== break 示例 ===");
for (int i = 1; i <= 10; i++) {
if (i == 5) break; // 到 5 就停
System.out.println(i);
}
// continue: 跳过本次,继续下一次
System.out.println("=== continue 示例 ===");
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // 跳过偶数
System.out.println(i);
}
}
}
1
2
3
4
=== continue 示例 ===
1
3
5
7
9
嵌套循环:九九乘法表
public class Multiplication {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.printf("%d×%d=%-4d", j, i, i * j);
}
System.out.println();
}
}
}
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
%d 整数,%-4d 左对齐占 4 位宽度,%n 换行。这是经典面试题,务必理解嵌套循环的执行顺序。public class Sum {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i 100; i++) {
sum i;
}
System.out.println("总和: " + sum);
}
} 第 5 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
方法
方法是组织代码的基本单元——把重复逻辑封装起来,调用时只需一个名字。
方法的定义与调用
public class MethodBasic {
// 定义方法:修饰符 返回类型 方法名(参数列表)
public static int add(int a, int b) {
return a + b;
}
// 无返回值用 void
public static void greet(String name) {
System.out.println("你好, " + name + "!");
}
public static void main(String[] args) {
int result = add(3, 5); // 调用有返回值的方法
System.out.println("3 + 5 = " + result);
greet("张三"); // 调用无返回值的方法
}
}
你好, 张三!
return 返回结果并结束方法;void 方法不需要 return。方法重载
同一个类中可以有多个 同名 方法,只要 参数列表不同(个数、类型、顺序)。编译器根据调用参数自动选择:
public class Overload {
// 两个 int 相加
public static int add(int a, int b) {
return a + b;
}
// 三个 int 相加
public static int add(int a, int b, int c) {
return a + b + c;
}
// 两个 double 相加
public static double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
System.out.println(add(1, 2)); // 调用第一个
System.out.println(add(1, 2, 3)); // 调用第二个
System.out.println(add(1.5, 2.5)); // 调用第三个
}
}
6
4.0
值传递
Java 方法参数是 值传递——方法内修改基本类型参数不影响外部:
public class PassByValue {
public static void change(int x) {
x = 100; // 只改了副本
}
public static void main(String[] args) {
int n = 10;
change(n);
System.out.println("n = " + n); // 还是 10
}
}
变量作用域
变量只在声明它的 {} 内有效:
public class Scope {
public static void main(String[] args) {
int x = 10; // main 方法作用域
if (x > 5) {
int y = 20; // if 块作用域
System.out.println(x + y); // 30
}
// System.out.println(y); // 编译错误!y 在这里不可见
}
}
递归
方法调用自己就是递归。必须有 终止条件,否则栈溢出。经典例子——阶乘:
public class Recursion {
// n! = n * (n-1)!
public static int factorial(int n) {
if (n <= 1) return 1; // 终止条件
return n * factorial(n - 1); // 递归调用
}
public static void main(String[] args) {
System.out.println("5! = " + factorial(5)); // 120
}
}
StackOverflowError。public class MaxMethod {
public static max(int a, int b) {
if (a > b) {
a;
}
return b;
}
public static void main(String[] args) {
System.out.println(max(3, 7));
}
} 第 6 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
数组
数组是存储多个同类型数据的容器——最基础的数据结构。
创建与访问
public class ArrayBasic {
public static void main(String[] args) {
// 声明并初始化
int[] scores = {90, 85, 78, 92, 88};
// 访问元素(下标从 0 开始)
System.out.println("第一个: " + scores[0]);
System.out.println("第三个: " + scores[2]);
// 数组长度
System.out.println("长度: " + scores.length);
// 修改元素
scores[1] = 95;
System.out.println("修改后: " + scores[1]);
}
}
第三个: 78
长度: 5
修改后: 95
scores[5] 会抛出 ArrayIndexOutOfBoundsException(数组越界异常)。其他创建方式
public class ArrayCreate {
public static void main(String[] args) {
// 方式1: 先声明再分配空间
int[] a = new int[5]; // 默认值全为 0
a[0] = 10;
// 方式2: 直接初始化
int[] b = {1, 2, 3};
// 方式3: new + 初始化
String[] names = new String[]{"Alice", "Bob", "Carol"};
System.out.println(a[0] + " " + a[1]); // 10 0
System.out.println(names.length);
}
}
3
遍历数组
for-each 更简洁,但不方便获取下标,也不能修改数组元素。
public class ArrayLoop {
public static void main(String[] args) {
int[] nums = {10, 20, 30, 40, 50};
// 方式1: 普通 for 循环
System.out.print("for: ");
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " ");
}
System.out.println();
// 方式2: 增强 for(for-each)
System.out.print("for-each: ");
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println();
}
}
for-each: 10 20 30 40 50
实战:求最大值
public class ArrayMax {
public static void main(String[] args) {
int[] nums = {3, 7, 2, 9, 5, 1, 8};
int max = nums[0]; // 假设第一个最大
for (int i = 1; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
System.out.println("最大值: " + max);
}
}
二维数组
public class TwoD {
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 遍历二维数组
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();
}
}
}
4 5 6
7 8 9
public class ArraySum {
public static void main(String[] args) {
int[] nums = {10, 20, 30};
int sum = 0;
for (int n nums) {
sum n;
}
System.out.println("总和: " + sum);
}
} 第 7 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
面向对象基础:类与对象
面向对象编程(OOP)是 Java 的灵魂——用"类"描述事物,用"对象"操作实例。
类与对象的概念
对象(Object) = 类的实例,通过
new 创建举例:Dog 是类,我家那只叫"旺财"的狗是对象。
定义第一个类
// 学生类
public class Student {
// 属性(字段 / 成员变量)
String name;
int age;
double score;
// 方法(行为)
public void study() {
System.out.println(name + " 正在学习");
}
public void introduce() {
System.out.println("我叫 " + name + ",今年 " + age + " 岁");
}
}
public class Main {
public static void main(String[] args) {
// 创建对象
Student s1 = new Student();
s1.name = "张三";
s1.age = 20;
s1.score = 92.5;
// 调用方法
s1.introduce();
s1.study();
// 创建另一个对象
Student s2 = new Student();
s2.name = "李四";
s2.age = 21;
s2.introduce();
}
}
张三 正在学习
我叫 李四,今年 21 岁
成员变量 vs 局部变量
| 对比项 | 成员变量(字段) | 局部变量 |
|---|---|---|
| 位置 | 类中、方法外 | 方法内 |
| 默认值 | 有(int→0, 引用→null) | 无,必须初始化 |
| 作用域 | 整个类 | 方法内 |
| 存储 | 堆(随对象) | 栈(随方法) |
this 关键字
this 指向 当前对象,常用于区分成员变量和参数同名的情况:
public class Book {
String title;
double price;
public void setInfo(String title, double price) {
this.title = title; // this.title 是成员变量
this.price = price; // 右边 title 是参数
}
public void show() {
System.out.println("《" + title + "》 价格: " + price);
}
public static void main(String[] args) {
Book b = new Book();
b.setInfo("Java入门", 59.9);
b.show();
}
}
static 修饰符
static 修饰的成员属于 类本身,不需要创建对象就能用:
public class MathUtil {
// 静态变量:所有对象共享
static int count = 0;
// 静态方法:直接用类名调用
public static int square(int n) {
return n * n;
}
public MathUtil() {
count++; // 每创建一个对象,count 加 1
}
public static void main(String[] args) {
System.out.println("3 的平方: " + MathUtil.square(3));
new MathUtil();
new MathUtil();
System.out.println("创建了 " + MathUtil.count + " 个对象");
}
}
创建了 2 个对象
public class Test {
public static void main(String[] args) {
s = new Student();
s. = "王五";
}
} 第 8 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
封装与构造方法
封装是面向对象三大特性之一,构造方法是创建对象的入口。
封装:隐藏细节,暴露接口
封装的核心:把属性设为 private,通过 public 的 getter/setter 控制访问。好处是可以在 setter 中加入 数据校验:
public class Account {
private String owner;
private double balance;
// getter
public double getBalance() {
return balance;
}
// setter:加入校验逻辑
public void setBalance(double balance) {
if (balance < 0) {
System.out.println("余额不能为负!");
return;
}
this.balance = balance;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
public class AccountTest {
public static void main(String[] args) {
Account acc = new Account();
acc.setOwner("张三");
acc.setBalance(1000);
System.out.println(acc.getOwner() + " 余额: " + acc.getBalance());
acc.setBalance(-500); // 会被拦截
System.out.println(acc.getOwner() + " 余额: " + acc.getBalance());
}
}
余额不能为负!
张三 余额: 1000.0
public(公开,任意访问)、private(私有,仅本类)、protected(受保护,同包+子类)、默认(同包)。字段一般用 private,方法一般用 public。构造方法
构造方法是一种特殊方法,在 new 对象时自动调用,用于初始化属性。特点:方法名与类名相同、无返回类型。
public class Person {
private String name;
private int age;
// 无参构造
public Person() {
this.name = "未知";
this.age = 0;
}
// 有参构造(重载)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println(name + ", " + age + " 岁");
}
public static void main(String[] args) {
Person p1 = new Person(); // 调用无参构造
Person p2 = new Person("李四", 25); // 调用有参构造
p1.show();
p2.show();
}
}
李四, 25 岁
构造方法链:this()
在一个构造方法中可以用 this(...) 调用另一个构造方法,避免重复代码:
public class Car {
String brand;
String color;
int year;
public Car(String brand) {
this(brand, "白色", 2024); // 调用三参构造
}
public Car(String brand, String color, int year) {
this.brand = brand;
this.color = color;
this.year = year;
}
public void info() {
System.out.println(year + "款 " + color + " " + brand);
}
public static void main(String[] args) {
Car c = new Car("丰田");
c.info();
}
}
标准 JavaBean 模式
实际开发中,实体类通常遵循 JavaBean 规范:私有字段 + 无参构造 + getter/setter:
public class Product {
private int id;
private String name;
private double price;
// 无参构造(必须)
public Product() {}
// 全参构造(推荐)
public Product(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
}
public class Dog {
private String name;
public Dog(String name) {
.name = ;
}
} 第 9 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
继承
继承让子类复用父类的代码——面向对象三大特性之二。
继承的基本语法
用 extends 关键字继承一个类。子类自动拥有父类的非 private 成员:
// 父类(基类)
class Animal {
String name;
public void eat() {
System.out.println(name + " 在吃东西");
}
public void sleep() {
System.out.println(name + " 在睡觉");
}
}
// 子类继承 Animal
class Dog extends Animal {
public void bark() {
System.out.println(name + " 汪汪汪!");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.name = "旺财";
d.eat(); // 继承自父类
d.bark(); // 自己的方法
}
}
旺财 汪汪汪!
Object;③ 子类不能继承父类的 private 成员和构造方法。方法重写(Override)
子类可以重新定义父类的方法,这叫 重写(Override)。要求方法名、参数列表与父类相同:
class Shape {
public double area() {
return 0;
}
}
class Circle extends Shape {
double radius;
public Circle(double r) { this.radius = r; }
@Override // 重写注解,帮助编译器检查
public double area() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
double width, height;
public Rectangle(double w, double h) {
this.width = w;
this.height = h;
}
@Override
public double area() {
return width * height;
}
}
public class Main {
public static void main(String[] args) {
Circle c = new Circle(5);
Rectangle r = new Rectangle(4, 6);
System.out.printf("圆面积: %.2f%n", c.area());
System.out.printf("矩形面积: %.2f%n", r.area());
}
}
矩形面积: 24.00
@Override 让编译器帮你检查是否正确重写。如果方法名拼错了,编译器会报错。强烈建议每次重写都加上。super 关键字
super 用于访问父类的成员,包括调用父类构造方法:
class Vehicle {
String brand;
public Vehicle(String brand) {
this.brand = brand;
System.out.println("Vehicle 构造");
}
public void run() {
System.out.println(brand + " 在行驶");
}
}
class Car extends Vehicle {
int wheels;
public Car(String brand, int wheels) {
super(brand); // 调用父类构造,必须放第一行
this.wheels = wheels;
System.out.println("Car 构造");
}
@Override
public void run() {
super.run(); // 调用父类方法
System.out.println(brand + " 有 " + wheels + " 个轮子");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("奔驰", 4);
car.run();
}
}
Car 构造
奔驰 在行驶
奔驰 有 4 个轮子
super(...) 必须是子类构造方法的第一条语句。final 关键字
| 修饰对象 | 效果 |
|---|---|
final class | 不能被继承(如 String) |
final 方法 | 不能被重写 |
final 变量 | 只能赋值一次(常量) |
class Cat Animal {
@Override
public void () {
System.out.println("喵喵喵");
}
} 第 10 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
多态与接口
多态是 OOP 最强大的特性,接口是灵活设计的利器。
多态:同一种调用,不同行为
多态的三个前提:① 继承/实现关系;② 方法重写;③ 父类引用指向子类对象。
class Animal {
public void speak() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
public void speak() { System.out.println("汪汪汪"); }
}
class Cat extends Animal {
public void speak() { System.out.println("喵喵喵"); }
}
public class Main {
// 父类引用作为参数——多态的威力
public static void makeSpeak(Animal a) {
a.speak(); // 运行时决定调用哪个子类的方法
}
public static void main(String[] args) {
// 父类引用指向子类对象
Animal a1 = new Dog(); // 向上转型
Animal a2 = new Cat();
makeSpeak(a1); // 汪汪汪
makeSpeak(a2); // 喵喵喵
}
}
喵喵喵
向下转型与 instanceof
把父类引用转回子类类型叫 向下转型,需先用 instanceof 检查:
Java 16+ 可以用 instanceof 模式匹配,一步到位:
Animal a = new Dog(); // 向上转型
// a.bark(); // 编译错误!Animal 没有 bark 方法
if (a instanceof Dog) {
Dog d = (Dog) a; // 向下转型
d.bark(); // 现在可以调用 Dog 特有方法
}
if (a instanceof Dog d) {
d.bark(); // d 已自动声明并转型
}
抽象类
用 abstract 修饰的类不能实例化,可以包含抽象方法(只有声明没有实现)。子类必须实现所有抽象方法:
abstract class Shape {
// 抽象方法:只有声明,没有实现
public abstract double area();
// 普通方法:抽象类中也可以有
public void display() {
System.out.printf("面积 = %.2f%n", area());
}
}
class Triangle extends Shape {
double base, height;
public Triangle(double b, double h) {
this.base = b; this.height = h;
}
@Override
public double area() {
return 0.5 * base * height;
}
}
public class Main {
public static void main(String[] args) {
Shape s = new Triangle(3, 4);
s.display();
}
}
接口
接口是一组方法规范的集合,用 interface 定义。类用 implements 实现接口,一个类可以 实现多个接口:
| 对比 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承 | 单继承(extends) | 多实现(implements) |
| 字段 | 任意类型 | 默认 public static final |
| 方法 | 可有普通方法 | 默认抽象(Java 8+ 可有 default) |
| 构造方法 | 有 | 无 |
| 用途 | 表示 "is-a" 关系 | 表示 "can-do" 能力 |
// 定义接口
interface Flyable {
void fly(); // 默认 public abstract
}
interface Swimmable {
void swim();
}
// 实现多个接口
class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("鸭子飞起来了");
}
public void swim() {
System.out.println("鸭子游起来了");
}
}
public class Main {
public static void main(String[] args) {
Duck d = new Duck();
d.fly();
d.swim();
// 接口引用也可以多态
Flyable f = new Duck();
f.fly();
}
}
鸭子游起来了
鸭子飞起来了
Comparable {
int (Comparable other);
} 第 11 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
异常处理
程序运行时难免出错,异常处理让程序优雅地应对意外。
异常体系
Java 异常的继承结构:
| 类别 | 基类 | 特点 | 示例 |
|---|---|---|---|
| 受检异常 | Exception | 编译器强制处理(try-catch 或 throws) | IOException, SQLException |
| 非受检异常 | RuntimeException | 编译器不强制处理 | NullPointerException, ArrayIndexOutOfBounds |
| 错误 | Error | 严重问题,不该捕获 | OutOfMemoryError, StackOverflowError |
try-catch-finally
public class TryCatch {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println(arr[5]); // 越界!
System.out.println("这行不会执行");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获到异常: " + e.getMessage());
} finally {
System.out.println("finally 总是执行");
}
System.out.println("程序继续运行");
}
}
finally 总是执行
程序继续运行
多重 catch
注意:catch 的顺序是 从子类到父类,否则编译错误(父类在前面会"吃掉"所有异常)。
try {
String s = null;
System.out.println(s.length()); // 空指针
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (Exception e) { // 父类异常放最后
System.out.println("其他异常");
}
throws 与 throw
| 对比 | throws | throw |
|---|---|---|
| 位置 | 方法签名后 | 方法体内 |
| 作用 | 声明可能抛出的异常 | 实际抛出异常对象 |
| 个数 | 可声明多个(逗号隔开) | 每次只能抛一个 |
public class ThrowsDemo {
// throws 声明方法可能抛出的异常
public static int divide(int a, int b) throws ArithmeticException {
if (b == 0) {
// throw 主动抛出异常
throw new ArithmeticException("除数不能为零");
}
return a / b;
}
public static void main(String[] args) {
try {
System.out.println(divide(10, 0));
} catch (ArithmeticException e) {
System.out.println("出错: " + e.getMessage());
}
}
}
自定义异常
// 继承 Exception 创建受检异常
class AgeInvalidException extends Exception {
public AgeInvalidException(String msg) {
super(msg);
}
}
public class CustomException {
public static void checkAge(int age) throws AgeInvalidException {
if (age < 0 || age > 150) {
throw new AgeInvalidException("年龄不合法: " + age);
}
System.out.println("年龄合法: " + age);
}
public static void main(String[] args) {
try {
checkAge(200);
} catch (AgeInvalidException e) {
System.out.println("捕获: " + e.getMessage());
}
}
}
public class Safe {
public static void main(String[] args) {
int[] a = {1, 2};
{
System.out.println(a[10]);
} (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界了");
}
}
} 第 12 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
集合框架
数组大小固定,集合框架提供了灵活的数据容器。
集合体系总览
| 接口 | 常用实现类 | 特点 |
|---|---|---|
List | ArrayList, LinkedList | 有序、可重复、有索引 |
Set | HashSet, TreeSet | 无序、不可重复 |
Map | HashMap, TreeMap | 键值对、键不可重复 |
ArrayList
ArrayList 是最常用的列表,底层是数组,可以动态扩容:
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建 ArrayList(泛型指定元素类型)
List<String> fruits = new ArrayList<>();
// 添加元素
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橘子");
// 获取元素
System.out.println("第二个: " + fruits.get(1));
// 修改元素
fruits.set(0, "红富士");
// 遍历
for (String f : fruits) {
System.out.println(f);
}
// 删除
fruits.remove("橘子");
System.out.println("删除后大小: " + fruits.size());
System.out.println("包含香蕉? " + fruits.contains("香蕉"));
}
}
红富士
香蕉
橘子
删除后大小: 2
包含香蕉? true
HashMap
HashMap 存储 键值对(key-value),通过键快速查找值:
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
// 创建 HashMap
Map<String, Integer> scores = new HashMap<>();
// 添加键值对
scores.put("张三", 90);
scores.put("李四", 85);
scores.put("王五", 95);
// 根据键取值
System.out.println("李四成绩: " + scores.get("李四"));
// 修改:put 相同的键会覆盖
scores.put("张三", 88);
System.out.println("张三新成绩: " + scores.get("张三"));
// 遍历
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 判断是否包含某个键
System.out.println("有王五? " + scores.containsKey("王五"));
System.out.println("大小: " + scores.size());
}
}
张三新成绩: 88
李四: 85
张三: 88
王五: 95
有王五? true
大小: 3
LinkedHashMap;需要按键排序,用 TreeMap。HashSet
HashSet 用于存储 不重复 的元素,常用于去重:
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Python");
set.add("Java"); // 重复,不会被添加
set.add("C++");
System.out.println("大小: " + set.size()); // 3
for (String s : set) {
System.out.println(s);
}
// 删除
set.remove("C++");
System.out.println("删除后包含 C++? " + set.contains("C++"));
}
}
Java
C++
Python
删除后包含 C++? false
实战:统计单词出现次数
import java.util.HashMap;
import java.util.Map;
public class WordCount {
public static void main(String[] args) {
String[] words = {"java", "python", "java", "c++", "python", "java"};
Map<String, Integer> count = new HashMap<>();
for (String w : words) {
// getOrDefault: 键不存在时返回默认值
count.put(w, count.getOrDefault(w, 0) + 1);
}
for (Map.Entry<String, Integer> e : count.entrySet()) {
System.out.println(e.getKey() + " 出现 " + e.getValue() + " 次");
}
}
}
c++ 出现 1 次
java 出现 3 次
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<> list = new ArrayList<>();
list.("hello");
System.out.println(list.get(0));
}
} 第 13 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。
泛型与综合实战
泛型让代码更安全、更通用。今天用一个综合项目串联前 13 天的所有知识。
为什么需要泛型
没有泛型时,集合可以存任意类型(Object),取出时需要强制转换,容易出错:
import java.util.ArrayList;
import java.util.List;
public class NoGeneric {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add(123); // 可以混入不同类型!
String s = (String) list.get(1); // 运行时 ClassCastException
System.out.println(s);
}
}
泛型基础
泛型用尖括号 <T> 指定类型参数,编译期就能发现类型错误:
import java.util.ArrayList;
import java.util.List;
public class Generic {
public static void main(String[] args) {
// 指定集合只能存 String
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // 编译错误!编译期就拦截
String s = list.get(0); // 不需要强制转换
System.out.println(s);
}
}
自定义泛型类
// 泛型类:T 是类型参数
public class Box<T> {
private T item;
public void put(T item) {
this.item = item;
}
public T get() {
return item;
}
public static void main(String[] args) {
Box<String> strBox = new Box<>();
strBox.put("礼物");
System.out.println(strBox.get());
Box<Integer> intBox = new Box<>();
intBox.put(42);
System.out.println(intBox.get());
}
}
42
泛型方法
public class GenericMethod {
// 泛型方法:<T> 声明类型参数
public static <T> void printArray(T[] arr) {
for (T item : arr) {
System.out.print(item + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] nums = {1, 2, 3};
String[] strs = {"A", "B", "C"};
printArray(nums); // 自动推断 T = Integer
printArray(strs); // 自动推断 T = String
}
}
A B C
通配符
| 写法 | 含义 | 用法 |
|---|---|---|
<?> | 任意类型(无界通配符) | 只读,不能添加 |
<? extends Number> | Number 及其子类(上界) | 读取,不能写入 |
<? super Integer> | Integer 及其父类(下界) | 写入,读取为 Object |
综合实战:学生管理系统
这个项目综合运用了 封装、继承、集合、泛型、异常处理 等所有知识点:
import java.util.ArrayList;
import java.util.List;
// 学生类(封装)
class Student {
private int id;
private String name;
private double score;
public Student(int id, String name, double score) {
this.id = id;
this.name = name;
this.score = score;
}
public int getId() { return id; }
public String getName() { return name; }
public double getScore() { return score; }
public String toString() {
return String.format("学号:%d 姓名:%s 成绩:%.1f", id, name, score);
}
}
// 学生管理系统
class StudentManager {
private List<Student> students = new ArrayList<>();
// 添加学生
public void add(Student s) {
students.add(s);
System.out.println("添加成功: " + s.getName());
}
// 按学号删除
public void removeById(int id) {
students.removeIf(s -> s.getId() == id);
System.out.println("已删除学号 " + id);
}
// 查询所有
public void listAll() {
if (students.isEmpty()) {
System.out.println("暂无学生");
return;
}
for (Student s : students) {
System.out.println(s);
}
}
// 统计平均分
public double average() {
if (students.isEmpty()) return 0;
double sum = 0;
for (Student s : students) sum += s.getScore();
return sum / students.size();
}
}
public class StudentSystem {
public static void main(String[] args) {
StudentManager mgr = new StudentManager();
mgr.add(new Student(1, "张三", 90));
mgr.add(new Student(2, "李四", 85));
mgr.add(new Student(3, "王五", 78));
System.out.println("\n--- 全部学生 ---");
mgr.listAll();
System.out.println("\n--- 删除李四 ---");
mgr.removeById(2);
System.out.println("\n--- 删除后 ---");
mgr.listAll();
System.out.printf("%n平均分: %.1f%n", mgr.average());
}
}
添加成功: 李四
添加成功: 王五
--- 全部学生 ---
学号:1 姓名:张三 成绩:90.0
学号:2 姓名:李四 成绩:85.0
学号:3 姓名:王五 成绩:78.0
--- 删除李四 ---
已删除学号 2
--- 删除后 ---
学号:1 姓名:张三 成绩:90.0
学号:3 姓名:王五 成绩:78.0
平均分: 84.0
下一步学习路线
| 方向 | 内容 |
|---|---|
| 进阶语法 | 枚举、注解、Lambda 表达式、Stream API |
| IO 与文件 | 字节流/字符流、File、NIO |
| 多线程 | Thread、Runnable、并发包(concurrent) |
| 网络编程 | Socket、HTTP、TCP/UDP |
| 数据库 | JDBC、连接池、SQL |
| 框架 | Spring Boot、MyBatis |
public class Box<> {
private T item;
public void put( item) { this.item = item; }
public T get() { return item; }
} 第 14 天完成!
恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。