跳到主内容
Zhimalab
14 天快速入门 · 互动教程
0%
首页 课程概览
☕ 14 天 · 互动 · 零基础友好

用两周时间,真正入门 Java

这不是一本枯燥的教科书。这是一份精心设计的互动学习路径——每课都有可运行示例、即时反馈测验和动手练习,帮你从零基础到达大二计算机专业的 Java 水平。

14
天课程
40+
代码示例
28
互动测验
14
动手练习
📖

渐进式学习

从变量到泛型,14 天循序渐进,每天聚焦一个核心主题。

即时反馈

每个知识点配有代码示例和输出预览,学完立刻测验巩固。

✏️

动手练习

每课结束有填空编程练习,在实战中检验你的理解。

第 1 天 · 入门

Java 简介与第一个程序

了解 Java 的特点,写出你的第一行代码。

Java 是什么

Java 是一门 面向对象跨平台 的编程语言。它的核心理念是 "一次编写,到处运行"(Write Once, Run Anywhere)——编译后的字节码可以在任何装有 JVM(Java 虚拟机)的平台上运行。

💡
为什么学 Java? Java 广泛应用于企业级后端开发、Android 应用、大数据处理(Hadoop/Spark)等领域,是全球使用最广泛的语言之一。掌握 Java 是计算机专业学生的必备技能。

第一个程序:Hello World

每个程序员的起点。来看 Java 版的 Hello World:

HelloWorld.java
    public class HelloWorld {
    // 程序的入口,main 方法
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
    
  
输出结果
Hello, World!
💡
逐行解读:
public class HelloWorld — 声明一个公开类,类名必须与文件名一致
public static void main(String[] args) — 程序入口方法,固定写法
System.out.println(...) — 向控制台输出一行文字
// — 单行注释,编译器会忽略

编译与运行

Java 是 编译型 语言。源代码 .java 文件需要先编译成 .class 字节码,再由 JVM 执行:

terminal
    # 1. 编译源文件
javac HelloWorld.java

# 2. 运行字节码
java HelloWorld
    
  
输出结果
Hello, World!

输出方法对比

方法效果示例
System.out.println()输出后换行println("Hi") → Hi
System.out.print()输出不换行print("Hi") → Hi
System.out.printf()格式化输出printf("%.2f", 3.14)
?
小测验
Hello World 程序中,程序的入口方法名是什么?
A start
B main
C run
D init
?
小测验
Java 源代码文件编译后生成什么文件?
A .class 字节码文件
B .exe 可执行文件
C .js 文件
D .py 文件
动手练习
补全代码,让程序输出 "Java 很有趣!"
public class MyFirst {
    public static void main(String[] args) {
        System.out.("Java 很有趣!");
    }
 }

第 1 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 2 天 · 基础

变量与数据类型

变量是存储数据的容器,数据类型决定了容器的大小和用途。

基本数据类型

Java 是 强类型语言,每个变量必须先声明类型。Java 有 8 种基本类型:

类型关键字大小范围 / 示例
整数byte1 字节-128 ~ 127
整数short2 字节-32768 ~ 32767
整数int4 字节约 ±21 亿(最常用)
整数long8 字节很大,加 L 后缀
浮点float4 字节f 后缀
浮点double8 字节默认浮点类型
字符char2 字节单个 Unicode 字符
布尔boolean1 位true / false
Types.java
    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);
    }
}
    
  
输出结果
年龄: 20
价格: 19.99
等级: A
是学生: true
⚠️
注意后缀! long 值要加 Lfloat 值要加 f,否则编译报错。字符用 单引号 'A',字符串用 双引号 "Hi"

变量命名规则

Java 变量命名遵循 驼峰命名法(camelCase):

规则合法示例非法示例
字母/数字/下划线/$,不能数字开头userName2name
不能是关键字myClassclass
区分大小写age ≠ Age
驼峰风格,见名知意studentCountsc

类型转换

