大学计算机专业 · 程序设计核心课程(Java 8 / 11 / 17 / 21)

Java 程序设计:
面向对象思想到现代 Java 实战

本教程面向大学一年级学生,强调 Java 的灵魂——面向对象编程(OOP),并覆盖核心类库、集合、异常、Lambda/Stream、并发、JDBC 与工程实践。学完后你将能独立设计与实现中等规模的 Java 程序,并理解企业级开发的完整工具链。

1995Sun 公司发布 Java 1.0
JVM一次编译,到处运行
面向对象类 / 继承 / 多态 / 接口
LTSJava 8 / 11 / 17 / 21
↓ 向下滚动,按章节系统学习并完成课堂练习(第 07 章是 OOP 核心,请重点学习)
00

学习指南:为什么学、怎么学、学到什么

在动手写代码之前,先建立全局认知 —— Java 的本质、JVM 的运行原理,以及一条循序渐进的学习路线。

00.1 什么是 Java

Java 由 Sun Microsystems 公司于 1995 年发布,2010 年后由 Oracle 维护。它是一门面向对象、跨平台、强类型的通用编程语言。

Java 的核心特点

  • 面向对象:一切皆对象,OOP 是 Java 的灵魂;
  • 跨平台:一次编译,到处运行(Write Once, Run Anywhere);
  • 自动内存管理:GC 机制自动回收不再使用的内存;
  • 强类型:编译期检查大量类型错误;
  • 丰富的标准类库:JDK 自带数千个类,从集合到网络、并发;
  • 异常处理机制:把错误处理与正常逻辑分离。

Java 的应用领域

  • 企业级后端:Spring / Spring Boot 占据 Web 后端主流;
  • Android 应用(Java / Kotlin 历史主流);
  • 大数据生态:Hadoop / Kafka / Flink / Elasticsearch;
  • 服务端中间件:Tomcat / Netty / Dubbo;
  • 金融/电信系统嵌入式
  • 科学计算 / 教学:MIT、Stanford 等知名课程使用 Java。
Java 与 C/C++ 的关系:Java 借鉴了 C/C++ 的语法,但移除了指针、多继承等复杂特性,加入了垃圾回收和虚拟机,是更"安全友好"的选择。

一句话总结:Java 是工业界应用最广的 OOP 语言之一 —— 学好它,能打开从后端到大数据的整片职业空间。

00.2 Java 程序运行原理:JVM 是关键

Java 程序不是直接编译成机器码,而是先编译成 字节码(.class),再由 JVM 翻译成机器码执行。这就是"一次编译,到处运行"的秘密。

源代码 Hello.java javac 编译 → Hello.class JVM(虚拟机) Windows / macOS / Linux 机器码 CPU 执行 同一份 .class 可以在任何装了 JVM 的系统上运行
Java 程序运行流程:源代码 → 字节码 → JVM → 机器码

JDK / JRE / JVM 三者关系

名称含义关系
JVMJava 虚拟机,真正执行字节码JRE 的一部分
JREJava 运行时环境 = JVM + 标准库JDK 的一部分
JDKJava 开发工具包 = JRE + 编译器 + 工具开发人员安装

00.3 Java 标准演进时间轴

1996JDK 1.0 2004Java 5泛型 / 注解 2014Java 8Lambda / Stream 2018Java 11LTS · Http Client 2021Java 17LTS · sealed class 2023Java 21LTS · 虚拟线程 2025+Java 25
Java 版本演进时间轴(重点 LTS:8 / 11 / 17 / 21)
学习建议:教学环境推荐 Java 17(LTS,长期支持版本,免费可用到 2029)。企业里 Java 8 / 11 也很常见,但新项目建议直接上 17。

00.4 学习路线(四阶段)

Java 基础第 01-06 章
面向对象第 07 章 ⭐⭐⭐
核心类库第 08-13 章
现代 Java第 14-15 章
并发 / 网络第 16-17 章
数据 / 应用第 18-22 章
综合项目第 24 章
01

Java 入门:JDK 安装与第一个程序

搭好工具链,写下你的第一个 HelloWorld.java

1.1 安装 JDK

JDK 发行版特点推荐场景
Oracle JDK官方版本,商业使用需付费商业项目
OpenJDK (Adoptium)免费开源,主流选择教学 / 大多数项目(推荐)
Microsoft OpenJDK微软优化版Windows 用户
Amazon Corretto亚马逊维护云端部署
环境变量配置:设置 JAVA_HOME 指向 JDK 安装目录;将 %JAVA_HOME%\bin(Windows)或 $JAVA_HOME/bin(macOS/Linux)加入 PATH。验证:java -versionjavac -version

1.2 第一个 Java 程序

JavaHelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

逐行解释

  • public class HelloWorld:声明一个公共类,文件名必须为 HelloWorld.java
  • public static void main(String[] args)主方法,JVM 的入口;
    • public:JVM 可访问;
    • static:不需创建对象即可调用;
    • void:无返回值;
    • String[] args:命令行参数;
  • System.out.println(...):把字符串打印到标准输出,并换行。

1.3 编译与运行

shellterminal
# 1. 用编辑器写 HelloWorld.java(注意:文件名必须与 public class 同名)
$ vim HelloWorld.java

# 2. 编译:javac 把 .java 编译成 .class
$ javac HelloWorld.java
$ ls
HelloWorld.class  HelloWorld.java

# 3. 运行:java 启动 JVM,加载字节码并执行
$ java HelloWorld
Hello, World!

# 常见编译错误示例
$ java HelloWorld.java       # ❌ 不要加 .java 后缀
错误: 找不到或无法加载主类 HelloWorld.java

