Java 基础编程教程
本教程面向零基础学习者,系统讲解 Java 编程语言的核心概念与实战技巧。每个章节均配有详细代码示例和深入解释,帮助你从入门到能够独立开发小型项目。
目录
- Java语言简介与历史
- JDK安装与环境配置
- 第一个Java程序
- 数据类型与变量
- 运算符
- 控制流语句
- 数组详解
- 面向对象编程:类与对象
- 封装、继承、多态
- 抽象类与接口
- 异常处理机制
- 集合框架(List/Set/Map)
- 泛型编程
- 字符串处理(String/StringBuilder)
- 文件I/O操作
- 多线程基础
- Lambda表达式
- Stream API
- 常用设计模式
- 实战:学生管理系统
- 实战:简易图书管理
- 调试技巧与最佳实践
1. Java语言简介与历史
1.1 Java 的诞生
Java 是由 Sun Microsystems 公司的 James Gosling 及其团队于 1995 年正式发布的编程语言。最初这个项目被称为 "Oak"(橡树),后来因为商标冲突改名为 Java——这个名字来源于印尼爪哇岛的咖啡,所以 Java 的标志是一杯热气腾腾的咖啡。
Java 的设计初衷是为家用电器编写分布式系统程序,但随着互联网的兴起,它迅速成为 Web 开发和企业级应用的首选语言。
1.2 Java 的核心特性
Java 能够在众多编程语言中脱颖而出,主要依赖以下几个核心特性:
跨平台性(Write Once, Run Anywhere) Java 程序编译后生成字节码(.class 文件),而不是直接生成机器码。字节码运行在 Java 虚拟机(JVM)之上,而 JVM 可以安装在不同的操作系统上。这意味着同一份 Java 代码可以在 Windows、Linux、macOS 等任意平台上运行,无需重新编译。
面向对象 Java 是纯粹的面向对象编程语言。在 Java 中,几乎所有的东西都是对象(除了基本数据类型)。面向对象的三大特性——封装、继承、多态——在 Java 中得到了充分体现。
自动垃圾回收 Java 提供了自动垃圾回收机制(Garbage Collection, GC),开发者无需手动释放内存。JVM 会自动检测不再使用的对象并回收它们占用的内存空间,大大降低了内存泄漏的风险。
强类型语言 Java 是强类型语言,每个变量都必须先声明类型才能使用。编译器会在编译阶段进行类型检查,提前发现潜在的类型错误,提高代码的安全性和可靠性。
丰富的标准库 Java 提供了庞大的标准类库(Java API),涵盖了网络编程、文件操作、数据库连接、图形界面、并发编程等方方面面,开发者可以直接调用,无需从零实现。
1.3 Java 的发展历史
| 年份 | 版本 | 重要事件 |
|---|---|---|
| 1995 | Java 1.0 | Java 正式发布 |
| 1998 | Java 1.2 | 引入集合框架、Swing GUI |
| 2004 | Java 5.0 | 引入泛型、枚举、注解、自动装箱 |
| 2006 | Java 6 | 性能优化、脚本引擎支持 |
| 2011 | Java 7 | try-with-resources、钻石操作符 |
| 2014 | Java 8 | Lambda 表达式、Stream API(里程碑版本) |
| 2017 | Java 9 | 模块化系统 |
| 2018+ | Java 11/17/21 | LTS 版本,持续增强 |
1.4 Java 的应用领域
- 企业级应用:银行系统、电商平台、ERP 系统
- Android 开发:Android 应用的主要开发语言(虽然 Kotlin 逐渐流行,但 Java 仍是基础)
- 大数据技术:Hadoop、Spark、Flink 等大数据框架都基于 Java/JVM
- Web 后端:Spring Boot、Spring Cloud 等框架广泛应用于微服务架构
- 中间件:Tomcat、Netty、RocketMQ 等知名中间件都用 Java 编写
2. JDK安装与环境配置
2.1 什么是 JDK、JRE、JVM
在开始编程之前,需要先理解三个关键概念的关系:
- JVM(Java Virtual Machine):Java 虚拟机,负责执行字节码,是跨平台的核心
- JRE(Java Runtime Environment):Java 运行时环境,包含 JVM 和核心类库,用于运行 Java 程序
- JDK(Java Development Kit):Java 开发工具包,包含 JRE 以及编译器(javac)、调试器等开发工具
它们的包含关系是:JDK ⊃ JRE ⊃ JVM
作为开发者,我们安装 JDK 即可。
2.2 下载与安装 JDK
推荐使用 JDK 17 或 JDK 21(LTS 长期支持版本)。以 JDK 17 为例:
Windows 系统:
- 访问 Oracle 官网或 Adoptium(Eclipse Temurin)下载页面
- 选择 Windows x64 安装包(.msi 或 .exe)
- 双击安装,记住安装路径(如
C:\Program Files\Java\jdk-17)
macOS 系统:
- 可以使用 Homebrew:
brew install openjdk@17 - 或者下载 .dmg 安装包手动安装
Linux 系统:
# Ubuntu/Debian
sudo apt update
sudo apt install openjdk-17-jdk
# CentOS/RHEL
sudo yum install java-17-openjdk-devel
2.3 配置环境变量
安装完成后,需要配置环境变量,让系统能找到 Java 命令。
Windows 系统:
- 右键"此电脑" → 属性 → 高级系统设置 → 环境变量
- 新建系统变量
JAVA_HOME,值为 JDK 安装路径 - 编辑
Path变量,添加%JAVA_HOME%\bin
Linux/macOS 系统:
在 ~/.bashrc 或 ~/.zshrc 中添加:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
然后执行 source ~/.bashrc 使配置生效。
2.4 验证安装
打开终端或命令提示符,执行以下命令:
java -version
javac -version
如果看到版本号输出,说明安装和配置成功。例如:
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment (build 17.0.8+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.8+9-LTS-211, mixed mode, sharing)
3. 第一个Java程序
3.1 编写 Hello World
创建一个名为 HelloWorld.java 的文件,输入以下代码:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("你好,Java!");
}
}
3.2 代码逐行解析
public class HelloWorld:定义一个公共类,类名必须与文件名完全一致(包括大小写)public static void main(String[] args):主方法,是程序的入口点。JVM 启动时会寻找这个方法public:公开访问权限static:静态方法,无需创建对象即可调用void:无返回值String[] args:命令行参数数组
System.out.println():向控制台输出一行文本并换行
3.3 编译与运行
# 编译:将 .java 源文件编译为 .class 字节码文件
javac HelloWorld.java
# 运行:通过 JVM 执行字节码
java HelloWorld
输出结果:
你好,Java!
3.4 常见错误排查
- 找不到文件:确认文件名和类名一致,且在正确的目录下执行
- 编码问题:如果包含中文,编译时加
-encoding UTF-8参数 - 缺少分号:Java 语句必须以分号结尾
4. 数据类型与变量
4.1 基本数据类型
Java 有 8 种基本数据类型(Primitive Types),分为四类:
| 类型 | 关键字 | 大小 | 范围 | 默认值 |
|---|---|---|---|---|
| 整型 | byte |
1字节 | -128 ~ 127 | 0 |
| 整型 | short |
2字节 | -32768 ~ 32767 | 0 |
| 整型 | int |
4字节 | -231 ~ 231-1 | 0 |
| 整型 | long |
8字节 | -263 ~ 263-1 | 0L |
| 浮点型 | float |
4字节 | 约 ±3.4×10^38 | 0.0f |
| 浮点型 | double |
8字节 | 约 ±1.7×10^308 | 0.0d |
| 字符型 | char |
2字节 | 0 ~ 65535 | '\u0000' |
| 布尔型 | boolean |
1位 | true / false | false |
4.2 变量声明与初始化
public class DataTypeDemo {
public static void main(String[] args) {
// 整型变量
byte age = 25;
short temperature = -10;
int population = 1400000000;
long distance = 384400000L; // long 类型需要加 L 后缀
// 浮点型变量
float price = 9.99f; // float 类型需要加 f 后缀
double pi = 3.141592653589793;
// 字符型变量
char grade = 'A';
char chinese = '中'; // 支持 Unicode 字符
// 布尔型变量
boolean isStudent = true;
boolean hasJob = false;
// 输出所有变量
System.out.println("年龄: " + age);
System.out.println("温度: " + temperature + "°C");
System.out.println("人口: " + population);
System.out.println("月球距离: " + distance + " 米");
System.out.println("价格: ¥" + price);
System.out.println("圆周率: " + pi);
System.out.println("等级: " + grade);
System.out.println("汉字: " + chinese);
System.out.println("是否学生: " + isStudent);
}
}
4.3 引用数据类型
除了基本类型,Java 还有引用数据类型(Reference Types),包括:
- 类(Class):如
String、Integer - 接口(Interface)
- 数组(Array)
public class ReferenceTypeDemo {
public static void main(String[] args) {
// String 是引用类型
String name = "张三";
String greeting = new String("你好");
// 数组也是引用类型
int[] scores = {90, 85, 92};
System.out.println("姓名: " + name);
System.out.println("问候: " + greeting);
System.out.println("第一个分数: " + scores[0]);
}
}
4.4 类型转换
自动类型转换(隐式):小范围类型自动转换为大范围类型。
int a = 100;
long b = a; // int → long,自动转换
double c = b; // long → double,自动转换
强制类型转换(显式):大范围类型转为小范围类型,可能丢失精度。
double pi = 3.14159;
int truncated = (int) pi; // 结果为 3,小数部分丢失
long bigNum = 130L;
byte smallNum = (byte) bigNum; // 溢出,结果为 -126
4.5 常量
使用 final 关键字声明常量,常量一旦赋值不可修改:
public class ConstantDemo {
public static void main(String[] args) {
final double TAX_RATE = 0.13;
final String SCHOOL_NAME = "清华大学";
final int MAX_RETRY = 3;
System.out.println("税率: " + TAX_RATE);
System.out.println("学校: " + SCHOOL_NAME);
// TAX_RATE = 0.15; // 编译错误!常量不可修改
}
}
5. 运算符
5.1 算术运算符
public class ArithmeticDemo {
public static void main(String[] args) {
int a = 17, b = 5;
System.out.println("a + b = " + (a + b)); // 22
System.out.println("a - b = " + (a - b)); // 12
System.out.println("a * b = " + (a * b)); // 85
System.out.println("a / b = " + (a / b)); // 3(整数除法)
System.out.println("a % b = " + (a % b)); // 2(取余)
// 浮点除法
double x = 17.0, y = 5.0;
System.out.println("x / y = " + (x / y)); // 3.4
// 自增自减
int count = 10;
System.out.println("count++ = " + count++); // 先使用再自增,输出10
System.out.println("count = " + count); // 11
System.out.println("++count = " + ++count); // 先自增再使用,输出12
}
}
5.2 关系运算符
public class RelationalDemo {
public static void main(String[] args) {
int x = 10, y = 20;
System.out.println("x == y : " + (x == y)); // false
System.out.println("x != y : " + (x != y)); // true
System.out.println("x > y : " + (x > y)); // false
System.out.println("x < y : " + (x < y)); // true
System.out.println("x >= 10: " + (x >= 10)); // true
System.out.println("y <= 15: " + (y <= 15)); // false
}
}
5.3 逻辑运算符
public class LogicalDemo {
public static void main(String[] args) {
boolean a = true, b = false;
System.out.println("a && b = " + (a && b)); // false(与)
System.out.println("a || b = " + (a || b)); // true(或)
System.out.println("!a = " + (!a)); // false(非)
// 短路求值示例
int x = 5;
// 因为 x > 10 为 false,短路后不会执行 x++
if (x > 10 && ++x > 0) {
// 不会执行
}
System.out.println("x = " + x); // 仍然是 5
// 三元运算符
int score = 85;
String result = (score >= 60) ? "及格" : "不及格";
System.out.println("成绩: " + score + "," + result);
}
}
5.4 位运算符
public class BitwiseDemo {
public static void main(String[] args) {
int a = 0b1010; // 二进制 1010,十进制 10
int b = 0b1100; // 二进制 1100,十进制 12
System.out.println("a & b = " + Integer.toBinaryString(a & b)); // 1000 (8)
System.out.println("a | b = " + Integer.toBinaryString(a | b)); // 1110 (14)
System.out.println("a ^ b = " + Integer.toBinaryString(a ^ b)); // 0110 (6)
System.out.println("~a = " + Integer.toBinaryString(~a)); // 取反
System.out.println("a << 2 = " + Integer.toBinaryString(a << 2)); // 101000 (40)
System.out.println("a >> 1 = " + Integer.toBinaryString(a >> 1)); // 101 (5)
}
}
5.5 赋值运算符
public class AssignmentDemo {
public static void main(String[] args) {
int x = 10;
x += 5; // x = x + 5 → 15
System.out.println("x += 5 → " + x);
x -= 3; // x = x - 3 → 12
System.out.println("x -= 3 → " + x);
x *= 2; // x = x * 2 → 24
System.out.println("x *= 2 → " + x);
x /= 4; // x = x / 4 → 6
System.out.println("x /= 4 → " + x);
x %= 4; // x = x % 4 → 2
System.out.println("x %= 4 → " + x);
}
}
6. 控制流语句
6.1 if-else 条件语句
public class IfElseDemo {
public static void main(String[] args) {
int score = 78;
// 基本 if-else
if (score >= 60) {
System.out.println("恭喜,你及格了!");
} else {
System.out.println("很遗憾,继续努力!");
}
// 多重 if-else
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 70) {
System.out.println("中等"); // 匹配这个分支
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
// 嵌套 if
boolean isRegistered = true;
if (isRegistered) {
if (score >= 60) {
System.out.println("已通过考试,可以获取证书");
} else {
System.out.println("未通过考试,请重考");
}
} else {
System.out.println("请先注册考试");
}
}
}
6.2 switch 语句
public class SwitchDemo {
public static void main(String[] args) {
int dayOfWeek = 3;
// 传统 switch
switch (dayOfWeek) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三"); // 输出这个
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
case 7:
System.out.println("周末");
break;
default:
System.out.println("无效的日期");
}
// Java 14+ 增强 switch(箭头语法)
String dayName = switch (dayOfWeek) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
case 4 -> "星期四";
case 5 -> "星期五";
case 6, 7 -> "周末";
default -> "无效";
};
System.out.println("今天是: " + dayName);
}
}
6.3 for 循环
public class ForLoopDemo {
public static void main(String[] args) {
// 基本 for 循环
System.out.println("=== 基本 for 循环 ===");
for (int i = 1; i <= 5; i++) {
System.out.println("第 " + i + " 次循环");
}
// 计算 1 到 100 的和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("1到100的和: " + sum); // 5050
// 打印九九乘法表
System.out.println("=== 九九乘法表 ===");
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();
}
// 增强 for 循环(for-each)
System.out.println("=== for-each 循环 ===");
int[] numbers = {10, 20, 30, 40, 50};
for (int num : numbers) {
System.out.println("数值: " + num);
}
}
}
6.4 while 与 do-while 循环
public class WhileDemo {
public static void main(String[] args) {
// while 循环:先判断条件,再执行
System.out.println("=== while 循环 ===");
int count = 1;
while (count <= 5) {
System.out.println("count = " + count);
count++;
}
// do-while 循环:先执行一次,再判断条件
System.out.println("=== do-while 循环 ===");
int num = 10;
do {
System.out.println("num = " + num);
num--;
} while (num > 0);
// 实际应用:猜数字游戏
System.out.println("=== 猜数字游戏 ===");
int target = 42;
int guess = 0;
int attempts = 0;
int[] guesses = {20, 50, 35, 45, 42}; // 模拟猜测序列
while (guess != target && attempts < guesses.length) {
guess = guesses[attempts];
attempts++;
if (guess < target) {
System.out.println("猜的 " + guess + " 太小了!");
} else if (guess > target) {
System.out.println("猜的 " + guess + " 太大了!");
} else {
System.out.println("恭喜!猜对了!答案就是 " + target);
System.out.println("你总共猜了 " + attempts + " 次");
}
}
}
}
6.5 break 与 continue
public class BreakContinueDemo {
public static void main(String[] args) {
// break:跳出循环
System.out.println("=== break 示例 ===");
for (int i = 1; i <= 10; i++) {
if (i == 6) {
System.out.println("遇到 6,跳出循环");
break;
}
System.out.println("i = " + i);
}
// continue:跳过本次迭代
System.out.println("=== continue 示例 ===");
for (int i = 1; i <= 10; i++) {
if (i % 3 == 0) {
continue; // 跳过3的倍数
}
System.out.println("i = " + i);
}
// 带标签的 break(跳出多层循环)
System.out.println("=== 带标签的 break ===");
outer:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i == 2 && j == 3) {
System.out.println("在 i=" + i + ", j=" + j + " 处跳出外层循环");
break outer;
}
System.out.print("(" + i + "," + j + ") ");
}
System.out.println();
}
}
}
7. 数组详解
7.1 数组的声明与创建
public class ArrayBasics {
public static void main(String[] args) {
// 方式一:声明后分配空间
int[] scores = new int[5]; // 创建长度为5的int数组
scores[0] = 95;
scores[1] = 87;
scores[2] = 73;
scores[3] = 91;
scores[4] = 88;
// 方式二:声明并初始化
String[] fruits = {"苹果", "香蕉", "橘子", "葡萄", "西瓜"};
// 方式三:使用 new 关键字初始化
double[] prices = new double[]{9.9, 15.5, 23.0, 8.8};
// 访问数组元素
System.out.println("第一个水果: " + fruits[0]); // 苹果
System.out.println("数组长度: " + scores.length); // 5
// 修改数组元素
scores[0] = 98;
System.out.println("修改后的第一个分数: " + scores[0]); // 98
// 遍历数组
System.out.println("所有水果:");
for (int i = 0; i < fruits.length; i++) {
System.out.println(" " + (i + 1) + ". " + fruits[i]);
}
// 使用 for-each 遍历
System.out.println("所有价格:");
for (double price : prices) {
System.out.println(" ¥" + price);
}
}
}
7.2 二维数组
public class TwoDArrayDemo {
public static void main(String[] args) {
// 声明并初始化二维数组
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 访问二维数组
System.out.println("matrix[1][2] = " + matrix[1][2]); // 6
// 遍历二维数组
System.out.println("矩阵内容:");
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.printf("%4d", matrix[i][j]);
}
System.out.println();
}
// 不规则数组(锯齿数组)
int[][] jagged = new int[3][];
jagged[0] = new int[]{1, 2};
jagged[1] = new int[]{3, 4, 5, 6};
jagged[2] = new int[]{7};
System.out.println("不规则数组:");
for (int[] row : jagged) {
for (int val : row) {
System.out.print(val + " ");
}
System.out.println();
}
}
}
7.3 数组常用操作
import java.util.Arrays;
public class ArrayOperations {
public static void main(String[] args) {
int[] arr = {64, 25, 12, 22, 11, 90, 45, 78};
// 排序
int[] sorted = arr.clone();
Arrays.sort(sorted);
System.out.println("排序后: " + Arrays.toString(sorted));
// 二分查找(需先排序)
int index = Arrays.binarySearch(sorted, 22);
System.out.println("22 的索引位置: " + index);
// 填充
int[] filled = new int[5];
Arrays.fill(filled, 99);
System.out.println("填充后: " + Arrays.toString(filled));
// 复制
int[] copied = Arrays.copyOf(arr, 5);
System.out.println("复制前5个: " + Arrays.toString(copied));
// 比较
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println("数组相等: " + Arrays.equals(a, b)); // true
// 手动实现冒泡排序
int[] bubbleArr = {64, 25, 12, 22, 11};
bubbleSort(bubbleArr);
System.out.println("冒泡排序结果: " + Arrays.toString(bubbleArr));
}
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
8. 面向对象编程:类与对象
8.1 类的定义
类是对象的模板,定义了对象的属性(字段)和行为(方法)。
public class Person {
// 字段(属性)
String name;
int age;
String gender;
// 构造方法
public Person() {
this.name = "未知";
this.age = 0;
this.gender = "未知";
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 方法(行为)
public void introduce() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁,性别" + gender);
}
public void celebrateBirthday() {
age++;
System.out.println(name + "过生日了!现在" + age + "岁了");
}
}
8.2 创建和使用对象
public class PersonTest {
public static void main(String[] args) {
// 使用无参构造创建对象
Person p1 = new Person();
p1.introduce(); // 大家好,我叫未知,今年0岁,性别未知
// 使用有参构造创建对象
Person p2 = new Person("张三", 25, "男");
p2.introduce(); // 大家好,我叫张三,今年25岁,性别男
Person p3 = new Person("李四", 22, "女");
p3.introduce(); // 大家好,我叫李四,今年22岁,性别女
// 调用方法
p2.celebrateBirthday(); // 张三过生日了!现在26岁了
// 直接访问字段(不推荐,应使用封装)
p3.age = 23;
System.out.println(p3.name + " 的年龄改为: " + p3.age);
}
}
8.3 方法重载
同一个类中可以有多个同名方法,只要参数列表不同即可,这就是方法重载。
public class Calculator {
// 两数相加
public int add(int a, int b) {
return a + b;
}
// 三个数相加(参数个数不同)
public int add(int a, int b, int c) {
return a + b + c;
}
// 浮点数相加(参数类型不同)
public double add(double a, double b) {
return a + b;
}
// 字符串拼接(参数类型不同)
public String add(String a, String b) {
return a + b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(3, 5)); // 8
System.out.println(calc.add(3, 5, 7)); // 15
System.out.println(calc.add(3.14, 2.86)); // 6.0
System.out.println(calc.add("Hello", "World")); // HelloWorld
}
}
8.4 this 关键字
this 关键字指向当前对象实例,常用于以下场景:
public class Student {
private String name;
private int age;
// 1. 区分同名的字段和参数
public Student(String name, int age) {
this.name = name; // this.name 是字段,name 是参数
this.age = age;
}
// 2. 调用其他构造方法
public Student() {
this("匿名", 0); // 调用双参构造
}
// 3. 返回当前对象(链式调用)
public Student setName(String name) {
this.name = name;
return this;
}
public Student setAge(int age) {
this.age = age;
return this;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
public static void main(String[] args) {
// 链式调用
Student s = new Student().setName("王五").setAge(20);
System.out.println(s);
}
}
9. 封装、继承、多态
9.1 封装
封装是将数据(字段)和操作数据的方法捆绑在一起,并通过访问修饰符控制外部访问。
public class BankAccount {
// 私有字段,外部不能直接访问
private String accountNumber;
private String ownerName;
private double balance;
// 构造方法
public BankAccount(String accountNumber, String ownerName, double initialBalance) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = initialBalance;
}
// 公共的 getter 方法(只读访问)
public String getAccountNumber() {
return accountNumber;
}
public String getOwnerName() {
return ownerName;
}
public double getBalance() {
return balance;
}
// 存款(包含业务逻辑验证)
public void deposit(double amount) {
if (amount <= 0) {
System.out.println("存款金额必须大于0");
return;
}
balance += amount;
System.out.println("存入 ¥" + amount + ",当前余额: ¥" + balance);
}
// 取款(包含业务逻辑验证)
public boolean withdraw(double amount) {
if (amount <= 0) {
System.out.println("取款金额必须大于0");
return false;
}
if (amount > balance) {
System.out.println("余额不足!当前余额: ¥" + balance);
return false;
}
balance -= amount;
System.out.println("取出 ¥" + amount + ",当前余额: ¥" + balance);
return true;
}
public static void main(String[] args) {
BankAccount account = new BankAccount("6222001234567890", "张三", 1000.0);
account.deposit(500.0); // 存入 ¥500.0,当前余额: ¥1500.0
account.withdraw(200.0); // 取出 ¥200.0,当前余额: ¥1300.0
account.withdraw(2000.0); // 余额不足!
// account.balance = 999999; // 编译错误!无法直接访问私有字段
System.out.println("余额: ¥" + account.getBalance()); // 通过 getter 访问
}
}
访问修饰符对比:
| 修饰符 | 同类 | 同包 | 子类 | 不同包 |
|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ❌ |
| 默认(无修饰符) | ✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ |
9.2 继承
继承允许子类复用父类的属性和方法,并在此基础上扩展。
// 父类:动物
class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name + "在吃东西");
}
public void sleep() {
System.out.println(name + "在睡觉");
}
public void showInfo() {
System.out.println("名字: " + name + ",年龄: " + age);
}
}
// 子类:狗
class Dog extends Animal {
private String breed; // 品种
public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造方法
this.breed = breed;
}
// 子类特有方法
public void bark() {
System.out.println(name + "汪汪叫!");
}
public void fetch() {
System.out.println(name + "去捡球了!");
}
// 重写父类方法
@Override
public void showInfo() {
super.showInfo(); // 调用父类方法
System.out.println("品种: " + breed);
}
}
// 子类:猫
class Cat extends Animal {
private boolean isIndoor; // 是否室内猫
public Cat(String name, int age, boolean isIndoor) {
super(name, age);
this.isIndoor = isIndoor;
}
public void meow() {
System.out.println(name + "喵喵叫!");
}
public void scratch() {
System.out.println(name + "在磨爪子!");
}
}
public class InheritanceDemo {
public static void main(String[] args) {
Dog dog = new Dog("旺财", 3, "金毛");
dog.showInfo();
dog.eat(); // 继承自父类
dog.bark(); // 子类特有
dog.fetch(); // 子类特有
System.out.println("---");
Cat cat = new Cat("咪咪", 2, true);
cat.eat(); // 继承自父类
cat.meow(); // 子类特有
cat.scratch(); // 子类特有
}
}
9.3 多态
多态是指同一操作作用于不同对象时,产生不同的行为。Java 中多态主要通过方法重写和向上转型实现。
// 形状基类
class Shape {
protected String name;
public Shape(String name) {
this.name = name;
}
public double area() {
return 0;
}
public void display() {
System.out.println(name + "的面积: " + String.format("%.2f", area()));
}
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
super("圆形");
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
super("矩形");
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
class Triangle extends Shape {
private double base, height;
public Triangle(double base, double height) {
super("三角形");
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
}
public class PolymorphismDemo {
// 接收 Shape 类型,实际可以是任何子类对象
public static void printArea(Shape shape) {
shape.display(); // 运行时决定调用哪个子类的 area() 方法
}
public static void main(String[] args) {
// 向上转型:子类对象赋给父类引用
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
Shape triangle = new Triangle(3.0, 8.0);
// 多态调用
printArea(circle); // 圆形的面积: 78.54
printArea(rect); // 矩形的面积: 24.00
printArea(triangle); // 三角形的面积: 12.00
// 使用数组存储不同形状
Shape[] shapes = {new Circle(3), new Rectangle(5, 4), new Triangle(6, 3)};
double totalArea = 0;
for (Shape s : shapes) {
s.display();
totalArea += s.area();
}
System.out.println("总面积: " + String.format("%.2f", totalArea));
// instanceof 类型检查
for (Shape s : shapes) {
if (s instanceof Circle c) {
System.out.println("这是一个圆形");
} else if (s instanceof Rectangle r) {
System.out.println("这是一个矩形");
}
}
}
}
10. 抽象类与接口
10.1 抽象类
抽象类使用 abstract 关键字修饰,不能被实例化,通常包含抽象方法(没有方法体)供子类实现。
// 抽象类:交通工具
abstract class Vehicle {
protected String brand;
protected int speed;
public Vehicle(String brand) {
this.brand = brand;
this.speed = 0;
}
// 抽象方法:子类必须实现
public abstract void start();
public abstract void stop();
public abstract String getType();
// 普通方法:子类可以直接继承
public void accelerate(int amount) {
speed += amount;
System.out.println(brand + " 加速到 " + speed + " km/h");
}
public void showStatus() {
System.out.println(getType() + " [" + brand + "] 当前速度: " + speed + " km/h");
}
}
class Car extends Vehicle {
private int doors;
public Car(String brand, int doors) {
super(brand);
this.doors = doors;
}
@Override
public void start() {
System.out.println(brand + " 轿车点火启动," + doors + "门设计");
}
@Override
public void stop() {
speed = 0;
System.out.println(brand + " 轿车停车熄火");
}
@Override
public String getType() {
return "轿车";
}
}
class Motorcycle extends Vehicle {
public Motorcycle(String brand) {
super(brand);
}
@Override
public void start() {
System.out.println(brand + " 摩托车踩下启动杆");
}
@Override
public void stop() {
speed = 0;
System.out.println(brand + " 摩托车刹车停下");
}
@Override
public String getType() {
return "摩托车";
}
}
public class AbstractDemo {
public static void main(String[] args) {
// Vehicle v = new Vehicle("测试"); // 编译错误!不能实例化抽象类
Vehicle car = new Car("宝马", 4);
car.start();
car.accelerate(60);
car.showStatus();
car.stop();
System.out.println("---");
Vehicle moto = new Motorcycle("本田");
moto.start();
moto.accelerate(80);
moto.showStatus();
moto.stop();
}
}
10.2 接口
接口是一种完全抽象的类型,定义了一组行为规范。Java 8 之后,接口可以包含默认方法和静态方法。
// 可飞行接口
interface Flyable {
void fly(); // 抽象方法
default void land() { // 默认方法(Java 8+)
System.out.println("正在降落...");
}
static int getMaxAltitude() { // 静态方法
return 10000;
}
}
// 可游泳接口
interface Swimmable {
void swim();
default void dive(int depth) {
System.out.println("下潜到 " + depth + " 米深处");
}
}
// 实现多个接口
class Duck extends Animal implements Flyable, Swimmable {
public Duck(String name, int age) {
super(name, age);
}
@Override
public void fly() {
System.out.println(name + " 鸭子展翅飞翔");
}
@Override
public void swim() {
System.out.println(name + " 鸭子在水里游泳");
}
}
class Eagle extends Animal implements Flyable {
public Eagle(String name, int age) {
super(name, age);
}
@Override
public void fly() {
System.out.println(name + " 鹰在高空翱翔");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
Duck duck = new Duck("唐老鸭", 2);
duck.eat(); // 继承自 Animal
duck.fly(); // 实现自 Flyable
duck.swim(); // 实现自 Swimmable
duck.dive(3); // 默认方法
System.out.println("---");
Eagle eagle = new Eagle("雄鹰", 5);
eagle.fly();
eagle.land(); // 接口默认方法
System.out.println("最大飞行高度: " + Flyable.getMaxAltitude() + " 米");
// 接口类型的多态
System.out.println("--- 接口多态 ---");
Flyable[] flyers = {duck, eagle};
for (Flyable f : flyers) {
f.fly();
f.land();
}
}
}
10.3 抽象类与接口的区别
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class |
interface |
| 构造方法 | 可以有 | 不能有 |
| 成员变量 | 可以有普通字段 | 只能有常量(public static final) |
| 方法 | 可以有实现 | Java 8 前只有抽象方法 |
| 继承/实现 | 单继承 | 多实现 |
| 设计目的 | "是什么"的关系 | "能做什么"的能力 |
11. 异常处理机制
11.1 异常体系结构
Java 异常体系的根类是 Throwable,它有两个子类:
- Error:严重错误,程序无法处理(如
OutOfMemoryError) - Exception:异常,程序可以处理
- 受检异常(Checked Exception):编译时检查,必须处理(如
IOException) - 非受检异常(Unchecked Exception):运行时异常(如
NullPointerException、ArrayIndexOutOfBoundsException)
- 受检异常(Checked Exception):编译时检查,必须处理(如
11.2 try-catch-finally
public class ExceptionDemo {
public static void main(String[] args) {
// 基本 try-catch
try {
int result = 10 / 0; // ArithmeticException
System.out.println("结果: " + result);
} catch (ArithmeticException e) {
System.out.println("捕获算术异常: " + e.getMessage());
}
// 多重 catch
try {
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
// 如果上面没有异常,这行会触发 NullPointerException
String str = null;
str.length();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界: " + e.getMessage());
} catch (NullPointerException e) {
System.out.println("空指针异常: " + e.getMessage());
} catch (Exception e) {
System.out.println("其他异常: " + e.getMessage());
}
// finally 块:无论是否异常都会执行
try {
System.out.println("尝试执行...");
int x = 10 / 2;
System.out.println("结果: " + x);
} catch (ArithmeticException e) {
System.out.println("异常: " + e.getMessage());
} finally {
System.out.println("finally 块总是执行(用于资源清理)");
}
}
}
11.3 try-with-resources
Java 7 引入的自动资源管理语法,自动关闭实现了 AutoCloseable 接口的资源。
import java.io.*;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 自动关闭资源
try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
PrintWriter writer = new PrintWriter(new FileWriter("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.println(line);
}
System.out.println("文件复制完成");
} catch (IOException e) {
System.out.println("IO 异常: " + e.getMessage());
}
// 无需手动关闭 reader 和 writer,自动关闭
}
}
11.4 自定义异常
// 自定义业务异常
class InsufficientBalanceException extends Exception {
private double deficit; // 缺少的金额
public InsufficientBalanceException(double balance, double amount) {
super("余额不足!当前余额: ¥" + balance + ",需要: ¥" + amount);
this.deficit = amount - balance;
}
public double getDeficit() {
return deficit;
}
}
class AccountService {
private double balance;
public AccountService(double balance) {
this.balance = balance;
}
// 声明抛出受检异常
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
throw new InsufficientBalanceException(balance, amount);
}
balance -= amount;
System.out.println("取款成功!剩余: ¥" + balance);
}
public double getBalance() {
return balance;
}
}
public class CustomExceptionDemo {
public static void main(String[] args) {
AccountService account = new AccountService(1000.0);
try {
account.withdraw(500.0); // 成功
account.withdraw(600.0); // 余额不足,抛异常
} catch (InsufficientBalanceException e) {
System.out.println("错误: " + e.getMessage());
System.out.println("还差: ¥" + e.getDeficit());
}
System.out.println("程序继续正常运行...");
}
}
12. 集合框架(List/Set/Map)
12.1 List 列表
List 是有序、可重复的集合。
import java.util.*;
public class ListDemo {
public static void main(String[] args) {
// ArrayList:基于数组,随机访问快
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橘子");
fruits.add("苹果"); // 允许重复
System.out.println("列表: " + fruits);
System.out.println("第一个: " + fruits.get(0));
System.out.println("长度: " + fruits.size());
System.out.println("包含苹果: " + fruits.contains("苹果"));
// 在指定位置插入
fruits.add(1, "葡萄");
System.out.println("插入后: " + fruits);
// 删除元素
fruits.remove("香蕉");
System.out.println("删除后: " + fruits);
// 修改元素
fruits.set(0, "红苹果");
System.out.println("修改后: " + fruits);
// 遍历方式一:for 循环
for (int i = 0; i < fruits.size(); i++) {
System.out.println(i + ": " + fruits.get(i));
}
// 遍历方式二:for-each
for (String fruit : fruits) {
System.out.println("水果: " + fruit);
}
// 遍历方式三:迭代器
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
System.out.println("迭代: " + it.next());
}
// LinkedList:基于链表,插入删除快
List<String> linked = new LinkedList<>();
linked.add("A");
linked.add("B");
linked.addFirst("首");
linked.addLast("尾");
System.out.println("LinkedList: " + linked);
}
}
12.2 Set 集合
Set 是无序、不可重复的集合。
import java.util.*;
public class SetDemo {
public static void main(String[] args) {
// HashSet:基于哈希表,无序
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
hashSet.add("Java"); // 重复,不会添加
hashSet.add("Go");
System.out.println("HashSet: " + hashSet);
System.out.println("大小: " + hashSet.size());
System.out.println("包含Java: " + hashSet.contains("Java"));
// TreeSet:基于红黑树,有序
Set<String> treeSet = new TreeSet<>();
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Cherry");
treeSet.add("Date");
System.out.println("TreeSet (有序): " + treeSet);
// 集合运算
Set<Integer> setA = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> setB = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
// 并集
Set<Integer> union = new HashSet<>(setA);
union.addAll(setB);
System.out.println("并集: " + union);
// 交集
Set<Integer> intersection = new HashSet<>(setA);
intersection.retainAll(setB);
System.out.println("交集: " + intersection);
// 差集
Set<Integer> difference = new HashSet<>(setA);
difference.removeAll(setB);
System.out.println("差集(A-B): " + difference);
// 实际应用:统计不重复单词数
String text = "Java is great and Java is popular and Java is powerful";
String[] words = text.toLowerCase().split(" ");
Set<String> uniqueWords = new HashSet<>(Arrays.asList(words));
System.out.println("不重复单词数: " + uniqueWords.size());
System.out.println("单词集合: " + uniqueWords);
}
}
12.3 Map 映射
Map 是键值对集合,键不可重复。
import java.util.*;
public class MapDemo {
public static void main(String[] args) {
// HashMap:最常用的 Map 实现
Map<String, Integer> scores = new HashMap<>();
scores.put("张三", 95);
scores.put("李四", 87);
scores.put("王五", 92);
scores.put("赵六", 78);
System.out.println("映射: " + scores);
System.out.println("张三的分数: " + scores.get("张三"));
System.out.println("包含李四: " + scores.containsKey("李四"));
System.out.println("大小: " + scores.size());
// 更新值
scores.put("张三", 98); // 覆盖旧值
System.out.println("更新后张三: " + scores.get("张三"));
// getOrDefault:键不存在时返回默认值
int score = scores.getOrDefault("未知", 0);
System.out.println("未知的分数: " + score);
// putIfAbsent:键不存在时才放入
scores.putIfAbsent("张三", 100); // 已存在,不覆盖
scores.putIfAbsent("孙七", 88); // 不存在,放入
System.out.println("putIfAbsent后: " + scores);
// 遍历方式一:entrySet
System.out.println("=== entrySet 遍历 ===");
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + " → " + entry.getValue());
}
// 遍历方式二:keySet
System.out.println("=== keySet 遍历 ===");
for (String name : scores.keySet()) {
System.out.println(name + " → " + scores.get(name));
}
// 遍历方式三:forEach (Java 8+)
System.out.println("=== forEach 遍历 ===");
scores.forEach((name, score2) ->
System.out.println(name + " 的分数是 " + score2)
);
// TreeMap:按键排序
Map<String, Integer> sorted = new TreeMap<>(scores);
System.out.println("TreeMap (按键排序): " + sorted);
// 实际应用:统计单词出现次数
String text = "apple banana apple cherry banana apple";
Map<String, Integer> wordCount = new HashMap<>();
for (String word : text.split(" ")) {
wordCount.merge(word, 1, Integer::sum);
}
System.out.println("单词统计: " + wordCount);
}
}
13. 泛型编程
13.1 泛型类
// 泛型类:通用的键值对容器
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
@Override
public String toString() {
return "(" + key + ", " + value + ")";
}
}
// 泛型栈
class GenericStack<T> {
private java.util.ArrayList<T> elements = new java.util.ArrayList<>();
public void push(T item) {
elements.add(item);
}
public T pop() {
if (elements.isEmpty()) {
throw new RuntimeException("栈为空");
}
return elements.remove(elements.size() - 1);
}
public T peek() {
if (elements.isEmpty()) {
throw new RuntimeException("栈为空");
}
return elements.get(elements.size() - 1);
}
public boolean isEmpty() {
return elements.isEmpty();
}
public int size() {
return elements.size();
}
}
public class GenericClassDemo {
public static void main(String[] args) {
// 使用泛型 Pair
Pair<String, Integer> nameAge = new Pair<>("张三", 25);
Pair<Integer, Boolean> idActive = new Pair<>(1001, true);
System.out.println("姓名-年龄: " + nameAge);
System.out.println("ID-状态: " + idActive);
// 使用泛型栈
GenericStack<Integer> intStack = new GenericStack<>();
intStack.push(10);
intStack.push(20);
intStack.push(30);
System.out.println("栈顶: " + intStack.peek()); // 30
System.out.println("弹出: " + intStack.pop()); // 30
System.out.println("弹出: " + intStack.pop()); // 20
GenericStack<String> strStack = new GenericStack<>();
strStack.push("Hello");
strStack.push("World");
System.out.println("弹出: " + strStack.pop()); // World
}
}
13.2 泛型方法
import java.util.*;
public class GenericMethodDemo {
// 泛型方法:<T> 声明在返回类型之前
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
// 泛型方法:找出数组中的最大值
public static <T extends Comparable<T>> T findMax(T[] array) {
if (array == null || array.length == 0) {
throw new IllegalArgumentException("数组为空");
}
T max = array[0];
for (T element : array) {
if (element.compareTo(max) > 0) {
max = element;
}
}
return max;
}
// 有界类型参数:T 必须是 Number 的子类
public static <T extends Number> double sum(List<T> list) {
double total = 0;
for (T num : list) {
total += num.doubleValue();
}
return total;
}
public static void main(String[] args) {
Integer[] nums = {3, 1, 4, 1, 5, 9};
String[] words = {"Hello", "World", "Java"};
Double[] decimals = {3.14, 2.71, 1.41};
System.out.print("整数数组: ");
printArray(nums);
System.out.print("字符串数组: ");
printArray(words);
System.out.println("最大整数: " + findMax(nums));
System.out.println("最大字符串: " + findMax(words));
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("整数求和: " + sum(intList));
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
System.out.println("浮点求和: " + sum(doubleList));
}
}
13.3 通配符
import java.util.*;
public class WildcardDemo {
// 上界通配符:? extends Number,只能读不能写
public static double sumOfList(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
// 下界通配符:? super Integer,可以写入 Integer 及其子类
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
// 无界通配符:?,只能读取为 Object
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
System.out.println("整数和: " + sumOfList(ints));
System.out.println("浮点和: " + sumOfList(doubles));
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);
System.out.println("添加的数字: " + numbers);
printList(ints);
printList(doubles);
printList(Arrays.asList("A", "B", "C"));
}
}
14. 字符串处理(String/StringBuilder)
14.1 String 类
Java 中的 String 是不可变对象,一旦创建内容不能修改。
public class StringDemo {
public static void main(String[] args) {
// 创建字符串
String s1 = "Hello"; // 字面量(存储在常量池)
String s2 = new String("Hello"); // 新对象(存储在堆中)
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String s3 = new String(chars);
// == 比较的是引用,equals 比较的是内容
System.out.println("s1 == s2: " + (s1 == s2)); // false
System.out.println("s1.equals(s2): " + s1.equals(s2)); // true
// 常用方法
String text = "Hello, Java World!";
System.out.println("长度: " + text.length()); // 17
System.out.println("字符at(0): " + text.charAt(0)); // H
System.out.println("子串: " + text.substring(7, 11)); // Java
System.out.println("索引of: " + text.indexOf("Java")); // 7
System.out.println("包含: " + text.contains("Java")); // true
System.out.println("转大写: " + text.toUpperCase()); // HELLO, JAVA WORLD!
System.out.println("转小写: " + text.toLowerCase()); // hello, java world!
System.out.println("替换: " + text.replace("Java", "Python")); // Hello, Python World!
System.out.println("去空格: " + " Hello ".trim()); // Hello
System.out.println("分割: " + java.util.Arrays.toString(text.split(", "))); // [Hello, Java World!]
System.out.println("是否为空: " + "".isEmpty()); // true
System.out.println("是否空白: " + " ".isBlank()); // true (Java 11+)
// 字符串比较
String a = "abc";
String b = "Abc";
System.out.println("equals: " + a.equals(b)); // false
System.out.println("equalsIgnoreCase: " + a.equalsIgnoreCase(b)); // true
System.out.println("compareTo: " + a.compareTo(b)); // 32(ASCII差值)
// 格式化
String formatted = String.format("姓名: %s, 年龄: %d, 分数: %.2f", "张三", 25, 95.5);
System.out.println(formatted);
// 连接
String joined = String.join(", ", "苹果", "香蕉", "橘子");
System.out.println("连接: " + joined); // 苹果, 香蕉, 橘子
}
}
14.2 StringBuilder
当需要频繁修改字符串时,应使用 StringBuilder,它比直接拼接 String 高效得多。
public class StringBuilderDemo {
public static void main(String[] args) {
// 创建 StringBuilder
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder("Hello");
// 追加内容
sb.append("Java");
sb.append(" ");
sb.append("编程");
sb.append(" ");
sb.append(2024);
System.out.println(sb.toString()); // Java 编程 2024
// 链式调用
sb2.append(", ").append("World").append("!");
System.out.println(sb2); // Hello, World!
// 插入
StringBuilder sb3 = new StringBuilder("Hello World");
sb3.insert(5, ", Beautiful");
System.out.println(sb3); // Hello, Beautiful World
// 删除
sb3.delete(5, 16); // 删除索引 5 到 15 的内容
System.out.println(sb3); // Hello World
// 替换
sb3.replace(6, 11, "Java");
System.out.println(sb3); // Hello Java
// 反转
StringBuilder sb4 = new StringBuilder("ABCDEF");
sb4.reverse();
System.out.println(sb4); // FEDCBA
// 性能对比:String 拼接 vs StringBuilder
long start, end;
// String 拼接(慢)
start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 100000; i++) {
str += "a"; // 每次都创建新对象
}
end = System.currentTimeMillis();
System.out.println("String 拼接耗时: " + (end - start) + "ms");
// StringBuilder(快)
start = System.currentTimeMillis();
StringBuilder sbFast = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sbFast.append("a"); // 在原对象上修改
}
end = System.currentTimeMillis();
System.out.println("StringBuilder 耗时: " + (end - start) + "ms");
}
}
14.3 StringBuffer
StringBuffer 与 StringBuilder API 相同,但 StringBuffer 是线程安全的(方法用 synchronized 修饰),适合多线程环境。单线程环境下优先使用 StringBuilder。
15. 文件I/O操作
15.1 文件读写(传统方式)
import java.io.*;
public class FileIODemo {
public static void main(String[] args) {
// 写入文件
try (PrintWriter writer = new PrintWriter(new FileWriter("output.txt"))) {
writer.println("第一行:Hello Java!");
writer.println("第二行:文件IO操作");
writer.println("第三行:学习快乐");
System.out.println("文件写入成功");
} catch (IOException e) {
System.out.println("写入失败: " + e.getMessage());
}
// 读取文件
try (BufferedReader reader = new BufferedReader(new FileReader("output.txt"))) {
String line;
int lineNum = 1;
while ((line = reader.readLine()) != null) {
System.out.println("第" + lineNum + "行: " + line);
lineNum++;
}
} catch (IOException e) {
System.out.println("读取失败: " + e.getMessage());
}
// 追加写入
try (PrintWriter writer = new PrintWriter(
new FileWriter("output.txt", true))) { // true 表示追加模式
writer.println("第四行:追加的内容");
System.out.println("追加写入成功");
} catch (IOException e) {
System.out.println("追加失败: " + e.getMessage());
}
// 字节流:复制文件
try (InputStream in = new FileInputStream("output.txt");
OutputStream out = new FileOutputStream("copy_output.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("文件复制成功");
} catch (IOException e) {
System.out.println("复制失败: " + e.getMessage());
}
}
}
15.2 NIO 文件操作(Java 7+)
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class NIOTest {
public static void main(String[] args) throws IOException {
Path path = Paths.get("nio_test.txt");
// 写入文件
Files.writeString(path, "Hello NIO!\n第二行内容\n第三行内容");
System.out.println("NIO写入成功");
// 读取全部行
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
// 读取全部内容为字符串
String content = Files.readString(path);
System.out.println("全文内容:\n" + content);
// 写入多行
List<String> newLines = List.of("第一行", "第二行", "第三行");
Files.write(Paths.get("multi_lines.txt"), newLines);
// 追加写入
Files.write(path, "\n追加的内容".getBytes(),
StandardOpenOption.APPEND);
// 文件信息
System.out.println("文件大小: " + Files.size(path) + " 字节");
System.out.println("是否存在: " + Files.exists(path));
System.out.println("是否可读: " + Files.isReadable(path));
// 创建目录
Path dir = Paths.get("test_directory");
Files.createDirectories(dir);
// 复制文件
Files.copy(path, dir.resolve("copied.txt"),
StandardCopyOption.REPLACE_EXISTING);
System.out.println("文件复制成功");
// 清理
Files.deleteIfExists(dir.resolve("copied.txt"));
Files.deleteIfExists(dir);
Files.deleteIfExists(path);
Files.deleteIfExists(Paths.get("output.txt"));
Files.deleteIfExists(Paths.get("copy_output.txt"));
Files.deleteIfExists(Paths.get("multi_lines.txt"));
}
}
16. 多线程基础
16.1 创建线程
// 方式一:继承 Thread 类
class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
this.threadName = name;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " 执行第 " + i + " 次");
try {
Thread.sleep(500); // 休眠 500ms
} catch (InterruptedException e) {
System.out.println(threadName + " 被中断");
}
}
System.out.println(threadName + " 执行完毕");
}
}
// 方式二:实现 Runnable 接口(推荐)
class PrintTask implements Runnable {
private String taskName;
public PrintTask(String name) {
this.taskName = name;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " - 步骤 " + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
return;
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 方式一:继承 Thread
MyThread t1 = new MyThread("线程A");
MyThread t2 = new MyThread("线程B");
t1.start(); // start() 启动新线程,run() 只是在当前线程执行
t2.start();
t1.join(); // 等待 t1 完成
t2.join(); // 等待 t2 完成
System.out.println("--- 方式二 ---");
// 方式二:实现 Runnable
Thread t3 = new Thread(new PrintTask("任务X"));
Thread t4 = new Thread(new PrintTask("任务Y"));
t3.start();
t4.start();
t3.join();
t4.join();
System.out.println("所有线程执行完毕");
}
}
16.2 线程同步
class BankAccount {
private int balance;
public BankAccount(int initialBalance) {
this.balance = initialBalance;
}
// synchronized 关键字确保同一时刻只有一个线程能访问此方法
public synchronized void deposit(int amount) {
balance += amount;
System.out.println(Thread.currentThread().getName() +
" 存入 " + amount + ",余额: " + balance);
}
public synchronized void withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
System.out.println(Thread.currentThread().getName() +
" 取出 " + amount + ",余额: " + balance);
} else {
System.out.println(Thread.currentThread().getName() +
" 余额不足,当前余额: " + balance);
}
}
public synchronized int getBalance() {
return balance;
}
}
public class SynchronizedDemo {
public static void main(String[] args) throws InterruptedException {
BankAccount account = new BankAccount(1000);
// 多个线程同时操作账户
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account.deposit(100);
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}, "存款线程");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account.withdraw(150);
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}, "取款线程");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终余额: " + account.getBalance());
}
}
16.3 线程池
import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;
public class ThreadPoolDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建固定大小的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 提交任务
for (int i = 1; i <= 8; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("任务 " + taskId + " 由 " +
Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
return;
}
return;
});
}
// 提交有返回值的任务
Future<Integer> future = pool.submit(() -> {
Thread.sleep(1000);
return 42;
});
System.out.println("异步结果: " + future.get()); // 阻塞等待结果
// 关闭线程池
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("线程池已关闭");
}
}
17. Lambda 表达式
17.1 Lambda 基础
Lambda 表达式是 Java 8 引入的重要特性,用于简洁地表示匿名函数。
import java.util.*;
import java.util.function.*;
public class LambdaDemo {
public static void main(String[] args) {
// 传统方式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("传统方式");
}
};
// Lambda 方式
Runnable r2 = () -> System.out.println("Lambda 方式");
// 多行 Lambda
Runnable r3 = () -> {
System.out.println("多行 Lambda");
System.out.println("可以包含多条语句");
};
r1.run();
r2.run();
r3.run();
// 带参数的 Lambda
List<String> names = Arrays.asList("Charlie", "Alice", "Bob", "David");
// 排序
Collections.sort(names, (a, b) -> a.compareTo(b));
System.out.println("排序后: " + names);
// 方法引用(Lambda 的简写形式)
names.sort(String::compareToIgnoreCase);
System.out.println("忽略大小写排序: " + names);
}
}
17.2 函数式接口
函数式接口是只有一个抽象方法的接口,可以用 Lambda 表达式来实现。
import java.util.function.*;
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Predicate<T>:接受 T,返回 boolean
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
System.out.println("空字符串: " + isEmpty.test("")); // true
System.out.println("非空字符串: " + isNotEmpty.test("Hi")); // true
// Function<T, R>:接受 T,返回 R
Function<String, Integer> strLength = String::length;
Function<Integer, String> intToStr = String::valueOf;
System.out.println("长度: " + strLength.apply("Hello")); // 5
// 函数组合
Function<String, String> toUpperThenTrim = String::trim;
System.out.println(toUpperThenTrim.apply(" Hello "));
// Consumer<T>:接受 T,无返回值
Consumer<String> printer = System.out::println;
printer.accept("消费型接口示例");
// Supplier<T>:无参数,返回 T
Supplier<Double> randomSupplier = Math::random;
System.out.println("随机数: " + randomSupplier.get());
// UnaryOperator<T>:接受 T,返回 T
UnaryOperator<String> toUpper = String::toUpperCase;
System.out.println("大写: " + toUpper.apply("hello"));
// BiFunction<T, U, R>:接受 T 和 U,返回 R
BiFunction<Integer, Integer, Integer> add = Integer::sum;
System.out.println("求和: " + add.apply(3, 5)); // 8
// 实际应用:列表过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evens = filter(numbers, n -> n % 2 == 0);
System.out.println("偶数: " + evens);
List<String> words = Arrays.asList("Hello", "", "World", "", "Java");
List<String> nonEmpty = filter(words, s -> !s.isEmpty());
System.out.println("非空: " + nonEmpty);
}
// 泛型过滤方法
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (predicate.test(item)) {
result.add(item);
}
}
return result;
}
}
18. Stream API
18.1 Stream 基础操作
Stream API 是 Java 8 引入的用于处理集合数据的抽象,支持链式操作和惰性求值。
import java.util.*;
import java.util.stream.*;
public class StreamDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList(
"张三", "李四", "王五", "赵六", "孙七",
"周八", "吴九", "郑十", "张伟", "李娜"
);
// filter:过滤
System.out.println("=== 过滤 ===");
List<String> zhangNames = names.stream()
.filter(name -> name.startsWith("张"))
.collect(Collectors.toList());
System.out.println("姓张的: " + zhangNames);
// map:转换
System.out.println("=== 转换 ===");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("名字长度: " + nameLengths);
// sorted:排序
System.out.println("=== 排序 ===");
List<String> sorted = names.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("排序后: " + sorted);
// distinct:去重
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 5);
List<Integer> distinct = nums.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("去重: " + distinct);
// limit 和 skip
System.out.println("=== limit & skip ===");
List<String> first3 = names.stream().limit(3).collect(Collectors.toList());
System.out.println("前3个: " + first3);
List<String> skip5 = names.stream().skip(5).collect(Collectors.toList());
System.out.println("跳过前5个: " + skip5);
// forEach
System.out.println("=== forEach ===");
names.stream()
.filter(n -> n.length() == 2)
.forEach(n -> System.out.println("两字名: " + n));
}
}
18.2 Stream 终结操作
import java.util.*;
import java.util.stream.*;
public class StreamTerminalDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// collect:收集为集合
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("偶数: " + evens);
// 收集为 Set
Set<Integer> evenSet = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toSet());
System.out.println("偶数集合: " + evenSet);
// reduce:归约
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("总和: " + sum);
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
System.out.println("最大值: " + max.orElse(0));
// count:计数
long count = numbers.stream()
.filter(n -> n > 5)
.count();
System.out.println("大于5的个数: " + count);
// anyMatch, allMatch, noneMatch
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);
boolean allPositive = numbers.stream().allMatch(n -> n > 0);
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);
System.out.println("有偶数: " + hasEven);
System.out.println("全为正: " + allPositive);
System.out.println("无负数: " + noneNegative);
// findFirst, findAny
Optional<Integer> first = numbers.stream()
.filter(n -> n > 5)
.findFirst();
System.out.println("第一个大于5的: " + first.orElse(-1));
// 统计汇总
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println("统计 - 总和: " + stats.getSum());
System.out.println("统计 - 平均: " + stats.getAverage());
System.out.println("统计 - 最大: " + stats.getMax());
System.out.println("统计 - 最小: " + stats.getMin());
System.out.println("统计 - 计数: " + stats.getCount());
}
}
18.3 Collectors 高级用法
import java.util.*;
import java.util.stream.*;
class Employee {
String name;
String department;
double salary;
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
@Override
public String toString() {
return name + "(" + department + ": ¥" + salary + ")";
}
}
public class CollectorsDemo {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("张三", "技术部", 15000),
new Employee("李四", "技术部", 18000),
new Employee("王五", "市场部", 12000),
new Employee("赵六", "市场部", 14000),
new Employee("孙七", "技术部", 20000),
new Employee("周八", "人事部", 11000)
);
// 分组:按部门分组
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(e -> e.department));
System.out.println("=== 按部门分组 ===");
byDept.forEach((dept, list) ->
System.out.println(dept + ": " + list));
// 分组统计
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(e -> e.department, Collectors.counting()));
System.out.println("=== 各部门人数 ===");
System.out.println(countByDept);
// 分组求和
Map<String, Double> salaryByDept = employees.stream()
.collect(Collectors.groupingBy(
e -> e.department,
Collectors.summingDouble(e -> e.salary)));
System.out.println("=== 各部门薪资总和 ===");
System.out.println(salaryByDept);
// 分组求平均
Map<String, Double> avgByDept = employees.stream()
.collect(Collectors.groupingBy(
e -> e.department,
Collectors.averagingDouble(e -> e.salary)));
System.out.println("=== 各部门平均薪资 ===");
avgByDept.forEach((dept, avg) ->
System.out.println(dept + ": ¥" + String.format("%.2f", avg)));
// 连接字符串
String nameList = employees.stream()
.map(e -> e.name)
.collect(Collectors.joining(", ", "[", "]"));
System.out.println("员工名单: " + nameList);
// 分区(按条件分为两组)
Map<Boolean, List<Employee>> partitioned = employees.stream()
.collect(Collectors.partitioningBy(e -> e.salary >= 15000));
System.out.println("高薪(>=15000): " + partitioned.get(true));
System.out.println("低薪(<15000): " + partitioned.get(false));
}
}
19. 常用设计模式
19.1 单例模式
确保一个类只有一个实例,并提供全局访问点。
// 方式一:饿汉式(线程安全,推荐)
class SingletonHungry {
private static final SingletonHungry INSTANCE = new SingletonHungry();
private SingletonHungry() {} // 私有构造
public static SingletonHungry getInstance() {
return INSTANCE;
}
public void showMessage() {
System.out.println("饿汉式单例");
}
}
// 方式二:懒汉式(双重检查锁)
class SingletonLazy {
private static volatile SingletonLazy instance;
private SingletonLazy() {}
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
// 方式三:枚举单例(最安全,推荐)
enum SingletonEnum {
INSTANCE;
public void showMessage() {
System.out.println("枚举单例");
}
}
public class SingletonDemo {
public static void main(String[] args) {
SingletonHungry s1 = SingletonHungry.getInstance();
SingletonHungry s2 = SingletonHungry.getInstance();
System.out.println("s1 == s2: " + (s1 == s2)); // true
s1.showMessage();
SingletonEnum.INSTANCE.showMessage();
}
}
19.2 工厂模式
将对象的创建逻辑封装在工厂类中,客户端无需知道具体的创建细节。
// 产品接口
interface Shape {
void draw();
}
class CircleShape implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形 ⭕");
}
}
class SquareShape implements Shape {
@Override
public void draw() {
System.out.println("绘制正方形 ⬜");
}
}
class TriangleShape implements Shape {
@Override
public void draw() {
System.out.println("绘制三角形 🔺");
}
}
// 简单工厂
class ShapeFactory {
public static Shape createShape(String type) {
return switch (type.toLowerCase()) {
case "circle" -> new CircleShape();
case "square" -> new SquareShape();
case "triangle" -> new TriangleShape();
default -> throw new IllegalArgumentException("未知形状: " + type);
};
}
}
public class FactoryDemo {
public static void main(String[] args) {
Shape circle = ShapeFactory.createShape("circle");
Shape square = ShapeFactory.createShape("square");
Shape triangle = ShapeFactory.createShape("triangle");
circle.draw();
square.draw();
triangle.draw();
}
}
19.3 观察者模式
定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。
import java.util.*;
// 观察者接口
interface Observer {
void update(String message);
}
// 被观察者(主题)
class NewsPublisher {
private List<Observer> subscribers = new ArrayList<>();
private String name;
public NewsPublisher(String name) {
this.name = name;
}
public void subscribe(Observer observer) {
subscribers.add(observer);
System.out.println("新订阅者加入: " + observer);
}
public void unsubscribe(Observer observer) {
subscribers.remove(observer);
System.out.println("取消订阅: " + observer);
}
public void publish(String news) {
System.out.println("\n[" + name + "] 发布新闻: " + news);
for (Observer observer : subscribers) {
observer.update(news);
}
}
}
// 具体观察者
class NewsReader implements Observer {
private String name;
public NewsReader(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(" " + name + " 收到新闻: " + message);
}
@Override
public String toString() {
return name;
}
}
public class ObserverDemo {
public static void main(String[] args) {
NewsPublisher publisher = new NewsPublisher("环球日报");
NewsReader reader1 = new NewsReader("张三");
NewsReader reader2 = new NewsReader("李四");
NewsReader reader3 = new NewsReader("王五");
publisher.subscribe(reader1);
publisher.subscribe(reader2);
publisher.subscribe(reader3);
publisher.publish("Java 21 正式发布!");
publisher.publish("Spring Boot 3.2 新特性解读");
publisher.unsubscribe(reader2);
publisher.publish("人工智能最新进展");
}
}
19.4 策略模式
定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
// 策略接口
interface SortStrategy {
void sort(int[] array);
String getName();
}
class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
int n = array.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
@Override
public String getName() { return "冒泡排序"; }
}
class SelectionSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
int n = array.length;
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (array[j] < array[minIdx]) {
minIdx = j;
}
}
int temp = array[minIdx];
array[minIdx] = array[i];
array[i] = temp;
}
}
@Override
public String getName() { return "选择排序"; }
}
class Sorter {
private SortStrategy strategy;
public Sorter(SortStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sort(int[] array) {
System.out.println("使用 " + strategy.getName() + " 排序...");
strategy.sort(array);
}
}
public class StrategyDemo {
public static void main(String[] args) {
int[] data1 = {64, 25, 12, 22, 11};
int[] data2 = data1.clone();
Sorter sorter = new Sorter(new BubbleSortStrategy());
sorter.sort(data1);
System.out.println("结果: " + java.util.Arrays.toString(data1));
sorter.setStrategy(new SelectionSortStrategy());
sorter.sort(data2);
System.out.println("结果: " + java.util.Arrays.toString(data2));
}
}
20. 实战:学生管理系统
这是一个综合运用前面所学知识的完整项目,实现学生信息的增删改查功能。
import java.util.*;
import java.util.stream.*;
// 学生实体类
class StudentInfo implements Comparable<StudentInfo> {
private static int idCounter = 1000;
private int id;
private String name;
private int age;
private String major;
private Map<String, Double> scores; // 课程 -> 分数
public StudentInfo(String name, int age, String major) {
this.id = ++idCounter;
this.name = name;
this.age = age;
this.major = major;
this.scores = new HashMap<>();
}
// Getters
public int getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
public String getMajor() { return major; }
public Map<String, Double> getScores() { return scores; }
// Setters
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
public void setMajor(String major) { this.major = major; }
// 添加成绩
public void addScore(String subject, double score) {
scores.put(subject, score);
}
// 计算平均分
public double getAverage() {
if (scores.isEmpty()) return 0;
return scores.values().stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0);
}
@Override
public int compareTo(StudentInfo other) {
return Integer.compare(this.id, other.id);
}
@Override
public String toString() {
return String.format("学号:%d | 姓名:%-4s | 年龄:%d | 专业:%-8s | 平均分:%.1f",
id, name, age, major, getAverage());
}
}
// 学生管理系统
class StudentManager {
private Map<Integer, StudentInfo> students;
public StudentManager() {
this.students = new TreeMap<>();
}
// 添加学生
public void addStudent(StudentInfo student) {
students.put(student.getId(), student);
System.out.println("✅ 成功添加学生: " + student.getName() + " (学号: " + student.getId() + ")");
}
// 删除学生
public boolean removeStudent(int id) {
StudentInfo removed = students.remove(id);
if (removed != null) {
System.out.println("✅ 已删除学生: " + removed.getName());
return true;
}
System.out.println("❌ 未找到学号为 " + id + " 的学生");
return false;
}
// 查找学生
public StudentInfo findStudent(int id) {
return students.get(id);
}
// 按姓名模糊查找
public List<StudentInfo> searchByName(String keyword) {
return students.values().stream()
.filter(s -> s.getName().contains(keyword))
.collect(Collectors.toList());
}
// 添加成绩
public boolean addScore(int id, String subject, double score) {
StudentInfo student = students.get(id);
if (student != null) {
student.addScore(subject, score);
System.out.println("✅ 已为 " + student.getName() + " 添加 " +
subject + " 成绩: " + score);
return true;
}
System.out.println("❌ 未找到学号为 " + id + " 的学生");
return false;
}
// 显示所有学生
public void showAllStudents() {
if (students.isEmpty()) {
System.out.println("📋 暂无学生信息");
return;
}
System.out.println("\n========== 学生列表 ==========");
students.values().forEach(System.out::println);
System.out.println("================================\n");
}
// 按平均分排序显示
public void showStudentsByAverage() {
System.out.println("\n===== 按平均分排序 =====");
students.values().stream()
.sorted(Comparator.comparingDouble(StudentInfo::getAverage).reversed())
.forEach(s -> System.out.printf(" %-4s 平均分: %.1f%n",
s.getName(), s.getAverage()));
}
// 统计信息
public void showStatistics() {
if (students.isEmpty()) {
System.out.println("暂无数据");
return;
}
DoubleSummaryStatistics stats = students.values().stream()
.mapToDouble(StudentInfo::getAverage)
.summaryStatistics();
System.out.println("\n===== 统计信息 =====");
System.out.println("学生总数: " + students.size());
System.out.printf("最高平均分: %.1f%n", stats.getMax());
System.out.printf("最低平均分: %.1f%n", stats.getMin());
System.out.printf("整体平均分: %.1f%n", stats.getAverage());
// 按专业统计
System.out.println("\n按专业统计人数:");
students.values().stream()
.collect(Collectors.groupingBy(StudentInfo::getMajor, Collectors.counting()))
.forEach((major, count) ->
System.out.println(" " + major + ": " + count + " 人"));
}
}
public class StudentSystemDemo {
public static void main(String[] args) {
StudentManager manager = new StudentManager();
// 添加学生
StudentInfo s1 = new StudentInfo("张三", 20, "计算机科学");
StudentInfo s2 = new StudentInfo("李四", 21, "软件工程");
StudentInfo s3 = new StudentInfo("王五", 19, "计算机科学");
StudentInfo s4 = new StudentInfo("赵六", 22, "数据科学");
StudentInfo s5 = new StudentInfo("孙七", 20, "软件工程");
manager.addStudent(s1);
manager.addStudent(s2);
manager.addStudent(s3);
manager.addStudent(s4);
manager.addStudent(s5);
// 添加成绩
manager.addScore(s1.getId(), "Java编程", 92);
manager.addScore(s1.getId(), "数据结构", 88);
manager.addScore(s1.getId(), "数据库", 95);
manager.addScore(s2.getId(), "Java编程", 85);
manager.addScore(s2.getId(), "数据结构", 90);
manager.addScore(s2.getId(), "数据库", 78);
manager.addScore(s3.getId(), "Java编程", 78);
manager.addScore(s3.getId(), "数据结构", 82);
manager.addScore(s3.getId(), "数据库", 88);
manager.addScore(s4.getId(), "Java编程", 95);
manager.addScore(s4.getId(), "数据结构", 92);
manager.addScore(s5.getId(), "Java编程", 88);
manager.addScore(s5.getId(), "数据库", 91);
// 显示所有学生
manager.showAllStudents();
// 按平均分排序
manager.showStudentsByAverage();
// 查找
System.out.println("\n===== 查找测试 =====");
System.out.println("查找学号1001: " + manager.findStudent(1001));
System.out.println("搜索'张': " + manager.searchByName("张"));
// 统计
manager.showStatistics();
// 删除
manager.removeStudent(1004);
manager.showAllStudents();
}
}
21. 实战:简易图书管理
另一个综合实战项目,实现图书的借阅管理功能。
import java.util.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.stream.*;
// 图书类
class Book {
private String isbn;
private String title;
private String author;
private boolean available;
private String borrower;
private LocalDate borrowDate;
public Book(String isbn, String title, String author) {
this.isbn = isbn;
this.title = title;
this.author = author;
this.available = true;
}
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
public boolean isAvailable() { return available; }
public String getBorrower() { return borrower; }
public LocalDate getBorrowDate() { return borrowDate; }
public boolean borrow(String borrowerName) {
if (!available) {
return false;
}
this.available = false;
this.borrower = borrowerName;
this.borrowDate = LocalDate.now();
return true;
}
public boolean returnBook() {
if (available) {
return false;
}
this.available = true;
this.borrower = null;
this.borrowDate = null;
return true;
}
@Override
public String toString() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
if (available) {
return String.format("[%s] %s - %s (可借)", isbn, title, author);
} else {
return String.format("[%s] %s - %s (借出 → %s, %s)",
isbn, title, author, borrower, borrowDate.format(fmt));
}
}
}
// 图书管理系统
class LibraryManager {
private Map<String, Book> books; // ISBN -> Book
private Map<String, List<String>> readerHistory; // 读者 -> 借阅历史
public LibraryManager() {
this.books = new LinkedHashMap<>();
this.readerHistory = new HashMap<>();
}
// 添加图书
public void addBook(Book book) {
books.put(book.getIsbn(), book);
System.out.println("📗 入库: " + book.getTitle());
}
// 删除图书
public boolean removeBook(String isbn) {
Book removed = books.remove(isbn);
if (removed != null) {
System.out.println("📕 已下架: " + removed.getTitle());
return true;
}
System.out.println("❌ 未找到ISBN: " + isbn);
return false;
}
// 借书
public boolean borrowBook(String isbn, String readerName) {
Book book = books.get(isbn);
if (book == null) {
System.out.println("❌ 未找到图书");
return false;
}
if (book.borrow(readerName)) {
readerHistory.computeIfAbsent(readerName, k -> new ArrayList<>())
.add(isbn);
System.out.println("✅ " + readerName + " 借阅成功: " + book.getTitle());
return true;
}
System.out.println("❌ 该书已被借出");
return false;
}
// 还书
public boolean returnBook(String isbn) {
Book book = books.get(isbn);
if (book == null) {
System.out.println("❌ 未找到图书");
return false;
}
if (book.returnBook()) {
System.out.println("✅ 归还成功: " + book.getTitle());
return true;
}
System.out.println("❌ 该书未被借出");
return false;
}
// 搜索图书
public List<Book> searchBooks(String keyword) {
String lower = keyword.toLowerCase();
return books.values().stream()
.filter(b -> b.getTitle().toLowerCase().contains(lower) ||
b.getAuthor().toLowerCase().contains(lower) ||
b.getIsbn().contains(keyword))
.collect(Collectors.toList());
}
// 显示所有图书
public void showAllBooks() {
if (books.isEmpty()) {
System.out.println("📚 图书馆暂无藏书");
return;
}
System.out.println("\n========= 图书馆藏书 =========");
books.values().forEach(b -> System.out.println(" " + b));
System.out.println("==============================\n");
}
// 显示可借图书
public void showAvailableBooks() {
System.out.println("\n===== 可借图书 =====");
books.values().stream()
.filter(Book::isAvailable)
.forEach(b -> System.out.println(" " + b));
}
// 显示已借出图书
public void showBorrowedBooks() {
System.out.println("\n===== 已借出图书 =====");
books.values().stream()
.filter(b -> !b.isAvailable())
.forEach(b -> System.out.println(" " + b));
}
// 读者借阅历史
public void showReaderHistory(String readerName) {
List<String> history = readerHistory.get(readerName);
if (history == null || history.isEmpty()) {
System.out.println(readerName + " 暂无借阅记录");
return;
}
System.out.println("\n===== " + readerName + " 的借阅历史 =====");
history.stream()
.map(books::get)
.filter(Objects::nonNull)
.forEach(b -> System.out.println(" " + b.getTitle() + " - " + b.getAuthor()));
}
// 统计信息
public void showStatistics() {
long total = books.size();
long available = books.values().stream().filter(Book::isAvailable).count();
long borrowed = total - available;
System.out.println("\n===== 图书馆统计 =====");
System.out.println("总藏书: " + total + " 本");
System.out.println("可借: " + available + " 本");
System.out.println("借出: " + borrowed + " 本");
System.out.println("借阅率: " + String.format("%.1f%%", (double) borrowed / total * 100));
// 按作者统计
System.out.println("\n按作者统计:");
books.values().stream()
.collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.forEach(e -> System.out.println(" " + e.getKey() + ": " + e.getValue() + " 本"));
}
}
public class LibraryDemo {
public static void main(String[] args) {
LibraryManager library = new LibraryManager();
// 添加图书
library.addBook(new Book("978-7-111-40701-0", "Java编程思想", "Bruce Eckel"));
library.addBook(new Book("978-7-111-21382-6", "Effective Java", "Joshua Bloch"));
library.addBook(new Book("978-7-115-41730-8", "Spring实战", "Craig Walls"));
library.addBook(new Book("978-7-111-54106-0", "设计模式", "GoF"));
library.addBook(new Book("978-7-111-54706-5", "深入理解Java虚拟机", "周志明"));
library.addBook(new Book("978-7-115-47544-5", "算法导论", "Thomas Cormen"));
// 显示所有图书
library.showAllBooks();
// 借书
System.out.println("=== 借书操作 ===");
library.borrowBook("978-7-111-40701-0", "张三");
library.borrowBook("978-7-111-54106-0", "张三");
library.borrowBook("978-7-111-21382-6", "李四");
library.borrowBook("978-7-111-40701-0", "王五"); // 已被借出
// 显示状态
library.showBorrowedBooks();
library.showAvailableBooks();
// 搜索
System.out.println("\n=== 搜索测试 ===");
System.out.println("搜索 'Java':");
library.searchBooks("Java").forEach(b -> System.out.println(" " + b));
// 还书
System.out.println("\n=== 还书操作 ===");
library.returnBook("978-7-111-40701-0");
// 再次查看
library.showAvailableBooks();
// 读者历史
library.showReaderHistory("张三");
// 统计
library.showStatistics();
}
}
22. 调试技巧与最佳实践
22.1 常见调试技巧
使用 System.out.println 调试
最简单的调试方法,适合快速定位问题:
public class DebugPrintDemo {
public static int calculate(int a, int b) {
System.out.println("[DEBUG] calculate called with a=" + a + ", b=" + b);
int result = a * b + a;
System.out.println("[DEBUG] calculate result=" + result);
return result;
}
public static void main(String[] args) {
int x = calculate(5, 3);
System.out.println("最终结果: " + x);
}
}
使用日志框架
在实际项目中,推荐使用 SLF4J + Logback 等日志框架:
// 实际项目中的日志使用方式(需要引入依赖)
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
// public class UserService {
// private static final Logger logger = LoggerFactory.getLogger(UserService.class);
//
// public void processUser(String userId) {
// logger.info("处理用户: {}", userId);
// try {
// // 业务逻辑
// logger.debug("用户处理完成: {}", userId);
// } catch (Exception e) {
// logger.error("处理用户失败: {}", userId, e);
// }
// }
// }
使用断言
public class AssertDemo {
public static int divide(int a, int b) {
assert b != 0 : "除数不能为零!";
return a / b;
}
public static void main(String[] args) {
// 需要启用断言:java -ea AssertDemo
try {
System.out.println(divide(10, 2)); // 正常
System.out.println(divide(10, 0)); // 触发断言
} catch (AssertionError e) {
System.out.println("断言失败: " + e.getMessage());
}
}
}
22.2 常见错误排查
public class CommonErrorsDemo {
public static void main(String[] args) {
// 1. NullPointerException 空指针异常
String str = null;
// 错误方式:str.length();
// 正确方式:
if (str != null) {
System.out.println(str.length());
}
// 或使用 Optional
int length = Optional.ofNullable(str).map(String::length).orElse(0);
System.out.println("长度: " + length);
// 2. ArrayIndexOutOfBoundsException 数组越界
int[] arr = {1, 2, 3};
// 错误方式:arr[5]
// 正确方式:
int index = 5;
if (index >= 0 && index < arr.length) {
System.out.println(arr[index]);
} else {
System.out.println("索引越界");
}
// 3. NumberFormatException 数字格式异常
String numStr = "abc";
try {
int num = Integer.parseInt(numStr);
} catch (NumberFormatException e) {
System.out.println("无法解析数字: " + numStr);
}
// 4. ConcurrentModificationException 并发修改异常
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
// 错误方式:在 for-each 中删除元素
// 正确方式:使用 Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.equals("B")) {
it.remove(); // 安全删除
}
}
System.out.println("删除后: " + list);
// 5. ClassCastException 类型转换异常
Object obj = "Hello";
// 错误方式:Integer num = (Integer) obj;
// 正确方式:使用 instanceof 检查
if (obj instanceof String s) {
System.out.println("是字符串: " + s);
}
}
}
22.3 Java 编程最佳实践
import java.util.*;
public class BestPracticesDemo {
// 1. 优先使用接口类型声明
// 好:
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
Set<Integer> set = new HashSet<>();
// 不好:
// ArrayList<String> list2 = new ArrayList<>();
// 2. 使用 StringBuilder 拼接字符串
public static String buildString(List<String> items) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
if (i > 0) sb.append(", ");
sb.append(items.get(i));
}
return sb.toString();
}
// 3. 使用常量避免魔法数字
public static final int MAX_RETRY_COUNT = 3;
public static final double TAX_RATE = 0.13;
public static final String DATE_FORMAT = "yyyy-MM-dd";
// 4. 使用 Objects 工具类
public static void objectsExample() {
// null 安全的 equals
String a = null;
String b = "hello";
boolean equal = Objects.equals(a, b); // false,不会 NPE
// null 检查
Objects.requireNonNull(b, "参数不能为空");
// hashCode
int hash = Objects.hash("张三", 25);
}
// 5. 使用 Optional 避免 null
public static Optional<String> findUser(int id) {
if (id > 0) {
return Optional.of("用户" + id);
}
return Optional.empty();
}
// 6. 使用不可变集合
public static void immutableCollections() {
// Java 9+ 不可变集合工厂方法
List<String> immutableList = List.of("A", "B", "C");
Map<String, Integer> immutableMap = Map.of("key1", 1, "key2", 2);
Set<String> immutableSet = Set.of("X", "Y", "Z");
// 不可变,修改会抛出 UnsupportedOperationException
// immutableList.add("D"); // 异常!
// Collections.unmodifiableXxx 方式
List<String> mutable = new ArrayList<>(Arrays.asList("1", "2", "3"));
List<String> unmodifiable = Collections.unmodifiableList(mutable);
}
// 7. 规范的 equals 和 hashCode 重写
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
// 8. 异常处理最佳实践
public static void bestPracticeException() {
try {
// 具体操作
int result = 10 / 0;
} catch (ArithmeticException e) {
// 捕获具体异常,不要用 catch (Exception e)
System.out.println("算术错误: " + e.getMessage());
} finally {
// 资源清理
}
}
// 9. 使用 var 局部变量类型推断(Java 10+)
public static void varExample() {
var name = "张三"; // 推断为 String
var numbers = List.of(1, 2, 3); // 推断为 List<Integer>
var map = new HashMap<String, List<Integer>>();
// var 只能用于局部变量
// 不能用于方法参数、返回类型、字段
}
public static void main(String[] args) {
// Optional 使用示例
Optional<String> user = findUser(1);
user.ifPresent(u -> System.out.println("找到用户: " + u));
String name = findUser(-1).orElse("默认用户");
System.out.println("用户: " + name);
// buildString 示例
List<String> items = Arrays.asList("苹果", "香蕉", "橘子");
System.out.println("拼接结果: " + buildString(items));
// 不可变集合
immutableCollections();
// User equals/hashCode
User u1 = new User("张三", 25);
User u2 = new User("张三", 25);
System.out.println("equals: " + u1.equals(u2)); // true
Set<User> userSet = new HashSet<>();
userSet.add(u1);
System.out.println("contains: " + userSet.contains(u2)); // true
System.out.println("\n🎉 恭喜!你已完成 Java 基础编程教程的全部学习!");
System.out.println("建议继续学习:");
System.out.println(" 1. JDBC 数据库编程");
System.out.println(" 2. Spring Boot 框架");
System.out.println(" 3. Maven/Gradle 构建工具");
System.out.println(" 4. 单元测试(JUnit)");
System.out.println(" 5. 设计模式深入");
}
}
总结
本教程从 Java 的历史讲起,系统介绍了 Java 编程的各个方面:
- 基础语法:数据类型、变量、运算符、控制流
- 面向对象:类、对象、封装、继承、多态、抽象类、接口
- 核心特性:异常处理、集合框架、泛型、字符串处理
- 进阶特性:文件 I/O、多线程、Lambda、Stream API
- 设计模式:单例、工厂、观察者、策略
- 实战项目:学生管理系统、图书管理系统
学习编程最重要的是动手实践。建议按照教程中的代码示例逐个编写运行,理解每一行代码的含义,然后尝试修改和扩展。遇到问题时,善用搜索引擎和官方文档,这是每个程序员的必备技能。
下一步学习建议:
- 学习 Maven/Gradle 项目构建工具
- 学习 JUnit 单元测试
- 深入学习 Spring/Spring Boot 框架
- 学习 JDBC 和数据库编程
- 探索 Java 并发编程高级主题
- 阅读《Effective Java》等经典书籍
祝你在 Java 编程的道路上越走越远!🚀