小类型可以自动转大类型(隐式),大转小需要强制转换(可能丢失精度):

Cast.java
    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);
    }
}
    
  
输出结果
自动转换: 100.0
强制转换: 3

String 字符串

String 不是基本类型,而是 引用类型(类),但使用频率极高:

StringDemo.java
    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
?
小测验
以下哪个是合法的变量名?
A 2ndPlace
B class
C _score
D public
?
小测验
double 类型的变量占多少字节?
A 4 字节
B 8 字节
C 2 字节
D 16 字节
动手练习
声明一个整型变量 count 赋值为 100,一个布尔变量 active 赋值为 true
public class Vars {
    public static void main(String[] args) {
         count = 100;
         active = true;
        System.out.println(count + " " + active);
    }
 }

第 2 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 3 天 · 基础

运算符与表达式

运算符是操作数据的符号,掌握它们是编程的基本功。

算术运算符

Arithmetic.java
    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
    }
}
    
  
输出结果
加: 22
减: 12
乘: 85
除: 3
取余: 2
⚠️
整数除法陷阱: 17 / 5 结果是 3 而不是 3.4!两个整数相除,结果还是整数(直接截断)。若想得到小数,需转为 double(double)a / b

自增自减

IncDec.java
    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,后使用)
    }
}
    
  
输出结果
5
6
7
💡

i++ 先用后加,++i 先加后用。实际开发中为避免混淆,建议单独写 i++;

关系与逻辑运算符

类别运算符含义
关系==等于
!=不等于
> <大于 / 小于
>= <=大于等于 / 小于等于
返回 boolean 值
== 比较基本类型比值,比较对象比地址
逻辑&&与(短路):两边都 true 才 true
||或(短路):一边 true 就 true
!非:取反
Logic.java
    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
可以休息: true
短路避免了除零错误
💡
短路求值: && 如果左边为 false,右边不执行;|| 如果左边为 true,右边不执行。这可以用来避免空指针等错误。

赋值与三元运算符

Assign.java
    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);
    }
}
    
  
输出结果
+= 后: 15
未成年
?
小测验
17 % 5 的结果是?
A 2
B 3
C 3.4
D 12
?
小测验
int a = 5; System.out.println(a++ + ++a); 输出?
A 11
B 12
C 13
D 10
动手练习
用三元运算符判断成绩是否及格(>=60)
public class Ternary {
    public static void main(String[] args) {
        int score = 85;
        String result = score  60 ? "及格" : "不及格";
        System.out.println(result);
    }
 }

第 3 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 4 天 · 流程控制

条件语句

让程序学会"做选择"——根据不同条件执行不同代码。

if-else 语句

条件从上到下依次判断,一旦匹配就执行对应分支,不再继续判断后面的条件。

IfElse.java
    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

Nested.java
    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 更清晰:

Switch.java
    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! 如果漏掉 break,程序会"穿透"(fall-through)继续执行后面的 case,直到遇到 break 或 switch 结束。这通常是个 bug。

switch 新语法(Java 14+)

Java 14 引入了增强 switch,使用箭头语法,自动不穿透:

SwitchNew.java
    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);
    }
}
    
  
输出结果
星期三
?
小测验
if-else 语句中,else 匹配哪个 if?
A 最近且未配对的 if
B 最远的 if
C 第一个 if
D 所有 if
?
小测验
switch 语句中漏掉 break 会怎样?
A 编译错误
B 继续执行后面的 case(穿透)
C 跳到 default
D 程序崩溃
动手练习
补全 switch 语句,当 color 为 1 时输出"红色"
public class ColorSwitch {
    public static void main(String[] args) {
        int color = 1;
        switch (color) {
             1:
                System.out.println("红色");
                ;
            default:
                System.out.println("未知");
        }
    }
 }

第 4 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 5 天 · 流程控制

循环

让程序自动重复执行,告别复制粘贴。

for 循环

当你知道循环次数时,for 循环是首选:

for (初始化; 条件; 更新)——三个部分用分号隔开。条件为 false 时循环结束。