1.4 主流 IDE

IntelliJ IDEA(最推荐)

  • JetBrains 出品,Java 开发的"行业标准";
  • Community 版免费;
  • 智能补全、重构、调试一流;
  • Windows / macOS / Linux 全平台。

其他选择

  • Eclipse:老牌免费 IDE,企业项目常见;
  • VS Code + 扩展:轻量,需安装 Java Extension Pack;
  • NetBeans:Apache 维护,简洁易上手。
02

Java 基本语法:程序结构、注释与变量

Java 程序的基本组成:包、类、方法、语句 —— 每个 Java 程序至少包含一个类。

2.1 Java 程序结构

JavaMain.java
// package 语句(可选):类的所属包
package com.example.lesson;

// import 语句(可选):引入其他包的类
import java.util.Scanner;

// public class:每个 .java 文件最多一个 public 类,文件名必须与之相同
public class Main {
    // main 方法:程序入口
    public static void main(String[] args) {
        // 语句
        System.out.println("Hello");
    }
}

2.2 三种注释

Java注释
// 单行注释:到行尾结束

/*
 * 多行注释:跨越多行
 * 可用于临时屏蔽代码块
 */

/**
 * 文档注释(Javadoc):用于生成 API 文档
 * @param x 参数说明
 * @return 返回值说明
 */
public int foo(int x) { return x * 2; }

2.3 变量与常量

Javavariable.java
// 变量:值可以变
int    age   = 18;
double score = 95.5;
char   grade = 'A';
String name  = "Java";

// 常量:用 final 修饰(必须初始化)
final double PI  = 3.14159;
final int    MAX = 100;
// PI = 3.14;     // ❌ 编译错误:不能修改 final 变量

2.4 输入与输出

Javaio.java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 输出
        System.out.print("不换行");
        System.out.println("自动换行");
        System.out.printf("姓名=%s, 年龄=%d\n", "Alice", 20);

        // 输入(从标准输入读)
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入姓名:");
        String name = sc.nextLine();      // 读整行(含空格)

        System.out.print("请输入年龄:");
        int age = sc.nextInt();           // 读整数

        System.out.printf("你好 %s,你 %d 岁\n", name, age);
        sc.close();
    }
}
03

数据类型、变量与运算符

Java 是强类型语言:每个变量都有明确的类型。掌握 8 种基本类型,是后续学习的基石。

3.1 8 种基本数据类型

类型含义大小默认值示例
byte字节型10byte b = 100;
short短整型20short s = 1000;
int整型(最常用)40int n = 100000;
long长整型80Llong l = 100L;
float单精度浮点40.0ffloat f = 3.14f;
double双精度浮点(默认)80.0ddouble d = 3.14;
char字符(UTF-16 单元)2'\u0000'char c = 'A';
boolean布尔1falseboolean b = true;

3.2 引用类型 vs 基本类型

基本类型(8 种)

  • 存的是实际数值,在栈上分配;
  • 传递时是值拷贝;
  • 不能为 null(除非用包装类);
  • 大小固定,与机器无关。

引用类型

  • String、数组、对象;
  • 存的是堆上对象的地址
  • 可为 null;
  • Java 的"一切皆对象"指的就是这些。
Java基本 vs 引用
int     a = 10;                  // 基本类型,存 10
String s = "Hello";            // 引用类型,存的是字符串对象的地址

int     b = a;                  // 值拷贝:b 也等于 10
String t = s;                  // 地址拷贝:t 与 s 指向同一对象

3.3 类型转换

Javacast.java
// 1) 自动转换:小 → 大(安全)
int    i = 100;
double d = i;                  // 100 → 100.0

// 2) 强制转换:大 → 小(可能丢失精度)
double d2 = 3.99;
int    i2 = (int) d2;            // 3(直接截断,不四舍五入)

// 3) 字符串与数字互转
String s1 = String.valueOf(42);   // int → String
int    i3 = Integer.parseInt("123"); // String → int

3.4 运算符

与 C/C++ 类似,Java 的运算符包括:算术、关系、逻辑、位运算、赋值、三元等。这里列出 Java 特有注意点:

  • 整数除法10 / 3 = 3(不是 3.33);
  • 取模10 % 3 = 1
  • 关系运算== 对基本类型比较值,对引用类型比较地址;
  • 短路与/或&&||
  • 位运算& | ^ ~ << >> >>>>>> 是 Java 特有的无符号右移)。

课堂练习 · 第03章

1表达式 7 / 2 的结果是?
2下列 Java 常量声明中合法的是?
3(int) 3.99 的结果是?
04

流程控制:顺序、选择、循环

与 C/C++ 基本一致;Java 14+ 引入switch 表达式,让代码更简洁。

4.1 选择结构

Javaif.java
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else if (score >= 60) {
    grade = "C";
} else {
    grade = "D";
}

4.2 switch 与新版 switch 表达式 Java 14+

JavaSwitchDemo.java
// 传统 switch:每个 case 需要 break
switch (day) {
    case 1: print("Mon"); break;
    case 2: print("Tue"); break;
    default: print("Other");
}

// Java 14+ switch 表达式:返回值,更安全(无穿透)
String name = switch (day) {
    case 1 -> "Mon";
    case 2 -> "Tue";
    case 3, 4, 5 -> "Midweek";     // 多值合并
    default -> "Weekend";
};

4.3 循环结构 + 增强 for

Javaloop.java
// while
int i = 0;
while (i < 5) {
    System.out.println(i++);
}

// do-while:至少执行一次
int n;
do {
    System.out.print("输入正数:");
    n = sc.nextInt();
} while (n <= 0);

