发布于 

Java 和 OOP 入门笔记

(部分题目内容和判题程序由 AI 生成,其余内容为手写)

技术栈扩展

学网安的要精通前后端、二进制才能游刃有余,前后端工程师纷纷在往全栈跑,AI 发展起来了大家又一窝蜂往深度学习钻,Vibe Coding 的同质化和垃圾的产出又迫使着每一个人学会 UI 设计和产品经理思维,现在的 CS 就是一人成军,如果你不多学几门技术栈,那么在 AI 发展起来的未来真的是毕业即失业。

由于我在 2026.9 的时候参加了学校社团的招新,了解了前端、ML 和网安的一些内容,学校(至少培养方案是这么说的)会涉及到 C 语言和更底层的东西,而 PM 思维需要在真实环境中不断打磨,那么最好的起步路线就是学习后端。

怎么学

由于我之前完全没有接触过 Java,面向对象编程也有点忘了,然后现在 AI 又非常厉害,我想探索一下 AI-based Learning。

具体而言,我会在 Gemini 的指导和帮助下实现几个示例。

前备知识

磨刀

我忘了多久开始用的 VSC 写代码,但是我一开始用 VSC 之后就停不下来了,我几乎所有代码都是在这里写的,包括我的博客文章也是在 VSC 里面写的。

但是 VSC 仍然有其局限性。所以我换成了 IDEA Ultimate。学生有一年的使用资格,如果不是学生也可以发动鬼脑(ckey.run)自己找。不得不说,IDEA 还是对 Java 有很强的语言适配,会体现在很多小细节上面。所以如果写 Java 项目的话还是老老实实用 IDE 吧,重是重了点,但是从使用体验上而言是值得的。

OOP 和 POP

POP 是“先做什么,后做什么”,核心是函数,数据和函数分离。

OOP 是“谁怎么样,谁干什么”,核心是类与对象,对象本身就是一系列数据和行为的结合体。

Java 与 JavaScript 的一些重要区别

  1. Java 有编译的过程。Java 离不开 JVM,JavaScript 离不开 V8(或者其他引擎),看似两者都需要操作系统特定的一个底层工具跑代码,但是也有分别。Java 有一个编译过程,其编译的结果是字节码,字节码在 JVM 上最终翻译成机器码。
  2. Java 有方法重载。这一点在后面会解释。
  3. Java 是强类型的。需要了解 Java 的一些类型和不同的作用机制,防止类型不匹配出现错误。
  4. OOP 和 POP。见上。
  5. 空值处理。Java 没有 undefined,同时如果访问 null 会报 NullPointerException。
  6. 判等。注意双等号和三等号,以及 .equals() 的使用。
  7. 异步。不同于 JS 的 async/await 或者 Promise,Java 默认同步,但是可以采用线程池机制实现异步。

Lab 1 从零开始的矩阵

你需要使用纯 Java 编写一个基础的 Matrix 类,实现矩阵的底层数据结构定义,并封装矩阵的格式化打印、矩阵转置、矩阵加法、数乘以及核心的矩阵乘法运算,以此迈出从面向过程到高级面向对象编程(OOP)的第一步。

了解项目结构

建立项目之后在 Main.java 里面。但是新的类应当分开(“低耦合”),于是 src 新建一个 Java 类,填入名字 Matrix。之后矩阵的代码就在 Matrix.java 里面写。Main.java 里面就写一个调用矩阵完成各项运算的小程序吧。

需要注意同包内互相引用不需要 import,直接写类名就行。如果是 JS 就需要用 module.export 以及 require。

矩阵需要有什么

一个类需要有属性(字段)、构造器(类的初始化)、方法(功能)。对于矩阵,主要就是行数列数和里面的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Matrix {
// 属性
int rows;
int cols;
double[][] data;

// 构造器
public Matrix(int r, int c) {
this.rows = r;
this.cols = c;
data = new double[r][c];
}

// 方法
public void print(){
// 具体实现
}
}

实现方法

这里主要考虑怎么把数学语言翻译成程序语言。

this 的运用

  1. 用 this.rows = rows 区分参数与变量
  2. 在二元运算中,明确区分主调方 this.data[i][j] 与传入对象 m.data[i][j],防止误写为“自身加自身”。

方法重载

同一个类里,方法名相同、参数列表不同。编译器会自行决定用哪个方法。在实现数乘和向量乘法的时候就可以写在一起。

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class Matrix {
// 属性
int rows;
int cols;
double[][] data;

// 构造器(初始化类)
public Matrix(int r, int c) {
this.rows = r;
this.cols = c;
data = new double[r][c];
}

// 方法
public void print() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
System.out.print(data[i][j] + "\t");
}
System.out.println();
}
System.out.println("为 " + this.rows + " x " + this.cols + " 矩阵");
}