ForLoop.java
    public class ForLoop {
    public static void main(String[] args) {
        // 打印 1 到 5
        for (int i = 1; i <= 5; i++) {
            System.out.println("第 " + i + " 次");
        }
    }
}
    
  
输出结果
第 1 次
第 2 次
第 3 次
第 4 次
第 5 次

while 循环

当不确定循环次数,只关心条件是否成立时用 while

WhileLoop.java
    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 + " 次");
    }
}
    
  
输出结果
循环了 11 次

do-while 循环

do-while 先执行一次再判断条件,保证 至少执行一次

DoWhile.java
    public class DoWhile {
    public static void main(String[] args) {
        int i = 10;
        do {
            System.out.println("i = " + i);
            i++;
        } while (i < 5);  // 条件为 false,但已经执行了一次
    }
}
    
  
输出结果
i = 10

break 与 continue

BreakContinue.java
    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);
        }
    }
}
    
  
输出结果
=== break 示例 ===
1
2
3
4
=== continue 示例 ===
1
3
5
7
9

嵌套循环:九九乘法表

Multiplication.java
    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×1=1
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
💡
printf 格式化: %d 整数,%-4d 左对齐占 4 位宽度,%n 换行。这是经典面试题,务必理解嵌套循环的执行顺序。
?
小测验
do-while 循环至少执行几次?
A 0 次
B 1 次
C 2 次
D 取决于条件
?
小测验
continue 语句的作用是?
A 结束整个循环
B 结束当前方法
C 跳过本次循环,进入下一次
D 跳到循环开头重新执行
动手练习
用 for 循环计算 1+2+3+...+100 的和
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 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 6 天 · 结构化

方法

方法是组织代码的基本单元——把重复逻辑封装起来,调用时只需一个名字。

方法的定义与调用

MethodBasic.java
    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("张三");              // 调用无返回值的方法
    }
}
    
  
输出结果
3 + 5 = 8
你好, 张三!
💡
方法四要素: 返回类型(int/void 等)、方法名参数列表(可以为空)、方法体return 返回结果并结束方法;void 方法不需要 return。

方法重载

同一个类中可以有多个 同名 方法,只要 参数列表不同(个数、类型、顺序)。编译器根据调用参数自动选择:

Overload.java
    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));    // 调用第三个
    }
}
    
  
输出结果
3
6
4.0

值传递

Java 方法参数是 值传递——方法内修改基本类型参数不影响外部:

PassByValue.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
    }
}
    
  
输出结果
n = 10

变量作用域

变量只在声明它的 {} 内有效:

Scope.java
    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 在这里不可见
    }
}
    
  
输出结果
30

递归

方法调用自己就是递归。必须有 终止条件,否则栈溢出。经典例子——阶乘:

Recursion.java
    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
    }
}
    
  
输出结果
5! = 120
⚠️
递归三要素: ① 大问题能分解为同类小问题;② 有终止条件(base case);③ 每次递归问题规模缩小。否则会无限递归导致 StackOverflowError
?
小测验
方法重载的要求是?
A 方法名相同,返回类型不同
B 方法名相同,参数列表不同
C 方法名不同,参数相同
D 参数名不同
?
小测验
Java 方法参数的传递方式是?
A 引用传递
B 值传递
C 指针传递
D 都可以
动手练习
定义一个方法 max,返回两个整数中的较大值
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 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 7 天 · 数据结构

数组

数组是存储多个同类型数据的容器——最基础的数据结构。

创建与访问

ArrayBasic.java
    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]);
    }
}
    
  
输出结果
第一个: 90
第三个: 78
长度: 5
修改后: 95
⚠️
下标从 0 开始! 长度为 5 的数组,下标是 0~4。访问 scores[5] 会抛出 ArrayIndexOutOfBoundsException(数组越界异常)。

其他创建方式

ArrayCreate.java
    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);
    }
}
    
  
输出结果
10 0
3