// 经典 for
for (int j = 0; j < 5; j++) {
    System.out.println(j);
}

// 增强 for(foreach):遍历数组或集合
int[] nums = {1, 2, 3, 4, 5};
for (int x : nums) {
    System.out.println(x);
}

4.4 综合练习:猜数字小游戏

JavaGuessNumber.java
import java.util.Scanner;
import java.util.Random;

public class GuessNumber {
    public static void main(String[] args) {
        Random r = new Random();
        int target = r.nextInt(100) + 1;     // 1~100
        Scanner sc = new Scanner(System.in);
        int guess, tries = 0;

        do {
            System.out.print("猜一个 1-100 的数:");
            guess = sc.nextInt();
            tries++;
            if (guess > target)      System.out.println("大了!");
            else if (guess < target) System.out.println("小了!");
        } while (guess != target);

        System.out.printf("用了 %d 次猜中!\n", tries);
    }
}

课堂练习 · 第04章

4关于 do-while 循环,下列说法正确的是?
5Java 14+ 的 switch 表达式说法错误的是?
6下列关于 break 与 continue 的说法,正确的是?
05

数组与字符串

数组是同类型元素的容器;String 是 Java 专门设计的不可变字符串类。

5.1 一维数组

JavaArrayDemo.java
// 三种声明方式(推荐第一种)
int[] scores = new int[5];            // 默认 0
int[] ages   = {18, 20, 22};    // 静态初始化
String[] names = new String[]{"A","B"};

// 访问 / 遍历
ages[0] = 25;
System.out.println(ages.length);    // 数组长度(属性,非方法)

for (int x : ages) {
    System.out.println(x);
}

// Arrays 工具类
import java.util.Arrays;
Arrays.sort(ages);                    // 升序排序
System.out.println(Arrays.toString(ages));

5.2 二维数组

Javamatrix.java
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();
}

5.3 String:不可变字符串 ⭐

String 是 Java 最重要的类之一:不可变(每次"修改"都创建新对象)。

JavaStringDemo.java
String s = "Hello, Java!";

// 常用方法
System.out.println(s.length());              // 12
System.out.println(s.charAt(0));           // 'H'
System.out.println(s.substring(0, 5));      // "Hello"
System.out.println(s.indexOf("Java"));      // 7
System.out.println(s.contains("Java"));     // true
System.out.println(s.startsWith("Hi"));    // false
System.out.println(s.replace("Java", "World")); // 新字符串对象
System.out.println(s.split(", ")[0]);      // "Hello"

// 字符串比较:必须用 equals
String a = "java", b = "JAVA";
a.equals(b);                 // false(区分大小写)
a.equalsIgnoreCase(b);      // true
// a == b  // ❌ 比较地址,可能为 false

5.4 StringBuilder:可变字符串

频繁拼接字符串时,用 StringBuilderString 高效得多。

JavaStringBuilderDemo.java
// ❌ 低效:每次 + 都创建新 String 对象
String s = "";
for (int i = 0; i < 1000; i++) s = s + i;

// ✔ 高效:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i);
String result = sb.toString();
06

方法与程序结构

方法 = C/C++ 中的"函数"。Java 中所有方法都必须属于类。

6.1 方法定义、参数与返回值

JavaMethodDemo.java
public class MethodDemo {
    // 无返回值
    public static void sayHi(String name) {
        System.out.println("Hi, " + name);
    }

    // 有返回值
    public static int add(int a, int b) {
        return a + b;
    }

    // 方法重载:同名不同参
    public static double add(double a, double b) {
        return a + b;
    }

    // 可变参数(必须是最后一个参数)
    public static int sum(int... nums) {
        int total = 0;
        for (int n : nums) total += n;
        return total;
    }

    public static void main(String[] args) {
        sayHi("Alice");
        System.out.println(add(3, 5));         // 8
        System.out.println(sum(1, 2, 3, 4, 5)); // 15
    }
}

6.2 值传递 vs 引用传递 ⭐

Java 只有值传递。对于引用类型,传的是"地址值的拷贝"——修改对象内容会影响原对象,但改变形参指向不影响原引用。

JavaPassDemo.java
// 1) 基本类型:完全拷贝,互不影响
static void swapInt(int a, int b) {
    int t = a; a = b; b = t;   // 仅在方法内交换
}

// 2) 引用类型:可以修改对象内容,但不能改变原引用指向
static void modify(int[] arr) {
    arr[0] = 999;            // ✔ 修改原数组元素
}

6.3 递归:阶乘与 Fibonacci

JavaRecursion.java
static long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

// Fibonacci:朴素递归效率极低,实际应用应改用迭代
static long fib(int n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

// 迭代版 Fibonacci:O(n) 时间,O(1) 空间
static long fibIter(int n) {
    if (n < 2) return n;
    long a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        long c = a + b; a = b; b = c;
    }
    return b;
}
07

面向对象编程 ⭐⭐⭐ —— Java 的灵魂

本章是整个 Java 课程的核心。请把更多时间放在这里——把每一节都动手写一遍。

7.1 面向对象思想:从"操作"到"建模"

面向过程(C 风格)

把程序看作一系列函数调用,数据与行为分离。

C面向过程
// 单独的数据 + 单独的函数
struct Student { char name[32]; int age; };

void setAge(struct Student* s, int a) {
    s->age = a;
}

面向对象(Java 风格)

把数据和行为封装进同一个"类",对象之间通过消息协作。

Java面向对象
// 数据 + 行为打包在一起
class Student {
    private String name;
    private int age;
    public void setAge(int a) { this.age = a; }
    public int  getAge()      { return age; }
}