public void transpose() {
double[][] originalData = this.data; // 存取老数据
this.data = new double[this.cols][this.rows]; // 建新的 data
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
data[j][i] = originalData[i][j];
}
}
int tmp = this.rows;
this.rows = this.cols;
this.cols = tmp;
}

public Matrix add(Matrix m) {
if (this.rows != m.rows || this.cols != m.cols) {
throw new IllegalArgumentException("矩阵不同型,无法相加");
}
Matrix result = new Matrix(this.rows, m.cols);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.cols; j++) {
result.data[i][j] = this.data[i][j] + m.data[i][j];
}
}
return result;
}

public Matrix multiply(double k) {
Matrix result = new Matrix(this.rows, this.cols);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.cols; j++) {
result.data[i][j] = this.data[i][j] * k;
}
}
return result;
}

public Matrix multiply(Matrix m) { // 矩阵乘法;注意这里 Java 支持函数的重载,可以直接和数乘用同一个函数名
if (this.cols != m.rows) {
throw new IllegalArgumentException("维数不匹配,无法相乘");
}
Matrix result = new Matrix(this.rows, m.cols);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < m.cols; j++) {
for (int k = 0; k < this.cols; k++) {
result.data[i][j] += this.data[i][k] * m.data[k][j];
}
}
}
return result;
}
}

判题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
public class Main {

// 浮点数比较允许的误差阈值(解决 double 计算的精度偏差)
private static final double EPSILON = 1e-6;
private static int totalCases = 0;
private static int passedCases = 0;

public static void main(String[] args) {
testTranspose();
testAdd();
testScalarMultiply();
testMatrixMultiply();
testDimensionMismatchExceptions();

printSummary();
}

// 1. 测试转置
static void testTranspose() {
System.out.println("[Testing] Transpose...");
Matrix a = new Matrix(2, 3);
a.data = new double[][]{
{1.0, 2.0, 3.0},
{4.0, 5.0, 6.0}
};

a.transpose();

double[][] expected = {
{1.0, 4.0},
{2.0, 5.0},
{3.0, 6.0}
};

assertMatrixEquals("Test 1: 2x3 Matrix Transpose", a, expected);
}

// 2. 测试加法
static void testAdd() {
System.out.println("[Testing] Add...");
Matrix a = new Matrix(2, 2);
a.data = new double[][]{{1.5, -2.0}, {0.0, 3.5}};

Matrix b = new Matrix(2, 2);
b.data = new double[][]{{0.5, 2.0}, {4.0, -1.5}};

Matrix res = a.add(b);

double[][] expected = {
{2.0, 0.0},
{4.0, 2.0}
};

assertMatrixEquals("Test 2: 2x2 Matrix Add", res, expected);
}

// 3. 测试数乘
static void testScalarMultiply() {
System.out.println("[Testing] Scalar Multiply...");
Matrix a = new Matrix(2, 2);
a.data = new double[][]{{1.5, -2.0}, {3.0, 0.0}};

Matrix res = a.multiply(2.0);

double[][] expected = {
{3.0, -4.0},
{6.0, 0.0}
};

assertMatrixEquals("Test 3: Scalar Multiply", res, expected);
}

// 4. 测试矩阵乘法 (2x3 * 3x2 -> 2x2)
static void testMatrixMultiply() {
System.out.println("[Testing] Matrix Multiply...");
Matrix a = new Matrix(2, 3);
a.data = new double[][]{
{1.0, 2.0, 3.0},
{4.0, 5.0, 6.0}
};

Matrix b = new Matrix(3, 2);
b.data = new double[][]{
{1.0, 2.0},
{3.0, 4.0},
{5.0, 6.0}
};

Matrix res = a.multiply(b);

double[][] expected = {
{22.0, 28.0},
{49.0, 64.0}
};

assertMatrixEquals("Test 4: 2x3 * 3x2 Matrix Multiply", res, expected);
}

// 5. 测试异常边界:维度不匹配时是否按预期抛出异常
static void testDimensionMismatchExceptions() {
System.out.println("[Testing] Exception Boundaries...");

// 测试加法维度不匹配
totalCases++;
try {
Matrix a = new Matrix(2, 2);
Matrix b = new Matrix(3, 2);
a.add(b);
System.out.printf(" [WA] Test 5-1: Add should throw IllegalArgumentException for mismatched shapes%n");
} catch (IllegalArgumentException e) {
passedCases++;
System.out.printf(" [AC] Test 5-1: Add dimension mismatch caught correctly%n");
}

// 测试乘法维度不匹配 (2x3 乘 2x2,列数3 != 行数2)
totalCases++;
try {
Matrix a = new Matrix(2, 3);
Matrix b = new Matrix(2, 2);
a.multiply(b);
System.out.printf(" [WA] Test 5-2: Multiply should throw IllegalArgumentException when cols != rows%n");
} catch (IllegalArgumentException e) {
passedCases++;
System.out.printf(" [AC] Test 5-2: Multiply dimension mismatch caught correctly%n");
}
}

// ================= OJ 辅助断言函数 =================

private static void assertMatrixEquals(String caseName, Matrix actual, double[][] expected) {
totalCases++;
int expRows = expected.length;
int expCols = expected[0].length;

// 校验维度
if (actual.rows != expRows || actual.cols != expCols) {
System.out.printf(" [WA] %s: Shape mismatch. Expected: (%d, %d), Got: (%d, %d)%n",
caseName, expRows, expCols, actual.rows, actual.cols);
return;
}

// 校验浮点数值(考虑精度误差)
for (int i = 0; i < actual.rows; i++) {
for (int j = 0; j < actual.cols; j++) {
if (Math.abs(actual.data[i][j] - expected[i][j]) > EPSILON) {
System.out.printf(" [WA] %s: Value mismatch at [%d][%d]. Expected: %f, Got: %f%n",
caseName, i, j, expected[i][j], actual.data[i][j]);
return;
}
}
}

passedCases++;
System.out.printf(" [AC] %s%n", caseName);
}

private static void printSummary() {
System.out.println("========================================");
if (passedCases == totalCases) {
System.out.printf("All Accepted (AC)! Total Passed: %d/%d%n", passedCases, totalCases);
} else {
System.out.printf("Wrong Answer (WA)! Passed: %d/%d%n", passedCases, totalCases);
}
System.out.println("========================================");
}
}