遍历数组

for-each 更简洁,但不方便获取下标,也不能修改数组元素。

ArrayLoop.java
    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: 10 20 30 40 50
for-each: 10 20 30 40 50

实战:求最大值

ArrayMax.java
    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);
    }
}
    
  
输出结果
最大值: 9

二维数组

TwoD.java
    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();
        }
    }
}
    
  
输出结果
1 2 3
4 5 6
7 8 9
?
小测验
int[] arr = new int[5]; 中 arr[3] 的默认值是?
A 0
B null
C 未定义
D 5
?
小测验
长度为 5 的数组,最后一个元素的下标是?
A 5
B 4
C 0
D 6
动手练习
用 for-each 遍历数组并求和
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 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 8 天 · OOP

面向对象基础:类与对象

面向对象编程(OOP)是 Java 的灵魂——用"类"描述事物,用"对象"操作实例。

类与对象的概念

💡
类(Class) = 模板 / 图纸,定义了属性(字段)和行为(方法)
对象(Object) = 类的实例,通过 new 创建
举例:Dog 是类,我家那只叫"旺财"的狗是对象。

定义第一个类

Student.java
    // 学生类
public class Student {
    // 属性(字段 / 成员变量)
    String name;
    int age;
    double score;

    // 方法(行为)
    public void study() {
        System.out.println(name + " 正在学习");
    }

    public void introduce() {
        System.out.println("我叫 " + name + ",今年 " + age + " 岁");
    }
}
    
  
输出结果
// 这是一个类定义,不需要输出
Main.java
    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();
    }
}
    
  
输出结果
我叫 张三,今年 20 岁
张三 正在学习
我叫 李四,今年 21 岁

成员变量 vs 局部变量

对比项成员变量(字段)局部变量
位置类中、方法外方法内
默认值有(int→0, 引用→null)无,必须初始化
作用域整个类方法内
存储堆(随对象)栈(随方法)

this 关键字

this 指向 当前对象,常用于区分成员变量和参数同名的情况:

ThisDemo.java
    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();
    }
}
    
  
输出结果
《Java入门》 价格: 59.9

static 修饰符

static 修饰的成员属于 类本身,不需要创建对象就能用:

StaticDemo.java
    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 + " 个对象");
    }
}
    
  
输出结果
3 的平方: 9
创建了 2 个对象
?
小测验
创建对象使用哪个关键字?
A create
B new
C make
D class
?
小测验
this 关键字代表什么?
A 父类对象
B 当前对象
C 类本身
D 静态成员
?
小测验
static 修饰的方法有什么特点?
A 只能被对象调用
B 属于类,可直接用类名调用
C 不能有返回值
D 不能有参数
动手练习
创建一个 Student 对象并设置 name 属性
public class Test {
    public static void main(String[] args) {
         s = new Student();
        s. = "王五";
    }
 }

第 8 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 9 天 · OOP

封装与构造方法

封装是面向对象三大特性之一,构造方法是创建对象的入口。

封装:隐藏细节,暴露接口

封装的核心:把属性设为 private,通过 public 的 getter/setter 控制访问。好处是可以在 setter 中加入 数据校验

Encapsulation.java
    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;
    }
}
    
  
输出结果
// 类定义,不需要输出
AccountTest.java
    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
余额不能为负!
张三 余额: 1000.0
💡
访问修饰符: public(公开,任意访问)、private(私有,仅本类)、protected(受保护,同包+子类)、默认(同包)。字段一般用 private,方法一般用 public。

构造方法

构造方法是一种特殊方法,在 new 对象时自动调用,用于初始化属性。特点:方法名与类名相同、无返回类型

Constructor.java
    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();
    }
}
    
  
输出结果
未知, 0 岁
李四, 25 岁
⚠️
注意: 如果你没有写任何构造方法,编译器会自动生成一个无参构造。但一旦你写了有参构造,编译器就不再自动生成无参构造了——如果需要无参构造,必须手动写。