OOP 三大特性:封装(数据隐藏 + 行为内聚) · 继承(代码复用 + 类型扩展) · 多态(同一消息,不同响应)。这三大特性是 OOP 思想的精髓,Java 在语法层面对它们的支持堪称典范。

7.2 定义类与创建对象 ⭐

类 = 对象的模板。Java 中所有代码都必须写在类里(除了少数注释和包声明)。

JavaStudent.java
public class Student {
    // 成员变量(字段)—— 推荐 private
    private String name;
    private int    age;
    private double score;

    // 构造方法(Constructor):用于初始化对象
    public Student() {
        this("Unknown", 0, 0.0);              // 调用其他构造
    }

    public Student(String name, int age) {
        this(name, age, 0.0);
    }

    public Student(String name, int age, double score) {
        this.name  = name;
        this.age   = age;
        this.score = score;
    }

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

    public void printInfo() {
        System.out.printf("%s, %d 岁, %.1f 分%n", name, age, score);
    }
}
Java使用 Student
public class Main {
    public static void main(String[] args) {
        // 创建对象:new 调用构造方法
        Student s1 = new Student();                          // 调用无参构造
        Student s2 = new Student("Alice", 20, 95.5);    // 调用带参构造

        s2.study();
        s2.printInfo();
    }
}
this 关键字:指向"当前对象"。可用于区分成员变量与形参同名(this.name = name),也可用于在一个构造方法中调用另一个(this(...))。

7.3 封装:访问控制 + Getter/Setter ⭐

封装的核心:把数据藏起来,把方法暴露出去。外部只能通过公开方法访问数据,从而保证数据有效性。

修饰符本类同包子类其他类
private
default
protected
public
JavaAccount.java — 封装示例
public class Account {
    private String owner;
    private double balance;       // 余额:必须保持非负

    public Account(String owner, double init) {
        this.owner   = owner;
        this.balance = init >= 0 ? init : 0;
    }

    // 通过 getter 读,通过 setter 受控地写
    public String getOwner()   { return owner; }
    public double getBalance() { return balance; }

    // 业务方法:体现"行为"
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("金额必须为正");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance) throw new IllegalArgumentException("余额不足");
        balance -= amount;
    }
}
经验法则:所有成员变量都用 private;必要时提供 public getter/setter;数据校验放在 setter 或业务方法中。

7.4 static 与 final:类级别的成员

JavaStaticDemo.java
public class Counter {
    public static int totalCount = 0;     // 类变量:所有对象共享
    public static final String VERSION = "1.0"; // 类常量

    private int id;

    public Counter() {
        totalCount++;
        this.id = totalCount;
    }

    // 静态方法:不能访问实例成员
    public static int getTotal() { return totalCount; }
}

// 使用
System.out.println(Counter.VERSION);       // 1.0  (类名.常量)
new Counter();
new Counter();
System.out.println(Counter.getTotal());  // 2
static 与 final 关键字:static 表示"类级别的"(属于类,不属于对象);final 表示"不可修改"。两者经常组合成"类常量" public static final

7.5 继承:extends ⭐

Java 只支持单继承(一个类只能 extends 一个父类)。多继承通过接口实现。

JavaInheritanceDemo.java
// 父类
public class Person {
    protected String name;
    protected int    age;

    public Person(String name, int age) {
        this.name = name; this.age = age;
    }

    public void sayHi() {
        System.out.println("Hi, 我叫 " + name);
    }
}

// 子类
public class Student extends Person {
    private String school;

    public Student(String name, int age, String school) {
        super(name, age);              // 调用父类构造(必须第一句)
        this.school = school;
    }

    @Override                    // 方法重写(覆盖父类)
    public void sayHi() {
        super.sayHi();               // 调用父类版本
        System.out.println("我在 " + school + " 上学");
    }
}
super 关键字:在子类中访问父类的成员(构造、方法、字段)。

7.6 方法重写(Override)与 @Override

子类可以重写父类的方法,提供自己的实现。@Override 注解让编译器帮你检查是否真的覆盖了父类方法。

JavaOverride.java
class Animal {
    public void sound() {
        System.out.println("...");
    }
}

class Dog extends Animal {
    @Override                     // 强烈推荐:编译器会校验
    public void sound() {
        System.out.println("汪汪!");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵!");
    }
}
重写规则:方法名、参数列表必须相同;返回类型可以是父类方法返回类型的子类(协变);访问权限不能更严格;不能重写 staticprivatefinal 方法。

7.7 多态:同一接口,不同实现 ⭐⭐⭐

多态是 OOP 的精髓:父类引用指向子类对象,调用方法时执行子类版本。

JavaPolymorphismDemo.java
public class PolymorphismDemo {
    public static void main(String[] args) {
        // 父类引用 → 子类对象
        Animal a1 = new Dog();
        Animal a2 = new Cat();

        makeSound(a1);   // 汪汪!
        makeSound(a2);   // 喵喵!

        // 多态数组:处理一组"形状"
        Animal[] animals = {new Dog(), new Cat(), new Animal()};
        for (Animal a : animals) a.sound();
    }

    // 接受父类引用:实际执行子类的方法
    static void makeSound(Animal a) {
        a.sound();
    }
}
多态三要素:① 继承 ② 方法重写 ③ 父类引用指向子类对象。这三者缺一不可。

7.8 抽象类:abstract ⭐

包含抽象方法(没有实现的方法)的类必须用 abstract 修饰,不能直接实例化。

JavaAbstractShape.java
public abstract class Shape {
    protected String color;

    public Shape(String color) { this.color = color; }

    // 抽象方法:只有签名,没有实现
    public abstract double area();
    public abstract double perimeter();

