Java 核心知识点查漏补缺(三)
JavaScript(JS)和 jQuery 是前端开发中密切相关的技术,但两者定位不同:JavaScript 是编程语言,而 jQuery 是基于 JavaScript 的库,用于简化 JS 操作(尤其是 DOM 操作和异步请求)。以下是详细对比和关系解析:
一、核心区别
| 维度 | JavaScript | jQuery |
|---|---|---|
| 本质 | 原生编程语言,浏览器内置支持 | 基于 JS 的库(封装了 JS 代码),需引入使用 |
| 定位 | 负责网页的逻辑和交互,功能全面(如变量、函数、DOM 操作等) | 简化 JS 开发,专注解决 DOM 操作、事件处理、AJAX 等常见问题 |
| 语法复杂度 | 原生语法较繁琐(如获取元素、兼容浏览器) | 语法简洁,一行代码实现复杂操作(如 $("div").hide()) |
| 浏览器兼容 | 需手动处理不同浏览器的差异(如早期 IE 的 API 差异) | 内置兼容处理,开发者无需关心浏览器差异 |
| 功能范围 | 全功能(逻辑运算、DOM、BOM、异步等) | 聚焦 DOM 操作、事件、AJAX、动画等前端常见需求 |
二、jQuery 解决了什么问题?
在 jQuery 诞生前(2006 年),原生 JS 操作存在诸多痛点,jQuery 应运而生:
1. 简化 DOM 操作
原生 JS 获取和操作元素的代码冗长,而 jQuery 用简洁的选择器实现:
// 原生 JS:获取所有 class 为 "box" 的 div 并隐藏
let divs = document.getElementsByClassName("box");
for (let i = 0; i < divs.length; i++) {divs[i].style.display = "none";
}// jQuery:一行代码搞定
$("div.box").hide(); // $ 是 jQuery 的核心函数
2. 统一事件处理
原生 JS 绑定事件的方式不统一(如 addEventListener 与 IE 的 attachEvent),jQuery 统一了接口:
// 原生 JS:绑定点击事件(需兼容 IE)
let btn = document.getElementById("btn");
if (btn.addEventListener) {btn.addEventListener("click", function() { ... });
} else if (btn.attachEvent) { // 兼容 IE 8 及以下btn.attachEvent("onclick", function() { ... });
}// jQuery:无需考虑兼容
$("#btn").click(function() { ... });
3. 简化 AJAX 请求
原生 JS 的 XMLHttpRequest 代码繁琐,jQuery 封装后更简洁:
// 原生 JS:发送 GET 请求
let xhr = new XMLHttpRequest();
xhr.open("GET", "data.json", true);
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {let data = JSON.parse(xhr.responseText);}
};
xhr.send();// jQuery:一行代码发送 AJAX
$.get("data.json", function(data) { ... });
4. 内置动画效果
jQuery 提供了简单的动画 API,无需手动写 CSS 或定时器:
// 隐藏元素并添加淡入动画
$("div").fadeIn(1000); // 1000 毫秒内淡入
三、jQuery 的核心语法
jQuery 的核心是选择器和链式调用:
选择器:通过类似 CSS 的语法获取元素(
$("选择器"))。$("div") // 所有 div 元素 $("#id") // id 为 "id" 的元素 $(".class") // class 为 "class" 的元素 $("ul li") // ul 下的所有 li 元素链式调用:多个操作可以连写(返回当前 jQuery 对象)。
// 选中元素 → 添加样式 → 绑定点击事件 → 隐藏 $("p").css("color", "red").click(function() { alert("点击了") }).hide(500);
四、为什么现在 jQuery 用得少了?
近年来,jQuery 的使用率逐渐下降,主要原因:
浏览器原生 API 优化:现代浏览器(Chrome、Firefox 等)统一了标准,原生 JS 的 DOM 操作(如
querySelector)和 AJAX(fetchAPI)已足够简洁,无需依赖 jQuery。// 原生 JS 现在也很简洁 document.querySelectorAll("div.box").forEach(div => {div.style.display = "none"; });前端框架崛起:React、Vue、Angular 等框架采用组件化思想,自带 DOM 操作逻辑,无需 jQuery。
功能冗余:jQuery 包含大量兼容旧浏览器的代码,而现代开发多面向最新浏览器,无需这些冗余代码。
五、总结
- 关系:jQuery 是 JS 的 “工具库”,基于 JS 实现,简化了特定场景的开发。
- 适用场景:
- 快速开发简单页面(如企业官网、静态页),jQuery 仍能提高效率。
- 维护旧项目(大量 legacy 系统仍依赖 jQuery)。
- 现代复杂应用(如单页应用)更推荐原生 JS 或前端框架。
JSON 与 Java 对象的相互转换是数据交互的核心操作,主流工具(fastjson、Jackson、Gson)均提供了简洁的实现方式。以下是完整总结,涵盖「Java 对象 ↔ JSON 字符串」「Java 对象 ↔ JSON 对象」「JSON 字符串 ↔ Java 对象」「JSON 对象 ↔ Java 对象」四种场景:
一、核心工具与依赖
| 工具 | 依赖配置(Maven) | 核心类 / 对象类型 |
|---|---|---|
| fastjson | <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.32</version></dependency> | JSON 类、JSONObject |
| Jackson | <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency> | ObjectMapper、JsonNode |
| Gson | <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.10.1</version></dependency> | Gson 类、JsonObject |
二、Java 对象 → JSON 字符串
1. fastjson
User user = new User("张三", 20, true);
String jsonString = JSON.toJSONString(user); // 输出:{"name":"张三","age":20,"isStudent":true}
Java 对象 → JSON 对象
1. fastjson(转 JSONObject)
User user = new User("张三", 20, true);
JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
// 操作:jsonObject.getString("name") → "张三"
JSON 字符串 → Java 对象
1. fastjson
String jsonString = "{\"name\":\"张三\",\"age\":20,\"isStudent\":true}";
User user = JSON.parseObject(jsonString, User.class);
JSON 对象 → Java 对象
1. fastjson(JSONObject → Java 对象)
JSONObject jsonObject = JSONObject.parseObject("{\"name\":\"张三\",\"age\":20}");
User user = jsonObject.toJavaObject(User.class);
sql语言分类:DDL DML DQL DCL
1. DQL(数据查询语言):查询数据
核心作用是从数据库表中获取所需数据,最常用的语句是 SELECT,是日常开发中使用频率最高的 SQL 类型。
基础语法:
SELECT 列名1, 列名2 -- 要查询的列(* 表示查询所有列)
FROM 表名 -- 要查询的表
WHERE 条件 -- 筛选条件(可选)
ORDER BY 列名 排序方式 -- 排序(可选,ASC升序/Desc降序)
LIMIT 数量; -- 限制返回条数(可选,部分数据库用TOP)
示例:查询 “用户表” 中年龄大于 20 的用户姓名和手机号,按年龄降序排列。
SELECT name, phone
FROM user
WHERE age > 20
ORDER BY age DESC;
2. DML(数据操纵语言):操作数据
用于对表中的数据进行新增、修改、删除,核心语句包括 INSERT、UPDATE、DELETE。
| 语句 | 作用 | 基础语法示例 |
|---|---|---|
| INSERT | 新增数据 | INSERT INTO user(name, age) VALUES('张三', 20), ('李四', 22);(批量新增) |
| UPDATE | 修改数据 | UPDATE user SET age = 21 WHERE name = '张三';(修改指定条件的记录) |
| DELETE | 删除数据 | DELETE FROM user WHERE age < 18;(删除满足条件的记录,谨慎使用) |
3. DDL(数据定义语言):定义结构
用于创建、修改、删除数据库中的对象(如表、字段、索引等),核心语句包括 CREATE、ALTER、DROP。
| 语句 | 作用 | 基础语法示例 |
|---|---|---|
| CREATE | 创建对象 | CREATE TABLE user(id INT, name VARCHAR(20), age INT);(创建用户表) |
| ALTER | 修改对象 | ALTER TABLE user ADD phone VARCHAR(11);(给用户表新增 “手机号” 字段) |
| DROP | 删除对象 | DROP TABLE user;(删除用户表,数据和结构全删除,谨慎使用) |
4. DCL(数据控制语言):控制权限
用于管理数据库的访问权限,控制用户对数据的操作范围,核心语句包括 GRANT、REVOKE。
| 语句 | 作用 | 基础语法示例 |
|---|---|---|
| GRANT | 授予权限 | GRANT SELECT, INSERT ON user TO 'test'@'localhost';(给用户授予查询 / 新增权限) |
| REVOKE | 收回权限 | REVOKE INSERT ON user FROM 'test'@'localhost';(收回用户的新增权限) |
一、数值型数据类型
专注存储整数、小数等数值,核心是 “按范围和精度选择”,避免资源浪费。
核心类型分类
| 类型分类 | 具体类型 | 关键特点 | 适用场景 |
|---|---|---|---|
| 整数型 | TINYINT | 1 字节,范围小(-128~127),常用于存储状态值 | 性别(1/2)、开关状态(0/1) |
| INT | 4 字节,范围适中(-21 亿~21 亿),性能与范围平衡 | 用户 ID、订单号、年龄 | |
| BIGINT | 8 字节,范围极大(-9e18~9e18),支持超大数值 | 海量数据 ID、时间戳(毫秒级) | |
| 小数型 | FLOAT/DOUBLE | 浮点数,精度有限(FLOAT 约 6-7 位,DOUBLE 约 15-17 位),可能有精度丢失 | 温度、体重等非精确小数 |
| DECIMAL | 定点数,精度完全精确(按定义的位数存储),无误差 | 金额、汇率、税率等精确数值 |
关键注意点
- 整数型可加
UNSIGNED修饰(如INT UNSIGNED),范围变为非负(0~4294967295),适合无负数场景(如库存)。 - 小数型格式为
类型(M,D),M是总位数,D是小数位数(如DECIMAL(10,2)表示最大存储 99999999.99)。
二、字符串型数据类型
存储文本、字符数据,按长度分为 “短字符串”“长文本(大数据类型)” 和 “特殊字符串”,核心是 “平衡存储效率与业务需求”。
核心类型分类
| 类型分类 | 具体类型 | 关键特点 | 适用场景 |
|---|---|---|---|
| 短字符串 | CHAR | 固定长度(0~255 字符),存储快,适合长度固定的数据 | 手机号(11 位)、身份证号(18 位) |
| VARCHAR | 可变长度(0~65535 字符),节省空间,适合长度波动数据 | 姓名、地址、邮箱 | |
| 长文本(大数据类型) | TEXT | 存储上限 64KB,适合中等长度文本 | 文章内容、用户评论 |
| MEDIUMTEXT | 存储上限 16MB,适合长文档、大段富文本 | 小说章节、产品详情 | |
| LONGTEXT | 存储上限 4GB,适合超大文本(实际很少用,通常存文件路径) | 完整小说、超大日志 | |
| 特殊字符串 | ENUM | 枚举类型,只能选预定义值中的一个,存储效率高 | 订单状态(待支付 / 已支付)、性别 |
| SET | 集合类型,可多选预定义值(最多 64 个),用位图存储 | 用户兴趣、商品标签 | |
| JSON | 专门存储 JSON 格式数据,支持语法校验和便捷操作 | 用户画像、商品规格等半结构化数据 |
关键注意点
- 长文本类型(TEXT 系列)属于大数据类型,不能设默认值,不建议作为索引(如需索引,需指定前缀长度,如
INDEX idx_content(content(50)))。 - 字符编码优先选
UTF8MB4(支持 emoji 表情),避免用UTF8(无法存储 emoji)。
三、日期时间型数据类型
专门存储日期、时间信息,核心是 “匹配时间精度和业务场景”,避免范围不足或冗余。
核心类型分类
| 具体类型 | 关键特点 | 格式示例 | 适用场景 |
|---|---|---|---|
| DATE | 仅存日期,3 字节,范围 1000-01-01~9999-12-31 | '2024-05-20' | 生日、订单日期 |
| TIME | 仅存时间,3 字节,范围 - 838:59:59~838:59:59 | '14:30:00' | 打卡时间、会议时长 |
| DATETIME | 存日期 + 时间,8 字节,范围广(1000~9999 年),不依赖时区 | '2024-05-20 14:30:00' | 订单创建时间、文章发布时间 |
| TIMESTAMP | 存日期 + 时间,4 字节,范围窄(1970~2038 年),依赖时区,支持自动更新 | '2024-05-20 14:30:00' | 数据最后修改时间、时间戳场景 |
| YEAR | 仅存年份,1 字节,范围 1901~2155 | 2024 | 毕业年份、产品生产年份 |
关键注意点
- TIMESTAMP 受数据库时区影响,插入 / 查询时会自动转换时区;DATETIME 存储原始值,不受时区影响。
- 需 “自动记录修改时间” 时,用
TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。
四、逻辑性数据类型
存储布尔值等逻辑状态,本质是整数类型的简化,核心是 “简洁表示逻辑判断”。
核心类型:BOOLEAN(BOOL)
- 本质:MySQL 中无真正的布尔类型,
BOOLEAN是TINYINT(1)的别名。 - 取值:仅支持
TRUE(等价于 1)和FALSE(等价于 0),插入其他数值会自动转换为 1 或 0。 - 适用场景:表示开关状态(如是否启用、是否删除、是否支付)。
- 示例:
CREATE TABLE user (id INT,is_vip BOOLEAN DEFAULT FALSE, -- 是否为VIPis_deleted BOOLEAN DEFAULT FALSE -- 是否删除(逻辑删除) );
约束是 MySQL 中用于保证单张数据表中数据完整性、一致性和唯一性的规则,通过限制字段的取值范围、关系或格式,避免无效数据或错误数据的插入。
| 约束类型 | 关键字 | 核心作用 | 适用场景 |
|---|---|---|---|
| 非空约束 | NOT NULL | 限制字段值不能为 NULL(必须填写) | 姓名、手机号等必填字段 |
| 唯一约束 | UNIQUE | 限制字段值在表中唯一(不能重复,但可以为 NULL) | 邮箱、用户名(需唯一标识) |
| 主键约束 | PRIMARY KEY | 唯一标识表中每条记录(非空 + 唯一),一张表只能有一个主键 | 用户 ID、订单 ID(唯一标识记录) |
| 默认值约束 | DEFAULT | 字段未赋值时,自动使用默认值 | 性别默认 “未知”、状态默认 “正常” |
| 自增约束 | AUTO_INCREMENT | 配合整数型主键使用,插入数据时自动生成唯一递增的值(从 1 开始,每次 + 1) | 自增 ID(无需手动指定主键值) |
| 检查约束 | CHECK | 限制字段值必须满足指定条件(MySQL 8.0+ 支持,低版本仅语法通过但不生效) |
一、数据库操作(Database)
数据库是数据表的容器,操作主要包括创建、查看、切换、删除等。
| 操作 | SQL 语句示例 | 说明 |
|---|---|---|
| 创建数据库 | CREATE DATABASE IF NOT EXISTS mydb DEFAULT CHARACTER SET utf8mb4; | 若数据库不存在则创建,指定编码为 utf8mb4(支持 emoji) |
| 查看所有数据库 | SHOW DATABASES; | 列出 MySQL 中所有数据库 |
| 切换数据库 | USE mydb; | 切换到 mydb 数据库(后续操作默认在此库中) |
| 查看当前数据库 | SELECT DATABASE(); | 显示当前正在使用的数据库 |
| 删除数据库 | DROP DATABASE IF EXISTS mydb; | 若数据库存在则删除(谨慎操作,数据会全部丢失) |
二、数据表操作(Table)
数据表是存储数据的基本单位,操作包括创建、查看、修改、删除表结构,核心是定义字段、类型和约束。
1. 创建表(CREATE TABLE)
-- 创建 user 表,包含 id(主键自增)、name(非空)、age、email(唯一)等字段
CREATE TABLE IF NOT EXISTS user (id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',name VARCHAR(20) NOT NULL COMMENT '姓名',age TINYINT UNSIGNED COMMENT '年龄(非负)',email VARCHAR(50) UNIQUE COMMENT '邮箱(唯一)',gender ENUM('男', '女', '未知') DEFAULT '未知' COMMENT '性别',create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户表';
- 关键参数:
ENGINE=InnoDB(存储引擎,支持事务)、DEFAULT CHARSET=utf8mb4(字符编码)、COMMENT(字段 / 表注释)。
2. 查看表相关信息
| 操作 | SQL 语句示例 | 说明 |
|---|---|---|
| 查看所有表 | SHOW TABLES; | 列出当前数据库中所有表 |
| 查看表结构 | DESC user; 或 DESCRIBE user; | 显示表的字段、类型、约束等信息 |
| 查看表创建语句 | SHOW CREATE TABLE user; | 查看创建表的完整 SQL(含引擎、编码等) |
3. 修改表结构(ALTER TABLE)
| 操作 | SQL 语句示例 | 说明 |
|---|---|---|
| 新增字段 | ALTER TABLE user ADD COLUMN phone VARCHAR(11) UNIQUE COMMENT '手机号'; | 给 user 表新增 phone 字段(唯一) |
| 修改字段类型 / 约束 | ALTER TABLE user MODIFY COLUMN age INT UNSIGNED; | 将 age 字段类型从 TINYINT 改为 INT |
| 修改字段名 | ALTER TABLE user CHANGE COLUMN phone tel VARCHAR(11); | 将 phone 字段改名为 tel |
| 删除字段 | ALTER TABLE user DROP COLUMN tel; | 删除 tel 字段(数据会丢失) |
| 修改表名 | ALTER TABLE user RENAME TO t_user; | 将表名从 user 改为 t_user |
4. 删除表(DROP TABLE)
DROP TABLE IF EXISTS t_user; -- 若表存在则删除(谨慎操作,数据和结构全丢失)
三、数据操作(DML)
对表中的数据进行新增(INSERT)、查询(SELECT)、修改(UPDATE)、删除(DELETE),即 “CRUD” 操作,是业务开发中最频繁的操作。
1. 新增数据(INSERT)
-- 方式1:指定字段插入(推荐,顺序可自定义)
INSERT INTO user (name, age, email)
VALUES ('张三', 20, 'zhangsan@test.com');-- 方式2:插入多条数据
INSERT INTO user (name, age, email)
VALUES ('李四', 22, 'lisi@test.com'),('王五', 25, 'wangwu@test.com');-- 方式3:不指定字段(需按表字段顺序插入所有值,包括自增字段用NULL占位)
INSERT INTO user
VALUES (NULL, '赵六', 28, 'zhaoliu@test.com', '男', NULL);
- 自增字段(如 id)可省略或用
NULL占位,会自动生成值;默认值字段(如 gender)可省略,自动用默认值。
2. 查询数据(SELECT)
基础查询:
-- 查询所有字段(* 表示所有,实际开发建议指定字段名)
SELECT * FROM user;-- 查询指定字段,加条件筛选
SELECT name, age FROM user WHERE age > 20;-- 去重查询(排除重复值)
SELECT DISTINCT age FROM user;-- 排序(ORDER BY,ASC升序/Desc降序,默认升序)
SELECT name, age FROM user ORDER BY age DESC;-- 限制查询条数(LIMIT,常用于分页)
SELECT * FROM user LIMIT 2; -- 只查前2条
SELECT * FROM user LIMIT 1, 2; -- 从第2条开始,查2条(分页:页码从0开始)
条件查询(WHERE 子句):
-- 等于/不等于
SELECT * FROM user WHERE gender = '男';
SELECT * FROM user WHERE age != 20;-- 范围查询(BETWEEN...AND / IN)
SELECT * FROM user WHERE age BETWEEN 20 AND 30; -- 年龄20~30
SELECT * FROM user WHERE name IN ('张三', '李四'); -- 姓名是张三或李四-- 模糊查询(LIKE,%匹配任意字符,_匹配单个字符)
SELECT * FROM user WHERE name LIKE '张%'; -- 姓张的人
SELECT * FROM user WHERE email LIKE '%@test.com'; -- 邮箱以@test.com结尾-- 逻辑运算(AND / OR / NOT)
SELECT * FROM user WHERE age > 20 AND gender = '女';
3. 修改数据(UPDATE)
-- 修改符合条件的记录(必须加WHERE,否则全表修改!)
UPDATE user
SET age = 21, email = 'zhangsan_new@test.com'
WHERE name = '张三';-- 用表达式修改(如年龄+1)
UPDATE user SET age = age + 1 WHERE id = 1;
- 警告:若省略
WHERE子句,会修改表中所有记录,务必谨慎!
4. 删除数据(DELETE)
-- 删除符合条件的记录(必须加WHERE,否则全表删除!)
DELETE FROM user WHERE id = 3;-- 删除所有数据(保留表结构,不重置自增序列)
DELETE FROM user;-- 清空表(删除所有数据,重置自增序列,速度比DELETE快)
TRUNCATE TABLE user;
DELETE是逐行删除,支持事务回滚;TRUNCATE是直接重建表,不支持回滚,适合彻底清空表。- 警告:省略
WHERE会删除全表数据,操作前务必确认!
查询练习(10 个场景)
以下查询均基于 student 表,覆盖基础查询、条件筛选、排序、分组、分页等核心场景。
1. 基础查询:查询所有学生的姓名、班级和成绩
sql
SELECT name, class_name, score FROM student;
2. 条件查询:查询高三 1 班的所有学生
sql
SELECT * FROM student WHERE class_name = '高三1班';
3. 多条件查询:查询女生且成绩 >= 80 分的学生
sql
SELECT name, gender, score FROM student
WHERE gender = '女' AND score >= 80;
4. 范围查询:查询年龄在 17-18 岁之间的学生
sql
SELECT name, age, class_name FROM student
WHERE age BETWEEN 17 AND 18; -- 等价于 age >=17 AND age <=18
5. 模糊查询:查询姓名以 “孙” 开头的学生
sql
SELECT name, gender, score FROM student
WHERE name LIKE '孙%'; -- % 匹配任意字符
6. 排序查询:按成绩降序排列,成绩相同则按年龄升序
sql
SELECT name, score, age FROM student
ORDER BY score DESC, age ASC;
7. 分页查询:查询第 2 页数据(每页 3 条,即第 4-6 条)
sql
-- 分页公式:LIMIT (页码-1)*每页条数, 每页条数
SELECT * FROM student LIMIT 3, 3; -- 偏移量3(跳过前3条),取3条
8. 聚合查询:统计每个班级的学生人数
sql
SELECT class_name 班级, COUNT(*) 人数
FROM student
GROUP BY class_name; -- 按班级分组
9. 分组筛选:查询平均分 >= 80 分的班级及平均分
sql
SELECT class_name 班级, AVG(score) 平均分
FROM student
GROUP BY class_name
HAVING 平均分 >= 80; -- 对分组后的结果筛选(不能用WHERE)
10. 复杂条件:查询 2022 年入学的男生中,成绩前 3 名的学生
sql
SELECT name, score, enroll_date FROM student
WHERE gender = '男' AND YEAR(enroll_date) = 2022 -- 2022年入学的男生
ORDER BY score DESC -- 按成绩降序
LIMIT 3; -- 取前3名MySQL 提供 5 个核心聚合函数,覆盖绝大多数统计场景:
| 函数 | 作用 | 适用类型 | 示例(基于 student 表) |
|---|---|---|---|
| COUNT() | 统计记录行数(非 NULL 值的数量) | 任意类型 | COUNT(*) → 统计所有学生总数 |
| SUM() | 计算指定字段的总和 | 数值型(INT、DECIMAL 等) | SUM(score) → 计算所有学生的成绩总和 |
| AVG() | 计算指定字段的平均值 | 数值型 | AVG(age) → 计算学生的平均年龄 |
| MAX() | 查找指定字段的最大值 | 数值型、日期型等 | MAX(score) → 查找最高成绩;MAX(enroll_date) → 查找最晚入学日期 |
| MIN() | 查找指定字段的最小值 | 数值型、日期型等 | MIN(age) → 查找最小年龄;MIN(score) → 查找最低成绩 |
二、聚合函数的使用场景
1. 全局统计(不分组,对全表数据计算)
sql
-- 1. 统计学生总数(COUNT(*) 包含 NULL 值的行)
SELECT COUNT(*) AS 总人数 FROM student;-- 2. 统计有成绩的学生数(COUNT(score) 忽略 NULL 值)
SELECT COUNT(score) AS 有成绩的人数 FROM student;-- 3. 计算所有学生的平均成绩、最高成绩、最低成绩
SELECT AVG(score) AS 平均成绩,MAX(score) AS 最高成绩,MIN(score) AS 最低成绩
FROM student;-- 4. 计算高三1班所有学生的成绩总和
SELECT SUM(score) AS 高三1班总成绩 FROM student WHERE class_name = '高三1班';
2. 分组统计(配合 GROUP BY,按分组计算)
对不同分组分别应用聚合函数,实现 “按类别统计”(如按班级、性别分组)。
sql
-- 1. 按班级分组,统计每个班级的人数、平均成绩、最高成绩
SELECT class_name AS 班级,COUNT(*) AS 班级人数,AVG(score) AS 平均成绩,MAX(score) AS 最高成绩
FROM student
GROUP BY class_name; -- 按班级分组-- 2. 按性别分组,统计男生/女生的平均年龄和最小年龄
SELECT gender AS 性别,AVG(age) AS 平均年龄,MIN(age) AS 最小年龄
FROM student
GROUP BY gender;-- 3. 按入学年份分组(用 YEAR() 函数提取年份),统计每年入学的人数
SELECT YEAR(enroll_date) AS 入学年份,COUNT(*) AS 入学人数
FROM student
GROUP BY 入学年份; -- 按提取的年份分组
3. 筛选分组结果(HAVING 子句)
WHERE 无法筛选聚合函数的结果,需用 HAVING 对分组后的统计结果进行筛选(必须在 GROUP BY 之后)。
sql
-- 1. 筛选平均成绩 >= 80 分的班级
SELECT class_name AS 班级,AVG(score) AS 平均成绩
FROM student
GROUP BY class_name
HAVING 平均成绩 >= 80; -- 对分组后的平均成绩筛选-- 2. 筛选人数 >= 3 的班级,并显示最高成绩
SELECT class_name AS 班级,COUNT(*) AS 人数,MAX(score) AS 最高成绩
FROM student
GROUP BY class_name
HAVING 人数 >= 3;
三、聚合函数的关键特性
忽略 NULL 值:所有聚合函数都会自动忽略字段值为
NULL的记录(COUNT(*)除外,它统计所有行,包括NULL)。例:若某学生score为NULL,SUM(score)、AVG(score)会忽略该记录,COUNT(score)也会忽略,而COUNT(*)仍会统计该行。与
DISTINCT配合:可对去重后的数据进行聚合(先去重再计算)。例:统计不同年龄的学生人数(去重年龄后计数):sql
SELECT COUNT(DISTINCT age) AS 不同年龄的数量 FROM student;不能直接用于
WHERE子句:WHERE用于筛选原始数据,而聚合函数依赖一组数据的计算结果,因此需用HAVING筛选分组后的聚合结果。错误示例(WHERE中使用聚合函数):sql
-- 错误!WHERE 不能直接用 AVG(score) SELECT class_name FROM student WHERE AVG(score) >= 80 GROUP BY class_name;正确示例(用
HAVING):sql
SELECT class_name FROM student GROUP BY class_name HAVING AVG(score) >= 80;
四、聚合函数练习(基于 student 表)
- 统计高三所有班级中,成绩在 60 分以上的学生总数。
- 按班级分组,计算每个班级的成绩总和,并筛选出总和 > 300 分的班级。
- 查找每个班级中年龄最大的学生的姓名和年龄(提示:需结合子查询或窗口函数,进阶内容)。
二、内连接(INNER JOIN)
只返回两个表中匹配条件的记录(即 “交集”),不匹配的记录会被过滤。
语法:
sql
SELECT 字段
FROM 表1
INNER JOIN 表2
ON 表1.关联字段 = 表2.关联字段; -- 关联条件(通常是外键=主键)
示例:
查询 “已选课的学生” 及其选课信息(只显示两边都匹配的记录):
sql
SELECT s.id AS 学生ID,s.name AS 姓名,c.course_name AS 课程,c.score AS 成绩
FROM student s
INNER JOIN course c
ON s.id = c.student_id; -- 关联条件:学生ID匹配
结果说明:
- 只包含
student和course中id匹配的记录(张三、李四、王五)。 - 未选课的赵六(
student中无匹配的course记录)和course中student_id=5(无对应学生)的记录均不显示。
三、外连接(LEFT/RIGHT/FULL JOIN)
外连接会保留 “主表” 的所有记录,未匹配的记录用 NULL 填充,分为左外连接、右外连接和全外连接。
1. 左外连接(LEFT JOIN / LEFT OUTER JOIN)
保留左表的所有记录,右表中无匹配的记录用 NULL 填充。
语法:
sql
SELECT 字段
FROM 左表
LEFT JOIN 右表
ON 左表.关联字段 = 右表.关联字段;
示例:
查询 “所有学生” 及其选课信息(包括未选课的学生):
sql
SELECT s.id AS 学生ID,s.name AS 姓名,c.course_name AS 课程,c.score AS 成绩
FROM student s
LEFT JOIN course c
ON s.id = c.student_id;
结果说明:
- 左表(
student)的所有学生(张三、李四、王五、赵六)均显示。 - 未选课的赵六,其
course_name和score为NULL。
2. 右外连接(RIGHT JOIN / RIGHT OUTER JOIN)
保留右表的所有记录,左表中无匹配的记录用 NULL 填充。
示例:
查询 “所有选课记录” 及其对应学生信息(包括无效的选课记录):
sql
SELECT s.id AS 学生ID,s.name AS 姓名,c.course_name AS 课程,c.score AS 成绩
FROM student s
RIGHT JOIN course c
ON s.id = c.student_id;
结果说明:
- 右表(
course)的所有选课记录(包括student_id=5的无效记录)均显示。 student_id=5对应的学生信息(id、name)为NULL。
3. 全外连接(FULL JOIN)
保留左右两表的所有记录,未匹配的部分用 NULL 填充。注意:MySQL 不直接支持 FULL JOIN,可通过 LEFT JOIN + UNION + RIGHT JOIN 模拟。
示例:
查询所有学生和所有选课记录(包括未匹配的两边记录):
sql
-- 左连接结果 + 右连接中左表无匹配的记录(去重)
SELECT s.id, s.name, c.course_name, c.score
FROM student s LEFT JOIN course c ON s.id = c.student_id
UNION -- 合并结果并去重
SELECT s.id, s.name, c.course_name, c.score
FROM student s RIGHT JOIN course c ON s.id = c.student_id;
四、子查询(Subquery)
子查询是嵌套在其他 SQL 语句中的查询,可作为条件、数据源或字段值,分为单行子查询、多行子查询和关联子查询。
1. 作为条件(WHERE 子句中)
单行子查询(返回单个值):
查询 “成绩大于班级平均分的学生”:
sql
-- 先查高三1班的平均分,再查大于该分数的学生
SELECT name, score
FROM course
WHERE student_id IN (SELECT id FROM student WHERE class_name = '高三1班')
AND score > (SELECT AVG(score) FROM course WHERE student_id IN (SELECT id FROM student WHERE class_name = '高三1班')
);
多行子查询(返回多个值,配合 IN/ANY/ALL):
查询 “选了‘数学’或‘英语’的学生姓名”:
sql
SELECT name
FROM student
WHERE id IN (SELECT student_id FROM course WHERE course_name IN ('数学', '英语')
);
2. 作为数据源(FROM 子句中,需起别名)
将子查询结果作为临时表,再进行查询:
sql
-- 先查每个学生的最高成绩,再筛选出最高成绩 >= 90 的学生
SELECT t.name, t.最高成绩
FROM (SELECT s.name, MAX(c.score) AS 最高成绩FROM student sJOIN course c ON s.id = c.student_idGROUP BY s.id
) t -- 子查询结果作为临时表 t
WHERE t.最高成绩 >= 90;
3. 关联子查询(子查询依赖外部查询的字段)
查询 “每个学生的选课数量”:
sql
SELECT s.name,(SELECT COUNT(*) FROM course c WHERE c.student_id = s.id) AS 选课数量
FROM student s;
五、多表查询对比与适用场景
| 类型 | 核心特点 | 适用场景 |
|---|---|---|
| 内连接 | 只返回匹配记录(交集) | 需关联查询且只关注匹配数据(如 “已选课的学生”) |
| 左外连接 | 保留左表所有记录,右表不匹配则为 NULL | 需展示左表全部数据,关联右表信息(如 “所有学生及其选课情况”) |
| 右外连接 | 保留右表所有记录,左表不匹配则为 NULL | 需展示右表全部数据,关联左表信息(如 “所有订单及其用户信息”) |
| 子查询 | 嵌套查询,逻辑清晰但复杂时性能可能较差 | 条件依赖其他查询结果(如 “查询成绩高于平均分的学生”) |
,反射(Reflection) 和注解(Annotation) 是两个强大的特性,常结合使用以实现灵活的代码逻辑(如框架开发、配置解析等)。以下是对两者的详细解析:
一、反射(Reflection)
反射是指程序在运行时可以访问、检测和修改自身结构(类、方法、字段等)的能力。简单来说,就是通过字节码文件(.class)反向获取类的信息并操作。
核心作用
- 动态获取类的信息(类名、父类、接口、字段、方法等)。
- 动态创建对象(无需在编译期知道类名)。
- 动态调用方法(包括私有方法)、修改字段值(包括私有字段)。
核心类(java.lang.reflect包)
Class:类的字节码对象,反射的入口。Constructor:类的构造方法。Method:类的方法。Field:类的字段。
使用步骤
获取
Class对象(3 种方式):// 1. 通过类名.class Class<?> clazz1 = User.class;// 2. 通过对象.getClass() User user = new User(); Class<?> clazz2 = user.getClass();// 3. 通过Class.forName("全类名")(最常用,动态加载) Class<?> clazz3 = Class.forName("com.example.User");操作类的成员:
// 动态创建对象(调用无参构造) User user = (User) clazz.newInstance();// 获取并调用方法(例如调用setName方法) Method setName = clazz.getMethod("setName", String.class); setName.invoke(user, "Alice"); // 等价于 user.setName("Alice")// 获取并修改字段(包括私有字段) Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); // 暴力访问私有字段 nameField.set(user, "Bob"); // 直接修改私有字段值
优缺点
- 优点:灵活性高,可动态处理未知类(如框架中的 IOC 容器)。
- 缺点:性能开销较大(绕过编译期检查),破坏封装性(可访问私有成员)。
二、注解(Annotation)
注解是一种标记性语言,用于在代码中嵌入元数据(描述数据的数据),可被编译器、虚拟机或工具解析。
核心作用
- 标记代码(如
@Override标记重写方法)。 - 传递配置信息(如
@Controller标记控制器类)。 - 编译期检查(如
@Deprecated标记过时方法)。
常见内置注解
@Override:检查方法是否重写父类。@Deprecated:标记过时元素。@SuppressWarnings:抑制编译器警告。@FunctionalInterface:标记函数式接口。
自定义注解
需使用元注解(描述注解的注解)定义注解的行为:
@Target:指定注解可修饰的元素(类、方法、字段等)。@Retention:指定注解的保留阶段(源码、字节码、运行时)。SOURCE:仅源码可见(如@Override)。CLASS:字节码可见(默认,类加载时丢弃)。RUNTIME:运行时可见(可通过反射获取,最常用)。
@Documented:注解是否包含在 Javadoc 中。@Inherited:注解是否可被子类继承。
示例:
// 自定义注解(运行时可见,可修饰类和方法)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {String value() default "default"; // 注解的属性int num() default 0;
}
使用注解:
@MyAnnotation(value = "class annotation", num = 1)
public class User {@MyAnnotation(value = "method annotation", num = 2)public void sayHello() {}
}
三、反射与注解的结合使用
注解本身不具备逻辑能力,需通过反射在运行时解析注解的元数据并执行相应逻辑(这是框架的核心原理之一,如 Spring 的@Autowired)。
示例:解析User类上的MyAnnotation注解
public class AnnotationParser {public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("com.example.User");// 解析类上的注解if (clazz.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);System.out.println("类注解value:" + annotation.value()); // 输出 "class annotation"System.out.println("类注解num:" + annotation.num()); // 输出 1}// 解析方法上的注解Method[] methods = clazz.getMethods();for (Method method : methods) {if (method.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);System.out.println("方法注解value:" + annotation.value()); // 输出 "method annotation"System.out.println("方法注解num:" + annotation.num()); // 输出 2}}}
}
四、典型应用场景
- 框架开发:Spring 的
@Controller、@Service,MyBatis 的@Select等,通过反射解析注解实现依赖注入、SQL 映射。 - 代码生成:如 Lombok 的
@Data,在编译期通过注解生成 getter/setter(结合注解处理器,非反射)。 - 单元测试:JUnit 的
@Test,通过反射识别测试方法并执行。
代理:
定义目标接口与实现类先创建业务接口(如
UserService、OrderService),并实现具体业务逻辑(如UserServiceImpl、OrderServiceImpl),这些实现类是被代理的 "目标对象"。创建调用处理器(InvocationHandler)自定义
MyInvocationHandler实现InvocationHandler接口,持有目标对象的引用,并在invoke方法中定义增强逻辑:- 方法调用前的操作(如打印 "开始执行" 日志)
- 通过反射调用目标对象的原始方法(
method.invoke(target, args)) - 方法调用后的操作(如打印 "执行结束" 日志)
通过代理工厂生成代理对象
ProxyFactory的createProxy方法通过Proxy.newProxyInstance()创建代理对象,需要传入三个参数:- 目标对象的类加载器(
classLoader) - 目标对象实现的接口数组(
interfaces) - 自定义的调用处理器(
MyInvocationHandler实例)
- 目标对象的类加载器(
通过代理对象执行方法客户端代码(如
ProxyFactoryExample)通过代理对象调用接口方法时:- 实际会触发调用处理器的
invoke方法 - 在
invoke方法中先执行增强逻辑,再调用目标对象的原始方法 - 最终返回执行结果
- 实际会触发调用处理器的