构造方法链:this()

在一个构造方法中可以用 this(...) 调用另一个构造方法,避免重复代码:

ThisCall.java
    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();
    }
}
    
  
输出结果
2024款 白色 丰田

标准 JavaBean 模式

实际开发中,实体类通常遵循 JavaBean 规范:私有字段 + 无参构造 + getter/setter:

JavaBean.java
    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; }
}
    
  
输出结果
// 标准 JavaBean,不需要输出
?
小测验
封装的核心做法是?
A 所有属性用 public
B 属性用 private,通过 getter/setter 访问
C 所有方法用 private
D 不使用属性
?
小测验
构造方法的特点是?
A 有返回类型
B 方法名与类名相同,无返回类型
C 可以有任意名字
D 只能有一个
?
小测验
如果只写了有参构造,还能用 new Person() 无参创建对象吗?
A 能,编译器自动生成
B 不能,需要手动写无参构造
C 能,Java 自动处理
D 不确定
动手练习
补全构造方法,初始化 name 属性
public class Dog {
    private String name;

    public Dog(String name) {
        .name = ;
    }
 }

第 9 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 10 天 · OOP

继承

继承让子类复用父类的代码——面向对象三大特性之二。

继承的基本语法

extends 关键字继承一个类。子类自动拥有父类的非 private 成员:

Inheritance.java
    // 父类(基类)
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();   // 自己的方法
    }
}
    
  
输出结果
旺财 在吃东西
旺财 汪汪汪!
💡
继承要点: ① Java 是 单继承,一个类只能 extends 一个父类;② 所有类的根父类是 Object;③ 子类不能继承父类的 private 成员和构造方法。

方法重写(Override)

子类可以重新定义父类的方法,这叫 重写(Override)。要求方法名、参数列表与父类相同:

Override.java
    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());
    }
}
    
  
输出结果
圆面积: 78.54
矩形面积: 24.00
💡
@Override 注解: 加上 @Override 让编译器帮你检查是否正确重写。如果方法名拼错了,编译器会报错。强烈建议每次重写都加上。

super 关键字

super 用于访问父类的成员,包括调用父类构造方法:

Super.java
    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();
    }
}
    
  
输出结果
Vehicle 构造
Car 构造
奔驰 在行驶
奔驰 有 4 个轮子
⚠️
构造顺序: 创建子类对象时,先执行父类构造方法,再执行子类构造方法。super(...) 必须是子类构造方法的第一条语句。

final 关键字

修饰对象效果
final class不能被继承(如 String)
final 方法不能被重写
final 变量只能赋值一次(常量)
?
小测验
Java 一个类可以继承几个父类?
A 1 个
B 2 个
C 多个
D 不限
?
小测验
方法重写(Override)的要求是?
A 方法名相同,参数列表相同
B 返回类型可以不同
C 访问权限可以更严格
D 参数列表可以不同
?
小测验
创建子类对象时,构造方法的执行顺序是?
A 先子类后父类
B 先父类后子类
C 只执行子类
D 随机执行
动手练习
让 Cat 类继承 Animal 类,并重写 sound 方法
class Cat  Animal {
    @Override
    public void () {
        System.out.println("喵喵喵");
    }
 }

第 10 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 11 天 · OOP

多态与接口

多态是 OOP 最强大的特性,接口是灵活设计的利器。

多态:同一种调用,不同行为

多态的三个前提:① 继承/实现关系;② 方法重写;③ 父类引用指向子类对象

Polymorphism.java
    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 模式匹配,一步到位:

Downcast.java
    Animal a = new Dog();  // 向上转型
// a.bark();  // 编译错误!Animal 没有 bark 方法

if (a instanceof Dog) {
    Dog d = (Dog) a;  // 向下转型
    d.bark();         // 现在可以调用 Dog 特有方法
}
    
  
输出结果
// 示例片段
Pattern.java
    if (a instanceof Dog d) {
    d.bark();  // d 已自动声明并转型
}
    
  
输出结果
// 示例片段