    // 普通方法可以保留
    public String getColor() { return color; }
}

public class Circle extends Shape {
    private double r;
    public Circle(String color, double r) {
        super(color); this.r = r;
    }
    @Override public double area()      { return 3.14159 * r * r; }
    @Override public double perimeter() { return 2 * 3.14159 * r; }
}

// Shape s = new Shape();   // ❌ 不能实例化抽象类
Shape s = new Circle("Red", 2.0);    // ✔ 多态

7.9 接口:interface ⭐⭐

接口定义"做什么",类实现"怎么做"。一个类可以实现多个接口(弥补 Java 单继承)。

JavaInterfaceDemo.java
// 接口:只声明"做什么",不写"怎么做"
public interface Flyable {
    void fly();                  // 默认 public abstract

    // Java 8+ 默认方法(有实现)
    default void land() {
        System.out.println("着陆");
    }

    // Java 8+ 静态方法
    static boolean canFly(Flyable f) {
        return f != null;
    }
}

public interface Swimmable {
    void swim();
}

// 类实现多个接口(弥补 Java 单继承)
public class Duck extends Animal implements Flyable, Swimmable {
    @Override public void fly()  { System.out.println("鸭子飞"); }
    @Override public void swim() { System.out.println("鸭子游"); }
}
抽象类 vs 接口:抽象类是"is-a"(是什么),共享代码;接口是"can-do"(能做什么),定义能力。Java 中优先用接口。

7.10 Object 类:所有类的祖宗

Java 中所有类都直接或间接继承自 java.lang.Object。掌握它的三个核心方法:

JavaObjectMethods.java
@Override
public String toString() {              // 对象的字符串表示
    return "Student{name=" + name + ", age=" + age + "}";
}

@Override
public boolean equals(Object o) {        // 内容相等
    if (this == o) return true;
    if (!(o instanceof Student)) return false;
    Student s = (Student) o;
    return 18 == s.age && name.equals(s.name);
}

@Override
public int hashCode() {               // 哈希码(与 equals 配对)
    return Objects.hash(name, age);
}
铁律:重写 equals 必须同时重写 hashCode —— 否则对象在 HashMap/HashSet 中行为异常。

7.11 枚举 enum 与 Record ⭐ Java 5 Java 16

JavaEnumDemo.java
// 枚举:一组固定常量的类型安全写法
public enum Color {
    RED, GREEN, BLUE;

    public String rgb() { return this.name(); }
}

// 使用
for (Color c : Color.values()) {
    System.out.println(c + " = " + c.rgb());
}

// Record(Java 16+):不可变数据类,自动生成 equals/hashCode/toString
public record Point(int x, int y) { }
Point p = new Point(3, 4);   // p.toString() = "Point[x=3, y=4]"

课堂练习 · 第07章 OOP(核心)

7关于 Java 继承,下列说法正确的是?
8多态的必要条件不包括?
9关于抽象类(abstract class),下列说法错误的是?
10关于 interface,下列说法错误的是?
11封装的核心原则是?
08

Java 核心类库:Object / 包装类 / Math

JDK 自带的基础类 —— 不学这些,你的程序会写得笨拙。

8.1 包装类与自动装箱 / 拆箱

8 种基本类型各有对应的包装类,用于把基本类型当作对象使用(如放入集合)。

基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
JavaWrapperDemo.java
// 自动装箱:int → Integer
Integer a = 10;             // 等价于 Integer.valueOf(10)

// 自动拆箱:Integer → int
int b = a;                  // 等价于 a.intValue()

// 字符串与整数互转
String s = String.valueOf(42);
int    n = Integer.parseInt("123");

// BigDecimal:金融计算(避免 double 精度问题)
BigDecimal money = new BigDecimal("0.1");
BigDecimal total = money.add(money).add(money);  // 0.3(精确)

8.2 Math、Random、System 常用方法

JavaMathDemo.java
// Math
Math.abs(-5);           // 5
Math.max(3, 7);       // 7
Math.sqrt(16);        // 4.0
Math.pow(2, 10);      // 1024.0
Math.random();        // [0, 1) 随机数

// Random
Random r = new Random();
int x = r.nextInt(100);  // [0, 100)
double y = r.nextDouble();

// System
System.out.println("标准输出");
System.err.println("标准错误");
System.out.printf("当前时间:%tT%n", System.currentTimeMillis());
// System.exit(0);   // 退出 JVM
09

异常处理:try / catch / finally / throw

Java 用异常对象把错误处理从主逻辑中分离,让程序更健壮。

9.1 异常类层次结构

Throwable Error 系统级错误 不应捕获 Exception RuntimeException 非受检异常 Checked Exception 受检异常:必须处理
Java 异常类层次

9.2 try-catch-finally 完整结构

JavaTryDemo.java
try {
    // 可能出错的代码
    int result = 10 / 0;
} catch (ArithmeticException e) {        // 捕获特定异常
    System.err.println("数学错误:" + e.getMessage());
} catch (Exception e) {                  // 兜底:捕获更宽泛
    System.err.println("其它错误:" + e.getMessage());
} finally {
    // 无论是否出错都执行(资源清理)
    System.out.println("结束");
}

9.3 throw / throws 与自定义异常

JavaCustomException.java
// 1) 自定义异常类
public class InsufficientFundsException extends Exception {
    private double shortage;
    public InsufficientFundsException(String msg, double shortage) {
        super(msg);
        this.shortage = shortage;
    }
    public double getShortage() { return shortage; }
}

// 2) 抛出异常(throw)与声明(throws)
public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance)
        throw new InsufficientFundsException("余额不足", amount - balance);
    balance -= amount;
}

