当前位置: 首页 > news >正文

Java 核心知识点查漏补缺(三)

JavaScript(JS)和 jQuery 是前端开发中密切相关的技术,但两者定位不同:JavaScript 是编程语言,而 jQuery 是基于 JavaScript 的库,用于简化 JS 操作(尤其是 DOM 操作和异步请求)。以下是详细对比和关系解析:

一、核心区别

维度JavaScriptjQuery
本质原生编程语言,浏览器内置支持基于 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 的核心是选择器链式调用

  1. 选择器:通过类似 CSS 的语法获取元素($("选择器"))。

    $("div") // 所有 div 元素
    $("#id") // id 为 "id" 的元素
    $(".class") // class 为 "class" 的元素
    $("ul li") // ul 下的所有 li 元素
    
  2. 链式调用:多个操作可以连写(返回当前 jQuery 对象)。

    // 选中元素 → 添加样式 → 绑定点击事件 → 隐藏
    $("p").css("color", "red").click(function() { alert("点击了") }).hide(500);
    

四、为什么现在 jQuery 用得少了?

近年来,jQuery 的使用率逐渐下降,主要原因:

  1. 浏览器原生 API 优化:现代浏览器(Chrome、Firefox 等)统一了标准,原生 JS 的 DOM 操作(如 querySelector)和 AJAX(fetch API)已足够简洁,无需依赖 jQuery。

    // 原生 JS 现在也很简洁
    document.querySelectorAll("div.box").forEach(div => {div.style.display = "none";
    });
    
  2. 前端框架崛起:React、Vue、Angular 等框架采用组件化思想,自带 DOM 操作逻辑,无需 jQuery。

  3. 功能冗余: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>ObjectMapperJsonNode
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(数据操纵语言):操作数据

用于对表中的数据进行新增、修改、删除,核心语句包括 INSERTUPDATEDELETE

语句作用基础语法示例
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(数据定义语言):定义结构

用于创建、修改、删除数据库中的对象(如表、字段、索引等),核心语句包括 CREATEALTERDROP

语句作用基础语法示例
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(数据控制语言):控制权限

用于管理数据库的访问权限,控制用户对数据的操作范围,核心语句包括 GRANTREVOKE

语句作用基础语法示例
GRANT授予权限GRANT SELECT, INSERT ON user TO 'test'@'localhost';(给用户授予查询 / 新增权限)
REVOKE收回权限REVOKE INSERT ON user FROM 'test'@'localhost';(收回用户的新增权限)

一、数值型数据类型

专注存储整数、小数等数值,核心是 “按范围和精度选择”,避免资源浪费。

核心类型分类
类型分类具体类型关键特点适用场景
整数型TINYINT1 字节,范围小(-128~127),常用于存储状态值性别(1/2)、开关状态(0/1)
INT4 字节,范围适中(-21 亿~21 亿),性能与范围平衡用户 ID、订单号、年龄
BIGINT8 字节,范围极大(-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~21552024毕业年份、产品生产年份
关键注意点
  • 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;

三、聚合函数的关键特性

  1. 忽略 NULL 值:所有聚合函数都会自动忽略字段值为 NULL 的记录(COUNT(*) 除外,它统计所有行,包括 NULL)。例:若某学生 score 为 NULLSUM(score)AVG(score) 会忽略该记录,COUNT(score) 也会忽略,而 COUNT(*) 仍会统计该行。

  2. 与 DISTINCT 配合:可对去重后的数据进行聚合(先去重再计算)。例:统计不同年龄的学生人数(去重年龄后计数):

    sql

    SELECT COUNT(DISTINCT age) AS 不同年龄的数量 FROM student;
    
  3. 不能直接用于 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 表)

  1. 统计高三所有班级中,成绩在 60 分以上的学生总数。
  2. 按班级分组,计算每个班级的成绩总和,并筛选出总和 > 300 分的班级。
  3. 查找每个班级中年龄最大的学生的姓名和年龄(提示:需结合子查询或窗口函数,进阶内容)。

二、内连接(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 对应的学生信息(idname)为 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:类的字段。
使用步骤
  1. 获取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");
    
  2. 操作类的成员

    // 动态创建对象(调用无参构造)
    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}}}
}

四、典型应用场景

  1. 框架开发:Spring 的@Controller@Service,MyBatis 的@Select等,通过反射解析注解实现依赖注入、SQL 映射。
  2. 代码生成:如 Lombok 的@Data,在编译期通过注解生成 getter/setter(结合注解处理器,非反射)。
  3. 单元测试:JUnit 的@Test,通过反射识别测试方法并执行。

代理:

  1. 定义目标接口与实现类先创建业务接口(如UserServiceOrderService),并实现具体业务逻辑(如UserServiceImplOrderServiceImpl),这些实现类是被代理的 "目标对象"。

  2. 创建调用处理器(InvocationHandler)自定义MyInvocationHandler实现InvocationHandler接口,持有目标对象的引用,并在invoke方法中定义增强逻辑:

    • 方法调用前的操作(如打印 "开始执行" 日志)
    • 通过反射调用目标对象的原始方法(method.invoke(target, args)
    • 方法调用后的操作(如打印 "执行结束" 日志)
  3. 通过代理工厂生成代理对象ProxyFactorycreateProxy方法通过Proxy.newProxyInstance()创建代理对象,需要传入三个参数:

    • 目标对象的类加载器(classLoader
    • 目标对象实现的接口数组(interfaces
    • 自定义的调用处理器(MyInvocationHandler实例)
  4. 通过代理对象执行方法客户端代码(如ProxyFactoryExample)通过代理对象调用接口方法时:

    • 实际会触发调用处理器的invoke方法
    • invoke方法中先执行增强逻辑,再调用目标对象的原始方法
    • 最终返回执行结果

http://www.dtcms.com/a/521431.html

相关文章:

  • Linux 线程深度解析:概念、机制与实践
  • 网站运营总监盐城最专业网站建设网站排名优化
  • KP1505X (KP15052SPA/KP15051SPA/ KP1505ASPA) 典型应用 与管脚封装
  • 【数据集1】Global precipitation from FY-3 polar orbit satellites
  • h5游戏免费下载:我要飞的更高
  • 网站开发设计价格爱家影院融合年包是什么
  • 中国网站为什么要备案网站域名备案服务
  • TCP实现聊天
  • 网站设计合同附件wordpress 克隆页面
  • 富文本测试
  • 韩国的小游戏网站网站建设企业排行榜
  • CSRF漏洞攻击(跨站请求伪造攻击)
  • 生活分类网站建设河南响应式建站
  • python爬数据做网站室内设计3d效果图
  • CRLF行结束符问题
  • SpringBoot-Web开发之请求参数处理
  • 区块链技术名词
  • 数据库网站建设高职院校优质校建设专栏网站
  • 回调函数的概念
  • 24.异常
  • Linux用户管理命令详解
  • STM32F4串口通信乱码
  • 网站虚拟交易技术怎么做大型新型网站
  • 易讯网站建设为企业做网站
  • vLLM/Docker 部署Qwen2.5-VL-32B模型
  • 广州设计网站建设电子商务网站建设与维护考试题
  • 单片机的开发(未完待续,有时间写)
  • 酒店内容推荐系统:这5个技术坑90%的人都踩过!
  • 定制型网站开发网站建设服务哪便宜
  • 十大下载网站免费安装网站扫二维码怎么做