抽象类

abstract 修饰的类不能实例化,可以包含抽象方法(只有声明没有实现)。子类必须实现所有抽象方法:

Abstract.java
    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();
    }
}
    
  
输出结果
面积 = 6.00

接口

接口是一组方法规范的集合,用 interface 定义。类用 implements 实现接口,一个类可以 实现多个接口

对比抽象类接口
关键字abstract classinterface
继承单继承(extends)多实现(implements)
字段任意类型默认 public static final
方法可有普通方法默认抽象(Java 8+ 可有 default)
构造方法
用途表示 "is-a" 关系表示 "can-do" 能力
Interface.java
    // 定义接口
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();
    }
}
    
  
输出结果
鸭子飞起来了
鸭子游起来了
鸭子飞起来了
?
小测验
多态的三个前提条件是?
A 继承、方法重写、父类引用指向子类对象
B 有接口、有抽象方法、有子类
C 有 final、有 static、有 this
D 有 public、有 private、有 protected
?
小测验
一个类可以实现几个接口?
A 1 个
B 2 个
C 多个
D 最多 3 个
?
小测验
接口中的字段默认是什么修饰符?
A private
B default
C public static final
D protected
动手练习
定义一个接口 Comparable,包含一个 compareTo 方法
 Comparable {
    int (Comparable other);
 }

第 11 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 12 天 · 健壮性

异常处理

程序运行时难免出错,异常处理让程序优雅地应对意外。

异常体系

Java 异常的继承结构:

类别基类特点示例
受检异常Exception编译器强制处理(try-catch 或 throws)IOException, SQLException
非受检异常RuntimeException编译器不强制处理NullPointerException, ArrayIndexOutOfBounds
错误Error严重问题,不该捕获OutOfMemoryError, StackOverflowError

try-catch-finally

TryCatch.java
    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("程序继续运行");
    }
}
    
  
输出结果
捕获到异常: Index 5 out of bounds for length 3
finally 总是执行
程序继续运行
💡
执行流程: try 中异常发生后的代码不再执行 → 跳到匹配的 catch → 无论是否异常,finally 都会执行 → 程序继续向下运行(不会崩溃)。

多重 catch

注意:catch 的顺序是 从子类到父类,否则编译错误(父类在前面会"吃掉"所有异常)。

MultiCatch.java
    try {
    String s = null;
    System.out.println(s.length());  // 空指针
} catch (NullPointerException e) {
    System.out.println("空指针异常");
} catch (Exception e) {       // 父类异常放最后
    System.out.println("其他异常");
}
    
  
输出结果
空指针异常

throws 与 throw

对比throwsthrow
位置方法签名后方法体内
作用声明可能抛出的异常实际抛出异常对象
个数可声明多个(逗号隔开)每次只能抛一个
Throws.java
    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());
        }
    }
}
    
  
输出结果
出错: 除数不能为零

自定义异常

CustomException.java
    // 继承 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());
        }
    }
}
    
  
输出结果
捕获: 年龄不合法: 200
?
小测验
finally 块什么时候执行?
A 只有异常发生时
B 只有没有异常时
C 无论是否异常都会执行
D catch 之后就不执行
?
小测验
NullPointerException 属于哪类异常?
A 受检异常(Checked)
B 非受检异常(RuntimeException)
C Error
D 都不是
?
小测验
throw 和 throws 的区别是?
A 没有区别
B throw 抛出异常,throws 声明异常
C throws 抛出异常,throw 声明异常
D throw 用于方法签名
动手练习
捕获数组越界异常并打印提示
public class Safe {
    public static void main(String[] args) {
        int[] a = {1, 2};
         {
            System.out.println(a[10]);
        }  (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界了");
        }
    }
 }

第 12 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 13 天 · 数据结构

集合框架

数组大小固定,集合框架提供了灵活的数据容器。

集合体系总览