9.4 try-with-resources ⭐⭐ Java 7+

自动关闭实现了 AutoCloseable 的资源,比手写 finally 干净得多。

Javatry-with-resources
// ❌ 传统写法:手动关闭,容易遗漏
Scanner sc = null;
try {
    sc = new Scanner(new File("data.txt"));
    // ...
} finally {
    if (sc != null) sc.close();
}

// ✔ 现代写法:try-with-resources
try (Scanner sc = new Scanner(new File("data.txt"));
     FileWriter w = new FileWriter("out.txt")) {
    // sc 与 w 会在 try 结束时自动关闭
}

课堂练习 · 第09章

12下列哪个异常是 RuntimeException?
13关于 finally 块,正确的是?
10

泛型:编写类型安全的通用代码

泛型让"一份代码处理多种数据类型"成为可能,且编译器能检查类型错误。

10.1 泛型类、泛型方法、泛型接口

JavaGenericDemo.java
// 泛型类:T 是类型参数
public class Box<T> {
    private T value;
    public Box(T v) { this.value = v; }
    public T get()   { return value; }
}

Box<String>  bs = new Box<>("hi");    // T = String
Box<Integer> bi = new Box<>(42);    // T = Integer

// 泛型方法
public static <T> T first(List<T> list) {
    return list.get(0);
}

// 常见类型参数约定
// T = Type, E = Element, K = Key, V = Value, R = Return

10.2 通配符:? extends / ? super

JavaWildcard.java
List<Integer>    intList = Arrays.asList(1, 2, 3);
List<Number>    numList = Arrays.asList(1.5, 2.5);

// ? extends T:上界,能读不能写
public static double sum(List<? extends Number> list) {
    double s = 0;
    for (Number n : list) s += n.doubleValue();
    return s;
}

// ? super T:下界,能写不能读
public static void addInts(List<? super Integer> list) {
    list.add(42);   // 可以添加 Integer
}
11

集合框架 ⭐⭐⭐ —— 日常开发的核心工具

Java 集合(Collection Framework)是处理"一组对象"的标准答案。

11.1 集合体系总览

Collection<E> List<E> 有序可重复 Set<E> 无序不可重复 Queue<E> 队列 ArrayList LinkedList Vector HashSet TreeSet Deque PQ Map<K,V> 键值对 (与 Collection 并列)
Java 集合体系总览

11.2 ArrayList ⭐ 与 List 操作

JavaListDemo.java
import java.util.*;

List<String> list = new ArrayList<>();  // 父接口引用 + 子类实例(多态)
list.add("Alice");
list.add("Bob");
list.add("1", "Carol");           // 指定下标插入

String first = list.get(0);
list.set(0, "Alex");           // 修改
list.remove("Bob");          // 按值删除

System.out.println(list.size());   // 元素个数
System.out.println(list.contains("Alice"));

// 三种遍历
for (String s : list) { System.out.println(s); }
list.forEach(System.out::println);

11.3 HashMap ⭐ 与 Map 操作

JavaMapDemo.java
Map<String, Integer> scores = new HashMap<>();

scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Carol", 92);

Integer a = scores.get("Alice");       // 95,不存在则返回 null
Integer b = scores.getOrDefault("Dave", 0);

System.out.println(scores.containsKey("Alice"));
scores.remove("Bob");

// 三种遍历
scores.forEach((k, v) -> System.out.println(k + ": " + v));
for (Map.Entry<String, Integer> e : scores.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}
关键要求:放进 HashMap / HashSet 的对象,必须正确实现 equalshashCode,否则行为异常。

11.4 ArrayList vs LinkedList 选择

场景推荐理由
频繁按下标读取ArrayList底层是数组,O(1) 随机访问
频繁在中间增删LinkedList链表结构,O(1) 增删(找到位置后)
大多数情况ArrayList(默认)缓存局部性更好,CPU 缓存友好
12

IO 与文件

Java 用"流(Stream)"统一读写;NIO(New IO)更现代高效。

12.1 字节流 vs 字符流

类型抽象类常用实现用途
字节流InputStream / OutputStreamFileInputStream, BufferedInputStream图片、音视频、二进制
字符流Reader / WriterFileReader, BufferedReader文本文件(自动处理编码)
JavaFileReadWrite.java
import java.nio.file.*;
import java.io.*;

// 读取整个文本文件(推荐用 NIO)
String content = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8);

// 写入
Files.writeString(Path.of("out.txt"), "Hello, Java!");

// 逐行读取(推荐用 try-with-resources)
try (BufferedReader br = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}
13

日期时间与正则表达式

现代 Java 推荐使用 java.time(Java 8+);正则用 PatternMatcher

13.1 java.time:现代日期 API

JavaDateTimeDemo.java
import java.time.*;
import java.time.format.*;

LocalDate      today    = LocalDate.now();
LocalTime      now      = LocalTime.now();
LocalDateTime  datetime = LocalDateTime.now();
ZonedDateTime  zoned    = ZonedDateTime.now();

// 计算:加 7 天、减 3 个月
LocalDate nextWeek = today.plusDays(7);
LocalDate prevMonth = today.minusMonths(3);

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String text = datetime.format(fmt);

// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-03-15 10:30:00", fmt);

13.2 正则表达式

JavaRegexDemo.java
import java.util.regex.*;

Pattern p = Pattern.compile("\\d{3}-\\d{4}");     // 电话号码
Matcher m = p.matcher("电话:010-1234,021-5678");

while (m.find()) {
    System.out.println("找到:" + m.group());
}

// 校验邮箱
boolean ok = "a@b.com".matches("^[\\w.]+@[\\w.]+$");
14