预期结果为 AC。

Lab 2 逐渐复杂的矩阵

在 Lab 1 的基础上,你需要引入更复杂的线性代数数学逻辑,实现方阵/对称/反称矩阵的性质判断、生成单位矩阵,并攻克求行列式和求逆矩阵的核心算法,同时通过重写 toString() 方法 来优雅地输出矩阵内容。

Lab 3 从本地类到内存 API

通过引入 Spring MVC 框架,将 Lab 1 & 2 编写的本地 Matrix 运算逻辑包装为可以通过 HTTP 协议访问的 Web API,并对齐前端 Axios 的异步请求规范。你需要创建 DTO、实现

  1. POST /api/matrix/multiply
  2. POST /api/matrix/determinant
  3. POST /api/matrix/inverse

三个 API 接口,并且解决跨域问题。

Lab 4 控制权反转与异常拦截

在 Lab 3 中,你可能把所有的矩阵计算逻辑直接写在了 Controller 的方法里,或者在 Controller 里通过 Matrix myMatrix = new Matrix() 手动实例化了对象。在工业级开发中,这种做法会导致代码高度耦合。如果计算逻辑需要修改,或者需要切换多线程版本,你就必须大面积修改 Controller。

Lab 4 的核心目的就是:干掉一切手动的 new 操作,将对象的创建和管理权全部上交给 Spring(控制反转 IoC),并在需要的地方自动注入(依赖注入 DI)。同时,我们要利用 Spring 的 AOP(面向切面编程)思想,建立一个“全网捕鱼”的中央错误拦截机制。

你需要将所有计算逻辑抽离到由 @Service 宣告的业务层,在控制器中改用 @Autowired 自动注入以干掉所有手动的 new 操作(IoC/DI),同时利用 @RestControllerAdvice 编写全局异常处理器,统一拦截数学错误(如矩阵不可逆),避免向前端 Axios 暴露不友好的 500 堆栈报错。

Lab 5 持久化 MYSQL 数据库

内存数据随进程结束而释放。本实验要求你引入 MySQL 关系型数据库与 MyBatis-Plus 生态,完成将 Web 端计算产生的矩阵历史记录进行持久化(写入硬盘)的功能。

你需要在本地或容器中配置 MySQL 数据库,在 application.yml 中配置连接,利用 MyBatis-Plus 建立实体类与数据库表的 ORM 映射,使得每次计算成功时都将计算矩阵及结果持久化存入硬盘,并提供一个 GET /api/matrix/history 接口,调用 list() 方法查出所有的计算历史展示给前端。

Lab 6 Matrix Agent Gateway

作为最后一个 Lab,你需要使用 Spring 内置的 RestTemplate 或 WebClient 在 Java 后端向大模型(如 DeepSeek、OpenAI)发送带有 API Key 的 POST 请求,将前端传来的“人话”指令投喂给 AI 转换为结构化矩阵数据,随后在本地调用你的 MatrixService 完成硬核计算,体验自然语言驱动的智能体网关开发。


本站由 叶菡茵 使用 Stellar 主题创建。
不建议将文章内容用于训练 LLM,以免影响语料质量。
除特别声明外,本博客所有文章均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。