接口常用实现类特点
ListArrayList, LinkedList有序、可重复、有索引
SetHashSet, TreeSet无序、不可重复
MapHashMap, TreeMap键值对、键不可重复

ArrayList

ArrayList 是最常用的列表,底层是数组,可以动态扩容:

ArrayListDemo.java
    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),通过键快速查找值:

HashMapDemo.java
    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());
    }
}
    
  
输出结果
李四成绩: 85
张三新成绩: 88
李四: 85
张三: 88
王五: 95
有王五? true
大小: 3
⚠️
HashMap 遍历顺序不确定! HashMap 不保证顺序。如果需要按插入顺序遍历,用 LinkedHashMap;需要按键排序,用 TreeMap

HashSet

HashSet 用于存储 不重复 的元素,常用于去重:

HashSetDemo.java
    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++"));
    }
}
    
  
输出结果
大小: 3
Java
C++
Python
删除后包含 C++? false

实战:统计单词出现次数

WordCount.java
    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() + " 次");
        }
    }
}
    
  
输出结果
python 出现 2 次
c++ 出现 1 次
java 出现 3 次
?
小测验
ArrayList 和数组相比的主要优势是?
A 速度更快
B 可以动态调整大小
C 可以存任意类型
D 占用内存更少
?
小测验
HashMap 中键(key)可以重复吗?
A 可以
B 不可以
C 可以但会被忽略
D 取决于实现
?
小测验
以下哪个集合不允许存储重复元素?
A ArrayList
B LinkedList
C HashSet
D 数组
动手练习
创建一个存储 String 的 ArrayList 并添加元素
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 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。

第 14 天 · 进阶

泛型与综合实战

泛型让代码更安全、更通用。今天用一个综合项目串联前 13 天的所有知识。

为什么需要泛型

没有泛型时,集合可以存任意类型(Object),取出时需要强制转换,容易出错:

NoGeneric.java
    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);
    }
}
    
  
输出结果
// 运行时抛出 ClassCastException

泛型基础

泛型用尖括号 <T> 指定类型参数,编译期就能发现类型错误:

Generic.java
    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);
    }
}
    
  
输出结果
hello

自定义泛型类

GenericClass.java
    // 泛型类: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

泛型方法

GenericMethod.java
    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
    }
}
    
  
输出结果
1 2 3
A B C

通配符

写法含义用法
<?>任意类型(无界通配符)只读,不能添加
<? extends Number>Number 及其子类(上界)读取,不能写入
<? super Integer>Integer 及其父类(下界)写入,读取为 Object

综合实战:学生管理系统

这个项目综合运用了 封装、继承、集合、泛型、异常处理 等所有知识点:

StudentSystem.java
    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
💡
恭喜! 这个管理系统用到了:类与对象(Student)、封装(private + getter)、构造方法集合(ArrayList)、泛型(List<Student>)、方法循环条件判断字符串格式化。你已经掌握了 Java 的核心基础!

下一步学习路线

方向内容
进阶语法枚举、注解、Lambda 表达式、Stream API
IO 与文件字节流/字符流、File、NIO
多线程Thread、Runnable、并发包(concurrent)
网络编程Socket、HTTP、TCP/UDP
数据库JDBC、连接池、SQL
框架Spring Boot、MyBatis
?
小测验
泛型的主要好处是?
A 让程序运行更快
B 编译期类型检查,避免运行时类型转换异常
C 减少代码行数
D 让代码更难读
?
小测验
List 中的 String 是什么?
A 一个变量名
B 泛型类型参数
C 一个方法
D 一个注解
?
小测验
综合项目中,StudentManager 使用了哪种集合存储学生?
A 数组
B HashMap
C ArrayList
D HashSet
动手练习
创建一个泛型类 Box,可以存储任意类型的物品
public class Box<> {
    private T item;
    public void put( item) { this.item = item; }
    public T get() { return item; }
 }

第 14 天完成!

恭喜完成本课内容。点击"完成本课"记录进度,然后继续下一天的学习。