Lambda 与 Stream ⭐⭐

Java 8 引入的函数式编程能力 —— 让集合处理更简洁、更易并行。

14.1 Lambda 表达式 Java 8

JavaLambdaDemo.java
// (参数) -> 表达式
Runnable r1 = () -> System.out.println("Hi");

Comparator<Integer> cmp = (a, b) -> a - b;

// 函数式接口(@FunctionalInterface)
// Consumer<T> 消费一个 T,无返回值
// Supplier<T> 生产一个 T,无参数
// Function<T,R> 接收 T,返回 R
// Predicate<T> 接收 T,返回 boolean

// 方法引用(Method Reference)
Function<String, Integer> len = String::length;
Consumer<String> printer = System.out::println;

14.2 Stream API:链式数据处理

JavaStreamDemo.java
List<String> names = Arrays.asList("Alice", "Bob", "Carol", "Alex");

// 找出以 A 开头、长度 > 4 的名字,按字母排序,去重
List<String> result = names.stream()
    .filter(s -> s.startsWith("A"))       // 过滤
    .filter(s -> s.length() > 4)        // 再过滤
    .sorted()                             // 排序
    .distinct()                           // 去重
    .limit(10)                            // 取前 10
    .collect(Collectors.toList());   // 收集

// 数值统计
IntStream range = IntStream.rangeClosed(1, 100);
long sum = range.sum();       // 5050
OptionalDouble avg = range.average();
Stream 三段式:① 创建流(collection.stream()Stream.of(...)) ② 中间操作(filter / map / sorted,无副作用) ③ 终止操作(collect / forEach / count,产生结果)。

14.3 Optional:优雅处理 null Java 8

JavaOptionalDemo.java
Optional<String> opt = Optional.ofNullable(mayBeNull);

String result = opt
    .filter(s -> s.length() > 0)
    .map(String::toUpperCase)
    .orElse("默认值");
15

反射与注解

反射让 Java 具备"运行时自省"能力 —— 是 Spring / Hibernate 等框架的基石。

15.1 反射:运行时获取类信息

JavaReflectionDemo.java
import java.lang.reflect.*;

Class<?> clazz = Student.class;

// 获取所有字段
for (Field f : clazz.getDeclaredFields()) {
    System.out.println("字段:" + f.getName());
}

// 动态创建对象
Student s = (Student) clazz.getDeclaredConstructor().newInstance();
s.study();

15.2 自定义注解

JavaCustomAnnotation.java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyTest {
    String value() default "";
}
16

多线程与并发 ⭐⭐

现代程序离不开并发 —— Java 在语言层内置了强大的多线程支持。

16.1 两种创建线程方式

JavaThreadDemo.java
// 方式 1:继承 Thread
class MyThread extends Thread {
    @Override public void run() {
        System.out.println("线程运行:" + getName());
    }
}
new MyThread().start();

// 方式 2:实现 Runnable(推荐,可继续继承其他类)
class MyTask implements Runnable {
    public void run() {
        System.out.println("任务运行");
    }
}
new Thread(new MyTask()).start();

// 方式 3:Lambda(Java 8+)
new Thread(() -> System.out.println("Lambda")).start();

16.2 线程池 ExecutorService Java 5+

JavaExecutorDemo.java
import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(4);

for (int i = 0; i < 10; i++) {
    int taskId = i;
    pool.submit(() -> {
        System.out.println("任务 " + taskId);
    });
}

pool.shutdown();

16.3 同步:synchronized 与 Lock

JavaSyncDemo.java
class Counter {
    private int count = 0;

    // 同步方法
    public synchronized void inc() {
        count++;
    }

    // 同步块(更细粒度)
    public void inc2() {
        synchronized (this) {
            count++;
        }
    }
}
17

网络编程:Socket 通信

Java 内置的 java.net 让 TCP/UDP 编程变得直接。

17.1 TCP 简单服务器

JavaSimpleServer.java
try (ServerSocket server = new ServerSocket(8080);
     Socket client = server.accept();                       // 阻塞等待客户端
     BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
     PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {

    String line;
    while ((line = in.readLine()) != null) {
        out.println("Echo: " + line);
    }
}
18

XML、JSON 与数据交换

现代 Java 项目更多用 JSON;XML 仍在企业接口、银行系统中常见。

18.1 JSON 与 Jackson

JavaJsonDemo.java(需 jackson-databind)
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();

// 对象 → JSON 字符串
Student s = new Student("Alice", 20);
String json = mapper.writeValueAsString(s);   // {"name":"Alice","age":20}

// JSON 字符串 → 对象
Student s2 = mapper.readValue(json, Student.class);
19

JDBC 与数据库 ⭐⭐⭐

Java 通过 JDBC 与数据库交互 —— 是企业级开发的"必备技能"。

19.1 JDBC 五步曲

JavaJdbcDemo.java
// 1) 加载驱动(Java 6+ 可省)
Class.forName("com.mysql.cj.jdbc.Driver");

// 2) 建立连接
String url = "jdbc:mysql://localhost:3306/school?useSSL=false";
try (Connection conn = DriverManager.getConnection(url, "root", "pwd")) {

    // 3) 创建 PreparedStatement(推荐,防 SQL 注入)
    PreparedStatement ps = conn.prepareStatement(
        "SELECT * FROM students WHERE age > ?");
    ps.setInt(1, 18);

    // 4) 执行查询
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
        String name = rs.getString("name");
        int age = rs.getInt("age");
        System.out.printf("%s - %d%n", name, age);
    }

    // 5) 关闭资源(try-with-resources 自动)
}
20

单元测试:JUnit 5

不会写测试的程序员写不出可靠的代码。

3 个最常用注解

JavaJUnitDemo.java
import org.junit.jupiter.api.*;

class CalculatorTest {
    private Calculator calc;

    @BeforeEach
    void setUp() { calc = new Calculator(); }

    @Test
    void testAdd() {
        Assertions.assertEquals(5, calc.add(2, 3));
    }

    @Test
    void testDivideByZero() {
        Assertions.assertThrows(ArithmeticException.class,
            () -> calc.divide(1, 0));
    }
}
21

Maven 与项目管理

Java 项目的行业标准构建工具 —— 管理依赖、编译、测试、打包。

Maven 项目结构与 pom.xml

XMLpom.xml
<!-- pom.xml:Maven 项目的核心配置 -->
<project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>        <--> 组织域名 -->
    <artifactId>demo</artifactId>          <--> 项目名 -->
    <version>1.0-SNAPSHOT</version>     <--> 版本 -->

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
常用命令:mvn clean compile 编译 · mvn test 跑测试 · mvn package 打包 · mvn dependency:tree 看依赖树。
22

代码规范与调试

写出能跑的程序只是开始 —— 让代码易读、易维护才是工程能力。

Java 命名规范(业界共识)

类别规则示例
PascalCase(名词)StudentService
方法 / 变量camelCase(动词 / 名词)getNametotalCount
常量UPPER_SNAKE_CASEMAX_SIZE
包名全小写,域名反写com.example.school
接口PascalCase,可加 I 前缀ComparableIStudent

Java 专属常见 bug

  • NullPointerException:对象未初始化就被访问;
  • ArrayIndexOutOfBoundsException:下标越界;
  • ClassCastException:类型转换错误;
  • 字符串比较用 ==:必须用 equals
  • 资源未关闭:用 try-with-resources;
  • ConcurrentModificationException:遍历时修改集合;
  • 死锁:多线程加锁顺序不一致。
23

数据结构与算法(Java 实现)

Java 自带集合框架覆盖了大部分常用结构;自定义时建议继承或参考现有接口。

单链表实现

JavaLinkedListDemo.java
class Node<T> {
    T data;
    Node<T> next;
    Node(T d) { data = d; }
}

public class MyLinkedList<T> {
    private Node<T> head;

    public void addFirst(T v) {
        Node<T> n = new Node<>(v);
        n.next = head;
        head = n;
    }

    public void print() {
        for (Node<T> p = head; p != null; p = p.next)
            System.out.print(p.data + " -> ");
        System.out.println("null");
    }
}
24

综合项目:从练习到作品

以下项目按难度递进,建议至少完成前 3 个。

项目一:学生成绩管理系统 📊

难度 ★★★ · OOP / ArrayList / HashMap / File

Student 类存储学生信息,ArrayList<Student> 保存多名学生;支持增删改查、按成绩排序、文件持久化。

项目二:通讯录系统 📞

难度 ★★★★ · OOP / HashMap / 文件 / 搜索

HashMap 实现 O(1) 查找;支持模糊搜索;CSV 持久化。

项目三:图书借阅系统 📚

难度 ★★★★ · OOP / 继承 / 多态 / 异常

基类 Book + 派生 TextBookNovelMagazine;支持借还状态机。

项目四:学生信息数据库系统 🗄️

难度 ★★★★ · JDBC / MySQL / 分页查询

用 JDBC 连接 MySQL,实现学生信息的增删改查与分页。

项目五:网络聊天室 💬

难度 ★★★★★ · Socket / 多线程 / 集合

TCP 客户端 + 多线程服务器;支持多人聊天。

项目六:综合学生管理系统 🎓

难度 ★★★★★ · 全部所学 + Maven + JUnit + Git

Maven 多模块项目;Spring Boot / JDBC / JUnit / Git;体现工程实践能力。

Java 速查手册

写代码时随手翻一翻 —— 比每次去搜更快。

常用关键字(50+ 个)

abstractassertbooleanbreakbytecasecatchcharclassconstcontinuedefaultdodoubleelseenumextendsfinalfinallyfloatforgotoifimplementsimportinstanceofintinterfacelongnativenewpackageprivateprotectedpublicrecordreturnsealedshortstaticstrictfpsuperswitchsynchronizedthisthrowthrowstransienttryvoidvolatilewhileyield

集合选择速查

需求推荐
通用序列ArrayList<T>
中间频繁增删LinkedList<T>
栈 / 队列ArrayDeque<T>
优先队列PriorityQueue<T>
去重集合HashSet<T>(无序)或 TreeSet<T>(有序)
键值映射HashMap<K,V>(默认)或 TreeMap<K,V>(有序)
线程安全ConcurrentHashMap

常用代码模板

Java模板
// 1) 读文本文件(NIO)
String text = Files.readString(Path.of("a.txt"), StandardCharsets.UTF_8);

// 2) 写文本
Files.writeString(Path.of("a.txt"), text);

// 3) 遍历 Map
Map.entrySet().forEach(e -> log(e.getKey(), e.getValue()));

// 4) 时间测量
long t0 = System.nanoTime();
// ...
long ms = (System.nanoTime() - t0) / 1_000_000;

// 5) List → Stream → Map
Map<Integer, String> m = list.stream()
    .collect(Collectors.toMap(Student::getId, Student::getName));
@media print{ #topnav,#progress,.back-top{display:none!important} .hero{padding:20px 0!important;background:none!important;color:#000!important} .hero h1{color:#000!important} .hero .sub,.hero .hero-chips{color:#333!important} section{break-inside:avoid;box-shadow:none!important;border:1px solid #ccc!important} pre{background:#f5f5f5!important;color:#000!important} }