JavaEE知识点梳理与整合
一、JavaScript
1、DOM对象
DOM(Document Object Model,文档对象模型)是 JS 操作 HTML/XML 文档的编程接口,它将文档解析为一个由节点(元素、属性、文本、注释等)组成的树形结构(DOM 树),每个节点都是一个 DOM 对象。DOM 对象提供了一系列属性和方法,允许 JS 动态操作文档的结构(增删元素)、样式(修改 CSS)和内容(读写文本)。
适用场景
需要通过 JS 动态交互页面时使用,例如:
- 响应用户事件(点击、输入、滚动等)修改页面内容;
- 动态添加 / 删除元素(如列表加载更多、表单验证提示);
- 修改元素样式(如鼠标悬停时改变颜色、切换主题);
- 获取用户输入数据(如表单提交前验证)。
代码案例
<!DOCTYPE html>
<html>
<body><div id="box">原始内容</div><button id="btn">点击修改</button><script>// 1. 获取DOM对象(元素节点)const box = document.getElementById('box');const btn = document.querySelector('#btn');// 2. 修改内容和样式btn.onclick = function() {box.innerHTML = '修改后的内容'; // 修改元素内容box.style.color = 'red'; // 修改样式box.style.fontSize = '20px';};// 3. 动态添加元素const newP = document.createElement('p'); // 创建p元素newP.textContent = '新添加的段落';document.body.appendChild(newP); // 添加到body中</script>
</body>
</html>
2、JS中的数据类型
JS 数据类型分为基本数据类型和引用数据类型:
-
基本数据类型(值类型):存储在栈内存中,值不可变,赋值时直接复制值。包括:
string(字符串,如"hello");number(数字,如123、3.14);boolean(布尔值,true/false);null(空值,表示 “无”);undefined(未定义,变量声明未赋值时的默认值);symbol(唯一标识符,ES6 新增);bigint(大整数,处理超过Number.MAX_SAFE_INTEGER的值,ES6 新增)。
-
引用数据类型:存储在堆内存中,栈内存中仅存储堆地址(引用),赋值时复制引用。包括:
object(对象,键值对集合);array(数组,有序列表);function(函数,可执行代码块);date(日期)、regexp(正则)等。
适用场景
- 基本类型:存储简单值(如文本、数值、状态标记),适合直接计算或判断(如字符串拼接、数字运算、条件判断)。
- 引用类型:存储复杂结构数据(如用户信息对象、商品列表数组),或封装可复用逻辑(如函数)。
案例
let str = "Hello, World!"; // 字符串
let num = 42; // 数字
let flag = true; // 布尔值
let a = null; // 空值
let b = undefined; // 未定义
let sys = Symbol(); // 符号
let aa = 111111111111111111n; // 大整数
let obj = {name: "John", age: 30}; // 对象
let arr = [1, 2, 3]; // 数组
let fun = function() {console.log("Hello, World!");}; // 函数
let set = new Set([1, 2, 3]); // 集合
let map = new Map([["key1", "value1"], ["key2", "value2"]]); // 映射
let regex = /\w+/g; // 正则表达式
let date = new Date(); // 日期
3、JS的对象创建
JS 中创建对象的常见方式有 5 种,核心是通过 “键值对” 定义属性和方法:
- 对象字面量:用
{}直接定义,简洁直观。 new Object():通过构造函数Object创建,本质与字面量一致。- 自定义构造函数:通过
function定义模板,用new实例化多个同类型对象。 class类(ES6):语法糖,更接近传统面向对象,通过constructor初始化属性,支持方法定义。Object.create():基于现有对象(原型)创建新对象,实现原型继承。
适用场景
- 对象字面量 /
new Object():创建单个简单对象(如配置项、临时数据)。 - 自定义构造函数 /
class:创建多个具有相同结构的对象(如用户、商品实例)。 Object.create():基于已有对象扩展新对象(如复用现有方法,实现继承)。
代码案例
//对象字面量
const person1 = {name: "Alice",age: 25,greet: function() {console.log("Hello, " + this.name);}
};
//Object构造函数
const person2 = new Object();
person2.name = "Bob";
person2.age = 30;
person2.greet = function() {console.log("Hello, " + this.name);
};
//自定义构造函数
function Person(name, age) {this.name = name;this.age = age;this.greet = function() {console.log("Hello, " + this.name);};
}
const person3 = new Person("Charlie", 35);
//class类
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log("Hello, " + this.name);}
}
const person4 = new Person("David", 40);
//使用Object.create()
const proto = {greet: function() {console.log("Hello, " + this.name);}
};
const person5 = Object.create(proto);
person5.name = "Eve";
person5.greet = function() {console.log("Hello, " + this.name);
};
4、JS 的函数定义
函数是封装可执行代码的块,JS 中定义函数的方式有 4 种:
- 函数声明:用
function 函数名(参数) { ... }定义,存在 “函数提升”(可在定义前调用)。 - 函数表达式:用
const 变量名 = function(参数) { ... }定义,无提升(需先定义后调用)。 - 箭头函数(ES6):用
(参数) => { ... }定义,简洁且无自身this(继承外部this),无arguments对象。 Function构造函数:new Function(参数1, 参数2, ..., 函数体),极少使用(性能差,可读性低)。
适用场景
- 函数声明:定义需要在代码前调用的函数(如工具函数)。
- 函数表达式:定义匿名函数或作为参数传递(如回调函数,
setTimeout的回调)。 - 箭头函数:定义简洁的回调(如数组
map/filter),或需要继承外部this的场景(如对象方法中的回调)。 Function构造函数:动态生成函数(极少用,如根据字符串生成代码)。
代码案例
<script>// 函数声明
function greet(name) {console.log("Hello, " + name);
}
greet("Alice");
// 函数表达式
const greet = function(name) {console.log("Hello, " + name);
};
greet("Bob");
// 箭头函数
const greet = (name) => {console.log("Hello, " + name);
};
greet("Charlie");
// 构造函数
const subtract = new Function("a", "b", "return a - b");
console.log(subtract(5, 3)); // 2
</script>
二、前端三大框架
1、Vue.js(国内主流)
背景与定位
- 2014 年由前 Google 工程师尤雨溪发布,现由独立团队(Vue Team)维护,核心定位是渐进式 JavaScript 框架。
- “渐进式” 指可按需整合其功能:从简单的页面交互(只需引入核心库)到复杂应用(结合路由、状态管理等生态工具),灵活性极高。
核心特点
- 模板语法:采用 HTML 模板 + 指令(如
v-if、v-for),贴近原生 HTML,学习成本低,开发者易上手。 - 响应式系统:基于 ES6 的
Proxy实现数据双向绑定(Vue 3),数据变化时自动更新 DOM,无需手动操作。 - 组件化:支持单文件组件(.vue),将 HTML、CSS、JS 封装在同一文件,便于维护。
- 轻量灵活:核心库仅关注视图层,可与现有项目(如 jQuery、React)无缝整合。
- 完善生态:配套路由(Vue Router)、状态管理(Vuex/Pinia)、构建工具(Vite)等,形成完整开发体系。
适用场景
- 中小型项目快速开发(如企业官网、管理后台);
- 需要低学习成本、高开发效率的场景;
- 现有项目的局部功能改造(利用其渐进式特性);
- 国内团队(生态适配国内需求,如微信小程序集成)。
优缺点
- 优点:学习门槛低、文档友好(中文文档完善)、性能优秀、灵活性高。
- 缺点:在超大型企业级应用的架构规范上,不如 Angular 严格;生态规模略小于 React/Angular。
2、React
背景与定位
- 2013 年由 Facebook(现 Meta)开源,现由 Meta 主导维护,核心定位是用于构建用户界面的 JavaScript 库(非完整框架)。
- 以 “组件化” 和 “声明式编程” 为核心,专注于视图层,需结合第三方库(如 React Router、Redux)实现完整应用。
核心特点
- JSX 语法:将 HTML 逻辑嵌入 JavaScript(
const element = <h1>Hello</h1>),打破 HTML 与 JS 的分离,更适合复杂 UI 逻辑。 - 虚拟 DOM:通过内存中的虚拟 DOM 树对比,仅更新变化的部分(Diff 算法),减少真实 DOM 操作,提升性能。
- 单向数据流:数据从父组件流向子组件,状态管理清晰,避免双向绑定的复杂度(大型应用更易维护)。
- 组件化:一切皆组件(函数组件 / 类组件),函数组件(配合 Hooks)为现在主流,逻辑复用更灵活。
- 跨平台能力:通过 React Native 可开发 iOS/Android 原生应用,通过 React Desktop 可开发桌面应用,复用 Web 端逻辑。
适用场景
- 大型复杂应用(如电商平台、社交产品);
- 需要跨平台(Web、移动端、桌面端)复用代码的场景;
- 团队更习惯 JavaScript 逻辑主导 UI 开发(而非 HTML 模板)。
优缺点
- 优点:生态庞大(插件 / 库丰富)、跨平台能力强、适合大型项目、社区活跃(问题解决方案多)。
- 缺点:学习曲线较陡(JSX、Hooks、状态管理等概念需理解);仅关注视图层,需自行整合路由、状态管理等工具。
3、Angular(企业级全栈框架,国外主流)
背景与定位
- 2010 年由 Google 发布 AngularJS(1.x),2016 年重构为 Angular(2+,现最新 16+),是完整的 MVC(MVVM)框架,自带全套解决方案。
- 定位为 “企业级应用开发框架”,强调规范和架构,适合大型团队协作。
核心特点
- 全功能框架:内置路由(Angular Router)、状态管理(Services)、表单验证、HTTP 客户端等,无需依赖第三方库。
- TypeScript 原生支持:强制使用 TypeScript(静态类型语言),提升代码可维护性和错误检查能力(大型项目优势明显)。
- 双向数据绑定:视图与数据自动同步(类似 Vue,但实现方式不同),简化表单等交互场景。
- 依赖注入:核心设计模式,便于代码解耦、测试和复用(企业级应用必备)。
- 指令系统:支持自定义指令(如
*ngFor循环、*ngIf条件渲染),扩展 HTML 功能。
适用场景
- 大型企业级应用(如金融系统、管理后台、ERP 系统);
- 团队规模大、需要严格规范和强类型约束的场景;
- 国外团队(Google 背书,欧美企业 adoption 率高)。
优缺点
- 优点:功能全面(一站式解决方案)、强类型保障(TypeScript)、架构规范(适合大型团队)、Google 长期维护。
- 缺点:学习成本高(需掌握 TypeScript、依赖注入等概念);框架较重,不适合小型项目或快速原型开发。
4、总结
| 框架 | 核心优势 | 学习成本 | 适合场景 | 生态规模 |
|---|---|---|---|---|
| Vue | 渐进式、易上手、灵活 | 低 | 中小型项目、国内团队 | 中 |
| React | 组件化、跨平台、生态庞大 | 中 | 大型复杂应用、跨平台开发 | 大 |
| Angular | 全功能、强规范、企业级支持 | 高 | 大型企业级应用、国外团队 | 中 |
三、AJAX
1、原生JS实现AJAX
1. 核心知识点
AJAX(Asynchronous JavaScript and XML)的本质是通过XMLHttpRequest对象(XHR)实现异步网络请求,核心步骤包括:
- 创建
XMLHttpRequest实例; - 监听
onreadystatechange事件(监控请求状态变化); - 通过
open(method, url, async)初始化请求(async为true表示异步); - 通过
send(data)发送请求(GET请求无需传参,POST需传请求体); - 处理响应:
readyState === 4表示请求完成,status === 200表示请求成功,通过responseText(文本)或responseXML(XML)获取响应数据。
2. 适用场景
- 无任何框架 / 库依赖的原生 JS 环境(如极简页面、老旧系统);
- 需要深度控制请求底层逻辑(如自定义超时、中断请求);
- 追求最小资源加载(避免引入额外库)。
3. 代码案例
// 1. 创建XMLHttpRequest对象const xhr = new XMLHttpRequest();// 2. 配置请求参数xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);// 3. 发送请求xhr.send();// 4. 监听请求状态变化xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {const response = JSON.parse(xhr.responseText);console.log(response);} else if (xhr.readyState === 4 && xhr.status !== 200) {console.error('请求失败');}}
2、JQuery实现AJAX
1. 核心知识点
jQuery 对原生XMLHttpRequest进行了封装,提供了更简洁的 API,核心包括:
- 基础方法:
$.ajax(options)(配置对象包含url、method、data、success、error等); - 简写方法:
$.get(url, data, success, dataType)(GET 请求)、$.post(url, data, success, dataType)(POST 请求); - 自动处理:默认解析 JSON 响应,无需手动
JSON.parse();自动设置部分请求头(如 POST 的Content-Type)。
2. 适用场景
- 已引入 jQuery 的传统项目(如多页应用);
- 快速开发简单异步交互(如表单提交、数据查询);
- 需减少原生 JS 冗余代码(避免手动处理 XHR 状态)。
3. 代码案例
// 基础$.ajax()方法(GET请求)
$.ajax({url: 'https://api.example.com/data',method: 'GET',dataType: 'json', // 预期响应数据类型(自动解析)success: (data) => { // 成功回调console.log('GET响应数据:', data);},error: (xhr, status, err) => { // 失败回调console.error('请求失败:', err);}
});
3、Vue 中 Axios(AJAX 平替)
1. 核心知识点
Axios 是基于 Promise 的 HTTP 客户端,专为现代前端框架设计,是 Vue 生态中替代传统 AJAX 的主流工具,核心特性:
- 支持 Promise API(可结合
async/await简化异步代码); - 自动转换 JSON 数据(请求 / 响应数据自动序列化 / 解析);
- 提供请求拦截器(如统一加 token)和响应拦截器(如统一处理错误);
- 支持浏览器和 Node.js 环境,兼容取消请求、超时设置等高级功能。
2. 适用场景
- Vue/React 等现代前端框架项目(单页应用 SPA);
- 需要处理复杂异步逻辑(如拦截、取消、超时);
- 追求代码可读性(
async/await替代回调地狱); - 需跨端兼容(浏览器和 Node.js 共用请求逻辑)。
在 Vue 项目中全局引入(main.js):
import axios from 'axios';
import Vue from 'vue';
Vue.prototype.$axios = axios; // 挂载到Vue原型,组件中可通过this.$axios使用
4、总结
| 实现方式 | 核心特点 | 适用场景 |
|---|---|---|
| 原生 JS | 底层控制强,无依赖,代码冗余 | 极简环境、自定义底层逻辑 |
| jQuery | 封装简洁,适合传统项目 | 已引入 jQuery 的多页应用 |
| Axios(Vue) | Promise 支持,拦截器,现代框架友好 | Vue/React 项目,复杂异步交互 |
四、JSON
1、JSON 格式:数据交换的 “通用语言”
核心知识点
JSON(JavaScript Object Notation)是一种轻量级的文本数据交换格式,并非编程语言,而是基于 JavaScript 对象字面量语法的通用数据格式。它的核心价值是解决不同语言(如 Java、JS、Python)之间的数据结构兼容性问题,让数据传输和存储更简洁、易读。
关键语法规则
- 数据结构:仅支持两种顶层结构,且可嵌套组合。
- 对象(Object):用
{}包裹,由键值对("key": value)组成,键名必须用双引号包裹。 - 数组(Array):用
[]包裹,元素可以是任意 JSON 支持的数据类型。
- 对象(Object):用
- 支持的数据类型:
- 基本类型:字符串(双引号包裹)、数字、布尔值(
true/false)、null。 - 复合类型:对象(Object)、数组(Array)。
- 基本类型:字符串(双引号包裹)、数字、布尔值(
- 语法限制:不支持函数、注释、
undefined,键名必须用双引号(单引号无效)。
示例
{"name": "张三", // 字符串"age": 25, // 数字"isStudent": false, // 布尔值"hobbies": ["篮球", "编程"], // 数组"address": { // 嵌套对象"city": "北京","street": "XX路"},"grade": null // null
}
2、Fastjson 包:Java 生态的 JSON 处理工具
核心知识点
Fastjson 是阿里巴巴开发的Java 语言 JSON 处理库,核心作用是实现Java 对象与 JSON 格式之间的相互转换,以及 JSON 字符串的解析和生成。它因解析速度快、API 简洁,成为 Java 后端项目中处理 JSON 数据的常用工具(了解即可,无需深入源码)。
主要功能
- 序列化(Serialize):将 Java 对象(如实体类、Map、List)转换为 JSON 字符串。
- 反序列化(Deserialize):将 JSON 字符串转换为 Java 对象(如指定实体类、Map)。
- JSON 解析:直接解析 JSON 字符串,获取其中的指定字段值(无需转换为完整 Java 对象)。
适用场景
- Java 后端接口:接收前端传递的 JSON 参数(反序列化),返回 JSON 格式的响应数据(序列化)。
- 配置文件:读取 JSON 格式的配置文件,转换为 Java 对象供程序使用。
- 数据存储:将 Java 对象序列化为 JSON 字符串,存储到数据库或缓存(如 Redis)中。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;// 1. 序列化:Java对象 → JSON字符串
User user = new User("张三", 25);
String jsonStr = JSON.toJSONString(user);
// 结果:{"name":"张三","age":25}// 2. 反序列化:JSON字符串 → Java对象
User user2 = JSON.parseObject(jsonStr, User.class);// 3. 直接解析JSON字符串
JSONObject jsonObj = JSON.parseObject(jsonStr);
String name = jsonObj.getString("name"); // 直接获取"name"字段值
int age = jsonObj.getIntValue("age"); // 直接获取"age"字段值
3、JS 操作 JSON 对象:前端数据处理核心
在 JavaScript 中,JSON 存在两种形态,操作的核心是两种形态的相互转换,以及对 JSON 对象的增删改查。
1. JS 中 JSON 的两种形态
| 形态 | 定义 | 特点 | 用途 |
|---|---|---|---|
| JSON 对象 | 本质是 JS 的普通对象({key: value}) | 键名可加双引号 / 不加,可直接通过.访问 | 前端代码中直接操作(如渲染页面、逻辑判断) |
| JSON 字符串 | 符合 JSON 语法的字符串('{"key":"value"}') | 键名必须加双引号,是纯文本格式 | 用于网络传输(前后端交互)、本地存储 |
2. 核心转换方法
JS 内置了JSON全局对象,提供两个核心方法实现两种形态的转换。
JSON.stringify ():JSON 对象 → JSON 字符串
作用:将 JS 对象(或数组)转换为符合 JSON 语法的字符串,用于传输或存储。
示例:
const userObj = { name: "张三", age: 25, hobbies: ["篮球", "编程"] };
// 转换为JSON字符串
const jsonStr = JSON.stringify(userObj);
console.log(jsonStr);
// 输出:{"name":"张三","age":25,"hobbies":["篮球","编程"]}
JSON.parse ():JSON 字符串 → JSON 对象
作用:将 JSON 格式的字符串解析为 JS 对象(或数组),供前端代码操作。
示例:
const jsonStr = '{"name":"张三","age":25}';
// 解析为JS对象
const userObj = JSON.parse(jsonStr);
console.log(userObj.name); // 输出:张三(可直接通过.访问属性)
3、JSON 对象的常见操作
转换为 JS 对象后,可通过常规 JS 语法操作:
- 访问属性:
userObj.name或userObj["name"]。 - 修改属性:
userObj.age = 26;。 - 添加属性:
userObj.gender = "男";。 - 删除属性:
delete userObj.hobbies;。 - 遍历对象:
const userObj = { name: "张三", age: 25 };
// 遍历键值对
for (const key in userObj) {console.log(`${key}: ${userObj[key]}`);
}
4、总结
| 知识点 | 核心内容 | 作用 / 应用场景 |
|---|---|---|
| JSON 格式 | 轻量级数据交换格式,有严格语法规则 | 跨语言数据传输(前后端交互)、配置文件 |
| Fastjson 包 | Java 库,实现 Java 对象与 JSON 的相互转换 | Java 后端处理 JSON 数据(接口、存储) |
| JS 操作 JSON | 两种形态(对象 / 字符串)+ 转换方法 + 增删改查 | 前端接收 / 处理后端 JSON 数据、本地存储 |
五、服务器
1、C/S 结构 vs B/S 结构
| 对比维度 | C/S 结构(客户端 / 服务器) | B/S 结构(浏览器 / 服务器) |
|---|---|---|
| 核心定义 | 需在用户设备安装专用客户端软件(如 APP、PC 客户端),通过客户端与服务器交互。 | 无需安装专用软件,以浏览器(如 Chrome、Safari)作为统一客户端,通过 HTTP 协议与服务器交互。 |
| 组成部分 | 1. 客户端(专用软件,如微信 APP、QQ 音乐 PC 端);2. 服务器(处理业务逻辑、存储数据)。 | 1. 浏览器(通用客户端);2. Web 服务器(接收 HTTP 请求);3. 应用服务器 / 数据库服务器(处理业务、存储数据)。 |
| 关键特点 | - 客户端功能强大,可实现复杂交互(如游戏、图形处理);- 需单独开发、安装、更新客户端;- 安全性较高(客户端可做本地加密)。 | - 无需安装客户端,跨平台(只要有浏览器即可);- 维护成本低(仅需更新服务器端);- 依赖网络和浏览器性能,交互复杂度受限。 |
| 适用场景 | - 对交互体验、功能复杂度要求高的场景(如手机 APP、大型游戏、企业内部管理系统);- 需离线使用部分功能的场景(如离线地图)。 | - 无需专用客户端的通用场景(如网页、在线商城、新闻网站);- 跨设备访问的场景(如电脑、手机、平板均可打开的网页);- 快速迭代、低维护成本的场景(如自媒体平台)。 |
| 典型示例 | 微信 APP、王者荣耀(手游客户端)、钉钉 PC 端、Photoshop(云同步部分)。 | 淘宝网页版、百度搜索、知乎网页、企业官网(如小米官网)。 |
2、常见服务器类型
1. Web 服务器(HTTP 服务器)
- 核心作用:专门处理客户端的HTTP/HTTPS 请求,返回静态资源(如 HTML、CSS、JS、图片)或转发动态请求到应用服务器。
- 关键特点:专注于 “接收请求 - 返回资源” 的基础 HTTP 交互,不处理复杂业务逻辑。
- 典型产品:
- Nginx:轻量级、高性能,常用于负载均衡(分发多台服务器的请求)、静态资源缓存。
- Apache:老牌 Web 服务器,兼容性强,配置灵活。
- IIS:微软开发,适用于 Windows 服务器环境,常与ASP.NET配合使用。
2. 应用服务器
- 核心作用:运行业务逻辑代码(如用户登录验证、订单处理、数据计算),是动态网页 / 应用的 “大脑”。
- 关键特点:需与 Web 服务器或客户端配合,接收请求后调用数据库、处理数据,再返回结果(通常是 JSON、动态 HTML)。
- 典型产品:
- Tomcat:轻量级应用服务器,支持 Java Web 应用(如 Spring Boot 项目)。
- Jetty:与 Tomcat 类似,更灵活,常用于嵌入式场景(如手机 APP 后端)。
- WebLogic:Oracle 开发的企业级应用服务器,支持复杂 Java EE 应用(如银行核心系统)。
3. 数据库服务器
- 核心作用:专门用于存储和管理数据,提供数据的增删改查(CRUD)服务,响应应用服务器的数据请求。
- 关键特点:需保证数据的安全性、一致性和高性能(如并发查询、数据备份)。
- 典型产品:
- MySQL:开源关系型数据库,适用于中小型应用(如博客、电商网站)。
- PostgreSQL:功能强大的开源关系型数据库,支持复杂查询和扩展。
- MongoDB:非关系型数据库(NoSQL),适用于存储非结构化数据(如用户行为日志、图片链接)。
4. 其他常见服务器
- 文件服务器:专门存储和管理文件(如图片、视频、文档),提供文件上传、下载服务(如网盘后端)。
- 邮件服务器:处理邮件的发送、接收和存储(如企业邮箱服务器,基于 SMTP、POP3 协议)。
3、静态网页 vs 动态网页
静态网页和动态网页的核心区别在于网页内容是否在服务器端动态生成,以及是否与服务器进行 “实时交互”。
对比总结
| 对比维度 | 静态网页(Static Web Page) | 动态网页(Dynamic Web Page) |
|---|---|---|
| 核心定义 | 内容在开发时固定,存储为本地 HTML 文件,服务器接收请求后直接返回原文件,内容不会随请求变化。 | 内容在服务器端实时生成,服务器接收请求后,通过应用程序(如 PHP、Java)调用数据库、处理逻辑,动态拼接 HTML 内容返回给客户端。 |
| 内容生成时机 | 开发阶段生成,后续无变化(除非手动修改文件)。 | 客户端发送请求时,服务器实时生成。 |
| 技术栈 | 仅需 HTML(结构)、CSS(样式)、JavaScript(前端交互,不涉及服务器)。 | 基础:HTML/CSS/JS;服务器端:PHP、Java(Spring Boot)、Python(Django/Flask)、Node.js;数据库:MySQL、MongoDB。 |
| 关键特点 | - 加载速度快(直接返回文件,无需服务器处理);- 内容固定,无法个性化(如不同用户看到相同内容);- 维护成本高(修改需逐个更新 HTML 文件)。 | - 内容动态变化(如登录后显示用户名、电商商品实时库存);- 支持个性化(如根据用户偏好推荐内容);- 依赖服务器和数据库,加载速度略慢于静态网页。 |
| 适用场景 | - 内容固定、更新频率低的页面(如企业官网介绍页、个人博客静态文章);- 对加载速度要求高的场景(如 CDN 分发的静态资源)。 | - 内容动态变化的页面(如电商商品页、用户中心、论坛帖子);- 需与用户交互的场景(如登录、注册、提交表单)。 |
| 典型示例 | 纯 HTML 编写的个人简历网页、静态博客(如用 Hexo 生成的页面)。 | 淘宝商品详情页(实时显示库存、价格)、知乎问答页(实时显示评论数)、抖音网页版(实时推荐视频)。 |
静态与动态的结合
现代 Web 开发中,纯静态或纯动态的场景较少,更多是 “动静结合”:
- 静态部分:网页框架(如导航栏、页脚)、CSS/JS 文件,通过 CDN 加速分发。
- 动态部分:用户数据(如昵称、订单)、实时内容(如新闻列表、商品评论),通过 AJAX 异步请求从服务器
六、HTTP协议
1、HTTP 协议:网络通信的 “通用语言”
核心概念
HTTP(HyperText Transfer Protocol,超文本传输协议)是客户端与服务器之间进行数据交互的应用层协议,基于 TCP/IP 协议工作,定义了客户端如何向服务器请求资源、服务器如何响应请求的规则。
- 核心作用:实现 “超文本”(如 HTML、图片、视频等)的传输,是万维网(WWW)的基础通信协议。
- 工作方式:采用 “请求 - 响应” 模式 —— 客户端(如浏览器、APP)发送请求,服务器接收后处理并返回响应,完成一次交互后断开 TCP 连接(HTTP/1.1 默认支持长连接,但本质仍是请求 - 响应模型)。
2、无状态协议:HTTP 的核心特性
核心定义
HTTP 是无状态协议,指服务器不会保留客户端的历史请求信息,每个请求都是独立的、全新的交互。服务器无法通过前一次请求的上下文判断当前请求的用户身份或状态。
关键细节
- “无状态” 的体现:例如,用户第一次登录网站后,服务器处理登录请求并返回成功响应,但不会记录 “该用户已登录”;当用户再次点击 “个人中心” 时,服务器无法直接识别 “这是刚才登录的用户”,需重新验证。
- 带来的问题:无法天然支持 “状态保持”(如登录状态、购物车数据),影响用户体验。
- 解决方案:通过额外技术弥补无状态的缺陷,常见方式包括:
- Cookie:服务器在响应中添加
Set-Cookie头,客户端(浏览器)存储小型文本数据,后续请求自动携带 Cookie,服务器通过 Cookie 识别用户。 - Session:服务器为每个用户创建独立的 Session(内存 / 文件 / 数据库存储),并将 SessionID 通过 Cookie 返回给客户端;客户端后续请求携带 SessionID,服务器通过 ID 找到对应的 Session 数据。
- Token:客户端登录后,服务器生成加密 Token 返回;客户端后续请求在 Header 中携带 Token,服务器验证 Token 有效性以识别用户(适用于跨域场景,如前后端分离、APP)。
- Cookie:服务器在响应中添加
3、HTTP 请求协议:客户端如何 “提问”
HTTP 请求是客户端向服务器发送的 “请求信息”,包含请求行、请求头、请求体三部分,结构固定且由 ASCII 字符组成(特殊数据需编码)。
1. 请求行(Request Line)
定义请求的核心信息,格式:请求方法 URL HTTP版本
-
请求方法:表示客户端对资源的操作意图,常见包括:
GET:获取资源(如浏览网页、查询数据),请求体为空,参数通过 URL 的查询字符串(?key=value)传递。POST:提交数据(如登录、表单提交),参数通过请求体传递,适合大量数据或敏感信息。- 其他:
PUT(更新资源)、DELETE(删除资源)、PATCH(部分更新)等。
-
URL:请求的资源路径(如
/api/user、https://example.com/index.html)。 -
HTTP 版本:如
HTTP/1.1(主流)、HTTP/2(支持多路复用)。示例:
POST /api/login HTTP/1.1
2. 请求头(Request Headers)
键值对格式的元数据,描述请求的附加信息,由客户端自动添加或手动设置。常见请求头:
Host:目标服务器的域名或 IP(如Host: example.com)。User-Agent:客户端身份(如浏览器型号、APP 版本,User-Agent: Mozilla/5.0 (Windows NT 10.0; ...))。Content-Type:请求体的数据格式(如application/json表示 JSON 数据,application/x-www-form-urlencoded表示表单数据)。Cookie:客户端携带的 Cookie 数据(如Cookie: sessionId=abc123)。Authorization:身份验证信息(如 Token,Authorization: Bearer xyz456)。
3. 请求体(Request Body)
可选部分,仅在POST、PUT等方法中使用,用于传递请求数据(如表单内容、JSON 对象)。
示例(JSON 格式):
{ "username": "test", "password": "123456" }
示例(表单格式):
username=test&password=123456
4、HTTP 状态码:服务器如何 “回答”
服务器响应中包含状态码,用于表示请求的处理结果(用户提到的 “状态协议” 可能指状态码相关内容)。状态码为 3 位数字,分为 5 类:
| 类别 | 含义 | 常见状态码及说明 |
|---|---|---|
| 1xx | 信息性响应 | 100 Continue:服务器已接收请求头,客户端可继续发送请求体。 |
| 2xx | 成功响应 | 200 OK:请求成功,响应体包含数据;201 Created:资源创建成功(如新增用户)。 |
| 3xx | 重定向 | 301 Moved Permanently:资源永久迁移(如域名更换);302 Found:临时重定向。 |
| 4xx | 客户端错误 | 400 Bad Request:请求格式错误;401 Unauthorized:未登录或 Token 无效;404 Not Found:资源不存在。 |
| 5xx | 服务器错误 | 500 Internal Server Error:服务器内部逻辑错误;503 Service Unavailable:服务器维护中。 |
七、数据库
1、数据库基础
01、数据库概念
核心概念
数据库(Database,简称 DB)是按照一定规则存储、组织和管理数据的集合,本质是一个 “结构化的存储系统”,区别于普通的文件(如 TXT、Excel)存储,能更高效地实现数据的增删改查、共享和安全控制。
- 关键特点:数据按预设结构(如表格、文档)组织,支持多用户并发访问,具备数据一致性(避免重复或冲突)和安全性(控制访问权限)。
- 与普通文件的区别:普通文件存储无固定结构,查询数据需逐行扫描(效率低),且无法解决多用户同时修改的冲突;数据库通过特定结构和管理系统,实现高效查询和并发控制。
02、数据库的核心作用
数据库的核心价值是解决 “大量数据的高效管理与使用” 问题,具体作用可分为以下 4 点:
- 结构化存储:将零散数据按规则分类组织(如用户表、订单表),避免数据混乱,方便后续查找。
- 高效数据操作:支持通过简单指令(如 SQL 语句)快速实现数据的增删改查,无需手动处理数据逻辑(如筛选、排序)。
- 数据共享与并发控制:允许多个用户 / 程序同时访问数据,通过 “锁机制” 等避免多人修改导致的数据冲突(如两个用户同时修改同一条订单)。
- 数据安全与维护:提供权限控制(如普通用户只能查看数据,管理员可修改)、数据备份(防止丢失)、故障恢复(如服务器宕机后恢复数据)等功能。
03、关系型数据库
核心定义
关系型数据库(Relational Database,简称 RDBMS)是采用 “关系模型”(即表格结构)组织数据的数据库,数据存储在多个相互关联的 “表” 中,表与表之间通过 “主键” 和 “外键” 建立关联,形成结构化的关系网络。
关键特征
- 表格结构:数据以 “表(Table)” 为单位存储,表由 “行(Row,对应一条数据记录)” 和 “列(Column,对应数据的属性)” 组成。例如,“用户表” 包含 “用户 ID”“姓名”“年龄” 等列,每一行是一个用户的具体信息。
- 主键与外键:
- 主键(Primary Key):表中唯一标识一条记录的列(如 “用户表” 的 “用户 ID”),确保数据不重复。
- 外键(Foreign Key):表中引用另一张表主键的列,用于建立表之间的关联。例如,“订单表” 的 “用户 ID” 列引用 “用户表” 的 “用户 ID”,表示 “该订单属于哪个用户”。
- SQL 语言:通过结构化查询语言(SQL,Structured Query Language)实现数据操作,语法统一(如
SELECT * FROM 用户表查询所有用户),学习成本低。 - ACID 特性:确保数据操作的可靠性(尤其适用于转账、订单提交等关键场景):
- 原子性(Atomicity):一个操作要么全部完成,要么全部失败(如转账时,“扣款” 和 “到账” 必须同时成功或同时失败)。
- 一致性(Consistency):操作前后数据符合预设规则(如转账后,总金额不变)。
- 隔离性(Isolation):多用户并发操作时,相互不干扰(如 A 用户修改数据时,B 用户看不到中间未完成的状态)。
- 持久性(Durability):操作完成后,数据永久保存(即使服务器断电,数据也不会丢失)。
04、常见的数据库类型及产品
数据库主要分为关系型数据库和非关系型数据库(NoSQL) 两大类,各自适用于不同场景。
1. 常见关系型数据库
| 数据库产品 | 核心特点 | 适用场景 |
|---|---|---|
| MySQL | 开源免费,轻量级,性能稳定,社区活跃;支持多种操作系统和编程语言。 | 中小型应用(如博客、电商网站、企业后台),是互联网行业最常用的关系型数据库。 |
| PostgreSQL | 开源,功能强大(支持复杂查询、自定义函数、JSON 数据类型),兼容性好,稳定性高。 | 对数据完整性和功能扩展性要求高的场景(如金融、科研数据管理)。 |
| Oracle | 闭源商业数据库,性能极强,支持海量数据和高并发,提供完善的企业级功能(如容灾、安全)。 | 大型企业核心系统(如银行交易系统、电信计费系统),成本较高。 |
| SQL Server | 微软开发的商业数据库,与 Windows 系统、.NET 框架兼容性极佳,易用性强。 | 基于 Windows 服务器的企业应用(如政府、国企内部管理系统)。 |
2. 常见非关系型数据库(补充了解)
非关系型数据库不采用表格结构,数据存储形式更灵活(如文档、键值对、图形),适用于非结构化 / 半结构化数据或高并发场景:
- MongoDB:文档型数据库,数据以 JSON 格式存储,适合存储非结构化数据(如用户评论、商品描述、日志)。
- Redis:键值对数据库,基于内存存储,读写速度极快,常用于缓存(如网页热点数据)、会话存储(如用户登录状态)。
- Cassandra:列存储数据库,支持海量数据和高并发写入,适用于大数据场景(如物联网数据采集)。
2、SQL分类
SQL 按功能主要分为 DDL、DML、DQL、DCL 四类,它们分别对应数据库的结构定义、数据操纵、数据查询和权限控制,分工明确。
1. DDL(数据定义语言):定义数据库结构
DDL 用于创建、修改和删除数据库及其对象(如表、视图、索引等),它操作的是数据库的 “骨架”,而非具体数据。执行 DDL 语句后,数据库会自动提交事务,无需手动提交。
- 核心作用:定义和管理数据库、表、列等结构信息。
- 常用命令:
CREATE:创建数据库或表,如CREATE DATABASE test;、CREATE TABLE user(id INT);。ALTER:修改表结构,如ALTER TABLE user ADD COLUMN name VARCHAR(20);。DROP:删除数据库或表,如DROP TABLE user;(此操作不可逆,需谨慎)。TRUNCATE:清空表中所有数据(保留表结构),如TRUNCATE TABLE user;。
2. DML(数据操纵语言):操作表中数据
DML 用于对表中的数据进行增、删、改操作,它操作的是表中的 “内容”。执行 DML 语句后,需要手动通过 COMMIT 提交事务,或通过 ROLLBACK 回滚。
- 核心作用:插入、更新、删除表中的具体数据行。
- 常用命令:
INSERT:插入数据,如INSERT INTO user(id, name) VALUES(1, 'Tom');。UPDATE:更新数据,如UPDATE user SET name='Jerry' WHERE id=1;(务必加 WHERE 条件,否则更新全表)。DELETE:删除数据,如DELETE FROM user WHERE id=1;(同样需加 WHERE 条件,否则删除全表数据)。
3. DQL(数据查询语言):查询表中数据
DQL 专门用于从表中查询和提取数据,是日常使用最频繁的 SQL 类型。它只读取数据,不会修改数据库结构或数据本身,核心命令只有 SELECT。
- 核心作用:根据条件筛选、排序、聚合表中的数据,返回结果集。
- 常用命令:
- 仅
SELECT,但需结合子句使用,如:SELECT id, name FROM user;(查询指定列)。SELECT * FROM user WHERE id > 5 ORDER BY name;(带条件和排序的查询)。SELECT COUNT(*) FROM user GROUP BY age;(带聚合函数和分组的查询)。
- 仅
4. DCL(数据控制语言):控制数据库权限
DCL 用于管理数据库用户的权限,决定谁能访问数据库、能执行哪些操作,确保数据安全。
- 核心作用:授予或撤销用户对数据库的操作权限。
- 常用命令:
GRANT:授予权限,如GRANT SELECT, INSERT ON test.user TO 'user1'@'localhost';。REVOKE:撤销权限,如REVOKE INSERT ON test.user FROM 'user1'@'localhost';。COMMIT/ROLLBACK:严格来说属于事务控制语言(TCL),但有时也被归入 DCL,用于提交或回滚事务。
四类 SQL 核心区别对比
| 分类 | 核心作用 | 操作对象 | 常用命令 | 事务特性 |
|---|---|---|---|---|
| DDL | 定义结构 | 数据库、表、索引等 | CREATE, ALTER, DROP | 自动提交 |
| DML | 操纵数据 | 表中的数据行 | INSERT, UPDATE, DELETE | 需手动提交 / 回滚 |
| DQL | 查询数据 | 表中的数据(只读) | SELECT(及子句) | 无事务影响 |
| DCL | 控制权限 | 用户、权限 | GRANT, REVOKE | 自动提交 |
3、单表的约束
约束的核心好处是强制数据遵循特定规则,从而确保数据的准确性、一致性和完整性,避免脏数据的产生。
约束的核心好处
- 保证数据完整性:防止输入无效、错误或不一致的数据,例如避免在 “年龄” 字段输入负数。
- 维护数据一致性:确保表内及表间数据的逻辑关系正确,例如外键约束能保证订单表中的 “用户 ID” 在用户表中一定存在。
- 简化业务逻辑:将数据验证规则交由数据库层面执行,减少应用程序代码的负担和出错概率。
四种核心单表约束详解
1. 主键约束(Primary Key Constraint)
主键约束用于唯一标识表中的每一条记录,是表的 “唯一身份证”。
- 一个表只能有一个主键。
- 主键字段的值必须唯一且非空,不允许重复或 NULL 值。
- 示例:用户表(user_id)、订单表(order_id),通过主键可以精准定位到某一个用户或某一笔订单。
2. 唯一约束(Unique Constraint)
唯一约束用于确保指定字段的值在表中是唯一的,但允许存在一个 NULL 值。
- 一个表可以有多个唯一约束。
- 唯一约束的字段值不允许重复,但可以为空(NULL)。
- 示例:用户表(email),确保每个用户的邮箱不重复,但允许部分用户暂未填写邮箱(值为 NULL)。
3. 非空约束(Not Null Constraint)
非空约束强制指定字段的值不能为空(NULL),即该字段是必填项。
- 仅限制字段不允许出现 NULL 值,不限制重复。
- 示例:用户表(username),确保每个用户都必须有用户名,不允许创建 “无名” 用户。
4. 外键约束(Foreign Key Constraint)
外键约束用于建立两个表之间的关联关系,它在一个表中引用另一个表的主键。
- 外键字段的值必须匹配引用表(主表)中主键的有效值,或为 NULL(若允许)。
- 作用是维护表间数据的一致性,防止引用不存在的数据。
- 示例:订单表(user_id)作为外键,引用用户表(user_id)的主键,确保每一笔订单都对应一个真实存在的用户,避免出现 “无主订单”。
总结
| 约束类型 | 核心特点 | 允许空值? | 主要作用 |
|---|---|---|---|
| 主键约束 | 唯一标识记录,表中唯一 | 不允许 | 唯一定位一条记录 |
| 唯一约束 | 字段值唯一,表中可多个 | 允许(一个) | 防止字段值重复 |
| 非空约束 | 字段值必填 | 不允许 | 确保核心信息不缺失 |
| 外键约束 | 关联另一表的主键 | 可配置 | 维护表间数据的引用完整性 |
4、CURD
一、数据表结构的增删改查(DDL)
针对表的结构(字段、类型、约束等)进行操作,核心是 “创建、修改、删除表,以及查询表结构”。
1. 增(创建表):CREATE TABLE
定义:创建新的数据表,指定表名、字段名、数据类型及约束。
语法:
CREATE TABLE 表名 (字段名1 数据类型 [约束],字段名2 数据类型 [约束],...[表级约束]
);
2. 改(修改表结构):ALTER TABLE
定义:修改已存在表的结构(如添加 / 删除字段、修改字段类型、添加约束等)。常见操作:
添加字段:ALTER TABLE 表名 ADD 字段名 数据类型 [约束];示例:给user表添加phone字段
示例:给user表添加phone字段
ALTER TABLE user ADD phone VARCHAR(20);
修改字段类型 / 约束:ALTER TABLE 表名 MODIFY 字段名 新数据类型 [新约束];示例:将age字段改为TINYINT且非空
示例:将age字段改为TINYINT且非空
ALTER TABLE user MODIFY age TINYINT NOT NULL;
删除字段:ALTER TABLE 表名 DROP 字段名;
示例:删除phone字段
ALTER TABLE user DROP phone;
3. 删(删除表):DROP TABLE
定义:删除整个数据表(包括结构和所有数据,不可恢复)。
语法:
DROP TABLE [IF EXISTS] 表名; -- IF EXISTS 避免表不存在时报错
4. 查(查询表结构)
定义:查看表的字段、类型、约束等结构信息。常用命令:
-
DESCRIBE 表名;(或缩写DESC 表名;):查看字段基本信息示例:DESC user; -
SHOW CREATE TABLE 表名;:查看表的完整创建语句(包括约束、引擎等)
二、表中数据的增删改查(DML,即 CRUD)
针对表中的记录(行)进行操作,核心是 “新增、修改、删除、查询数据”。
1. 增(新增数据):INSERT
定义:向表中插入一条或多条新记录。语法:
- 插入指定字段:
INSERT INTO 表名 (字段1, 字段2, ...) VALUES (值1, 值2, ...); - 插入所有字段(按表结构顺序):
INSERT INTO 表名 VALUES (值1, 值2, ...); - 批量插入:
INSERT INTO 表名 (字段1, 字段2) VALUES (值1, 值2), (值3, 值4), ...;
示例:向user表插入数据
-- 插入单条(指定字段)
INSERT INTO user (username, age, email) VALUES ('张三', 20, 'zhangsan@test.com');-- 批量插入
INSERT INTO user (username, email) VALUES ('李四', 'lisi@test.com'),('王五', 'wangwu@test.com');
2. 改(修改数据):UPDATE
定义:更新表中已存在的记录(需配合WHERE指定条件,否则会修改所有记录)。
语法:
UPDATE 表名
SET 字段1 = 新值1, 字段2 = 新值2, ...
[WHERE 条件]; -- 必加!否则全表更新
示例:将username为 “张三” 的用户年龄改为 22
UPDATE user
SET age = 22
WHERE username = '张三';
3. 删(删除数据):DELETE / TRUNCATE
定义:删除表中的记录(注意区分 “删除数据” 和 “删除表” 的区别)。
| 操作方式 | 语法 | 特点 | 适用场景 |
|---|---|---|---|
| DELETE(逐行删) | DELETE FROM 表名 [WHERE 条件]; | 1. 按条件删除记录,可回滚(事务中);2. 不重置自增主键;3. 速度较慢(逐行删除)。 | 删除部分记录(需保留表结构) |
| TRUNCATE(清空表) | TRUNCATE TABLE 表名; | 1. 直接清空全表数据,不可回滚;2. 重置自增主键;3. 速度快(类似重建表)。 | 清空全表数据(保留表结构) |
| DROP(删表) | DROP TABLE 表名; | 删除表结构及所有数据,不可恢复(属于 DDL,非数据删除)。 | 彻底删除表(不保留结构) |
示例:
- 删除
age < 18的用户:DELETE FROM user WHERE age < 18; - 清空
user表数据(保留结构):TRUNCATE TABLE user;
4. 查(查询数据):SELECT
定义:从表中查询符合条件的记录,是最复杂也最常用的操作。
(1)基本查询
语法:SELECT 字段1, 字段2, ... FROM 表名;(*表示查询所有字段)
示例:查询user表所有用户的username和age
SELECT username, age FROM user;
(2)查询中使用运算和别名
运算:对字段进行计算(如算术运算、字符串拼接等)。
示例:查询用户 “年龄 + 10” 后的结果
SELECT username, age + 10 AS future_age FROM user;
别名:用AS给字段或表起别名(简化语句),AS可省略。
示例:给表起别名u,字段起别名
SELECT u.username AS 用户名, u.age 年龄 FROM user u; -- AS可省略
(3)条件查询:WHERE子句
语法:SELECT 字段 FROM 表名 WHERE 条件;常用条件:
- 比较运算符:
=(等于)、!=/<>(不等于)、>、<、>=、<= - 逻辑运算符:
AND(且)、OR(或)、NOT(非) - 范围:
BETWEEN a AND b(在 a 到 b 之间,含边界)、IN (值1, 值2)(在指定集合中) - 空值:
IS NULL(为空)、IS NOT NULL(不为空) - 模糊查询:
LIKE(%匹配任意字符,_匹配单个字符)
(4)排序:ORDER BY
语法:SELECT 字段 FROM 表名 [WHERE 条件] ORDER BY 字段 [ASC/DESC];
ASC:升序(默认,可省略);DESC:降序。- 支持多字段排序(先按第一个字段排,再按第二个字段排)。
示例:按年龄降序排列,年龄相同则按用户名升序
SELECT * FROM user ORDER BY age DESC, username ASC;
(5)分组:GROUP BY + HAVING
定义:将记录按指定字段分组,通常配合聚合函数(COUNT、SUM、AVG等)使用。
WHERE:分组前筛选记录(不能用聚合函数);HAVING:分组后筛选组(可使用聚合函数)
语法:
SELECT 分组字段, 聚合函数(字段)
FROM 表名
[WHERE 条件]
GROUP BY 分组字段
[HAVING 分组条件];
总结
- 数据表结构操作(DDL):用
CREATE TABLE(增)、ALTER TABLE(改)、DROP TABLE(删)、DESC/SHOW CREATE TABLE(查)。 - 数据操作(DML):
- 增:
INSERT; - 改:
UPDATE(必加WHERE); - 删:
DELETE(删部分)、TRUNCATE(清空全表)、DROP(删表,非数据操作); - 查:
SELECT(支持条件、排序、分组等复杂查询)。
- 增:
5、聚合函数
概念
聚合函数是数据库中用于对一组数据进行汇总计算并返回单一结果的函数,主要用于统计、分析数据(如求和、平均值、最大值等)。它们通常作用于表中的一列或多列数据,忽略NULL值(除特殊情况外),且常与GROUP BY结合使用(对分组后的数据进行聚合),也可单独使用(对全表数据进行聚合)。
常见聚合函数
1. COUNT():统计记录行数
2. SUM():计算数值总和
3. AVG():计算平均值
4. MAX():获取最大值
5. MIN():获取最小值
6. GROUP_CONCAT()(MySQL 特有):拼接字符串
聚合函数的核心特点
- 忽略
NULL值:除COUNT(*)外,所有聚合函数都会自动忽略字段值为NULL的行。 - 与
GROUP BY配合:若使用GROUP BY,聚合函数会对每个分组单独计算结果;若不使用,则对全表数据整体计算。 - 不能直接用于
WHERE:聚合函数的结果不能直接作为WHERE的条件(WHERE在分组前执行,无法识别聚合结果),需用HAVING(分组后筛选)。
6、多表查询
多表查询是数据库中针对存在关联关系的多个表(通常通过外键关联)进行数据查询的操作,核心是通过关联条件将多个表 “连接” 起来,或通过子查询嵌套实现复杂逻辑。
一、多表查询的前提
多表之间需存在逻辑关联(如 “用户表” 和 “订单表” 通过 “用户 ID” 关联),通常通过外键约束保证关联的合法性。查询时需指定关联条件(如ON 表1.字段 = 表2.字段),否则会产生 “笛卡尔积”(两表行数相乘的冗余结果)。
二、内连接(INNER JOIN):取两表的 “交集”
内连接只返回两个表中满足关联条件的匹配行,不匹配的行不会显示。是最常用的多表连接方式。
示例
假设有两个表:
user(用户表):id(主键)、username(用户名)order(订单表):id(订单 ID)、user_id(外键,关联user.id)、amount(金额)
需求:查询所有用户及其对应的订单信息(只显示有订单的用户)。
SELECT u.username, o.id AS order_id, o.amount
FROM user u
INNER JOIN `order` o -- 注意order是关键字,需用反引号包裹
ON u.id = o.user_id; -- 关联条件:用户ID匹配
结果特点:只包含 “有订单的用户” 和 “属于该用户的订单”,没有订单的用户不会出现。
三、外连接:保留 “主表” 的所有行,匹配副表的行
外连接会保留其中一个表(主表)的所有行,另一个表(副表)中不匹配的行用NULL填充。根据主表的不同,分为左外连接、右外连接和全外连接。
1. 左外连接(LEFT JOIN / LEFT OUTER JOIN):左表为主表
以左表为基准,返回左表的所有行,以及右表中满足关联条件的行;右表不匹配的行用NULL填充。
示例:
需求:查询所有用户(包括没有订单的用户)及其对应的订单信息
SELECT u.username, o.id AS order_id, o.amount
FROM user u
LEFT JOIN `order` o
ON u.id = o.user_id;
结果特点:所有用户都会显示;有订单的用户会显示订单信息,没有订单的用户其order_id和amount为NULL。
2. 右外连接(RIGHT JOIN / RIGHT OUTER JOIN):右表为主表
以右表为基准,返回右表的所有行,以及左表中满足关联条件的行;左表不匹配的行用NULL填充。
示例:
需求:查询所有订单(包括订单对应的用户不存在的情况)及对应的用户信息。
SELECT u.username, o.id AS order_id, o.amount
FROM user u
RIGHT JOIN `order` o
ON u.id = o.user_id;
结果特点:所有订单都会显示;有对应用户的订单会显示用户名,无对应用户的订单其username为NULL(通常因外键约束,这种情况很少见)。
3. 全外连接(FULL JOIN / FULL OUTER JOIN):保留两表所有行
返回左表和右表的所有行,两表中不匹配的行分别用NULL填充。
注意:MySQL 不直接支持FULL JOIN,需用LEFT JOIN + UNION + RIGHT JOIN模拟。
示例:
-- 左连接结果 + 右连接中左表不匹配的部分(去重)
SELECT u.username, o.id AS order_id, o.amount
FROM user u
LEFT JOIN `order` o ON u.id = o.user_id
UNION -- 合并结果并去重
SELECT u.username, o.id AS order_id, o.amount
FROM user u
RIGHT JOIN `order` o ON u.id = o.user_id;
四、子查询:嵌套在其他查询中的查询
子查询(Subquery)是嵌套在SELECT、INSERT、UPDATE、DELETE语句中的查询,通常用于为外层查询提供条件或数据源。子查询的结果可作为 “值”“列表” 或 “临时表” 使用。
子查询的位置及用法
01、作为条件(在WHERE子句中)
子查询返回单个值(单行单列)或多个值(多行单列),外层查询用=、IN、ANY、ALL等操作符判断。
单行子查询(返回一个值):用=、>、<等
示例:查询 “订单金额大于平均订单金额” 的订单
SELECT * FROM `order`
WHERE amount > (SELECT AVG(amount) FROM `order`); -- 子查询返回平均金额
多行子查询(返回多个值):用IN(在列表中)、ANY(任意一个)、ALL(所有)
示例:查询 “购买过金额≥1000 的订单” 的用户
SELECT username FROM user
WHERE id IN (SELECT user_id FROM `order` WHERE amount >= 1000); -- 子查询返回符合条件的user_id列表
02、作为数据源(在FROM子句中)
子查询返回多行多列,作为 “临时表”(需起别名),外层查询对临时表进行操作。
示例:查询 “每个用户的订单总金额”,并筛选出总金额≥2000 的用户
SELECT u.username, t.total_amount
FROM user u
INNER JOIN (SELECT user_id, SUM(amount) AS total_amount -- 子查询:计算每个用户的总订单金额FROM `order` GROUP BY user_id
) t ON u.id = t.user_id
WHERE t.total_amount >= 2000; -- 筛选总金额≥2000的用户
03、作为字段值(在SELECT子句中)
子查询返回单行单列,作为外层查询的一个字段值(较少用,可能影响性能)。
示例:查询每个用户的用户名及对应的订单数
SELECT username,(SELECT COUNT(*) FROM `order` o WHERE o.user_id = u.id) AS order_count -- 子查询:统计该用户的订单数
FROM user u;
五、内连接、外连接、子查询的区别与适用场景
| 类型 | 核心特点 | 适用场景 |
|---|---|---|
| 内连接 | 取两表交集,只返回匹配行 | 需关联两表且只关注匹配数据(如 “用户 + 其订单”) |
| 外连接 | 保留主表所有行,副表不匹配用 NULL | 需保留主表全部数据(如 “所有用户,包括无订单的”) |
| 子查询 | 嵌套查询,用内层结果辅助外层查询 | 复杂逻辑(如 “基于聚合结果的筛选”“多步条件判断”) |
八、JDBC
JDBC(Java Database Connectivity)是 Java 语言操作关系型数据库的标准规范(接口)。其核心作用是:通过统一的 Java API 屏蔽不同数据库的底层差异,使开发者无需关注具体数据库的实现细节,即可用 Java 代码操作任意支持 JDBC 的数据库。
1、入门
01、导入驱动jar包
需引入对应数据库的 JDBC 驱动(如 MySQL 8.x 的mysql-connector-java-8.0.xx.jar),Maven 项目可通过依赖配置:
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
02、注册驱动
告知 JDBC 使用哪个数据库的驱动(MySQL 5.x 及以上可省略,驱动类会自动注册):
Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 8.x驱动类
03、获取数据库连接(Connection)
通过DriverManager获取与数据库的连接,需指定 URL、用户名、密码:
String url = "jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
- URL 格式:
jdbc:数据库类型://主机名:端口号/数据库名?参数(MySQL 默认端口 3306)。
04、创建执行 SQL 的对象(Statement)
通过Connection获取Statement对象,用于执行 SQL 语句:
Statement stmt = conn.createStatement();
05、执行 SQL 语句
查询(返回结果集):executeQuery(String sql)
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
增删改(返回影响行数):executeUpdate(String sql)
int rows = stmt.executeUpdate("INSERT INTO user(name) VALUES('张三')");
06、处理结果集(ResultSet)
对查询结果进行遍历(通过光标next()移动):
while (rs.next()) {int id = rs.getInt("id"); // 通过列名获取String name = rs.getString(2); // 通过列索引获取(从1开始)System.out.println(id + ":" + name);
}
07、释放资源
关闭ResultSet、Statement、Connection(顺序:后创建的先关闭,放在finally中确保执行):
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
2、核心类 / 接口详解
1. DriverManager 类(驱动管理)
- 作用:管理 JDBC 驱动,负责创建与数据库的连接。
- 核心方法:
static void registerDriver(Driver driver):注册驱动(通常无需手动调用,驱动类静态代码块会自动执行)。static Connection getConnection(String url, String user, String password):获取数据库连接(核心方法)。
2. Connection 接口(数据库连接)
- 作用:代表 Java 程序与数据库的连接,是操作数据库的入口。
- 核心方法:
Statement createStatement():创建Statement对象(执行 SQL)。PreparedStatement prepareStatement(String sql):创建PreparedStatement对象(预编译 SQL,防注入)。void setAutoCommit(boolean autoCommit):设置事务是否自动提交(默认true,手动事务需设为false)。void commit():提交事务。void rollback():回滚事务。void close():关闭连接。
3. Statement 接口(执行 SQL)
- 作用:用于执行静态 SQL 语句(编译时确定的 SQL)。
- 核心方法:
ResultSet executeQuery(String sql):执行查询语句,返回ResultSet。int executeUpdate(String sql):执行增删改语句,返回影响的行数。boolean execute(String sql):执行任意 SQL(若返回结果集则为true,否则为false,较少用)。- 问题:存在SQL 注入漏洞(见下文)。
4. PreparedStatement 接口(预编译 SQL)
- 继承自
Statement,是更安全的 SQL 执行对象。 - 特点:SQL 语句预编译(
?作为参数占位符),参数通过setXxx(index, value)设置,避免 SQL 注入。 - 示例:
String sql = "SELECT * FROM user WHERE name = ? AND age = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "张三"); // 第一个?赋值(索引从1开始)
pstmt.setInt(2, 20); // 第二个?赋值
ResultSet rs = pstmt.executeQuery(); // 无需再传SQL
5. ResultSet 接口(结果集)
- 作用:封装查询语句的返回结果,类似 “临时表”。
- 核心方法:
boolean next():将光标从当前位置移到下一行(初始在第一行之前),返回是否有数据。getXxx(String columnName)/getXxx(int columnIndex):获取当前行指定列的值(Xxx为数据类型,如getInt、getString)。void close():关闭结果集。
3、JDBC 的 CRUD 操作
新增
String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "李四");
pstmt.setInt(2, 25);
int rows = pstmt.executeUpdate(); // 返回1(成功插入1行)
查询
String sql = "SELECT id, name FROM user WHERE age > ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 20);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {System.out.println(rs.getInt("id") + ":" + rs.getString("name"));
}
修改
String sql = "UPDATE user SET age = ? WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 26);
pstmt.setString(2, "李四");
int rows = pstmt.executeUpdate(); // 返回1(成功更新1行)
删除
String sql = "DELETE FROM user WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1);
int rows = pstmt.executeUpdate(); // 返回1(成功删除1行)
4、JDBC 工具类编写
目的:抽取重复代码(如获取连接、释放资源),通过配置文件管理连接信息,提高复用性。
创建配置文件(jdbc.properties)放在src/main/resources下:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC
user=root
password=123456
编写工具类(JDBCUtils)
public class JDBCUtils {private static String driver;private static String url;private static String user;private static String password;// 静态代码块:加载配置文件static {try {Properties prop = new Properties();// 读取配置文件(类路径下)prop.load(JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));driver = prop.getProperty("driver");url = prop.getProperty("url");user = prop.getProperty("user");password = prop.getProperty("password");// 注册驱动Class.forName(driver);} catch (Exception e) {throw new RuntimeException("加载配置文件失败", e);}}// 获取连接public static Connection getConnection() throws SQLException {return DriverManager.getConnection(url, user, password);}// 释放资源public static void close(ResultSet rs, Statement stmt, Connection conn) {if (rs != null) {try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }}if (stmt != null) {try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }}if (conn != null) {try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }}}
}
使用工具类
Connection conn = null;
PreparedStatement pstmt = null;
try {conn = JDBCUtils.getConnection();String sql = "INSERT INTO user(name) VALUES(?)";pstmt = conn.prepareStatement(sql);pstmt.setString(1, "王五");pstmt.executeUpdate();
} catch (SQLException e) {e.printStackTrace();
} finally {JDBCUtils.close(null, pstmt, conn);
}
5、SQL注入漏洞问题
1. SQL 注入问题,漏洞
定义:在已知用户名的情况下,通过SQL语言关键字,登录系统。密码随意修改
产生原因:SQL语句的拼接,利用SQL关键字产生效果
本质:SQL关键字在SQL语句中执行的优先级不同导致的问题。(SQL中的and关键字比or关键字的优先级高)
需要解决SQL注入问题
2、解决
解决SQL注入问题,采用SQL语句预编译的方式,把SQL语句中的参数使用?占位符表示,先把SQL语句编译,格式固定的。再给?传入值,传入任何内容都表示值。数据库会判断SQL执行的结果。
CreateStatement方法创建的statement对象不能够防止SQL注入
prepaStatement方法创建的prepaStatement对象通过预编译的方式能够防止SQL注入
//可以预防SQL注入
stmt = conn.prepareStatement(sql);//不能够预防SQL注入
stmt = conn.createStatement();
public String login2(String username,String password){Connection conn = null;// 预编译执行SQL语句对象PreparedStatement stmt = null;ResultSet rs = null;try {// 获取到连接conn = JdbcUtils.getConnection();// 使用?占位符String sql = "select * from t_user where username = ? and password = ?";// 预编译SQL语句,把SQL语句固定//Statement statement = conn.createStatement();//statement不能防止sql注入问题 prepareStatement 能够防止sql注入问题stmt = conn.prepareStatement(sql);// 需要给 ? 设置值stmt.setString(1,username);stmt.setString(2,password);// 执行SQL语句rs = stmt.executeQuery();// 遍历数据if(rs.next()){// 表示存在数据,如果存在,说明用户名和密码编写正确return "登录成功...";}else{return "登录失败了...";}} catch (SQLException e) {e.printStackTrace();}finally {JdbcUtils.close(conn,stmt,rs);}return null;}
6、Spring 框架中的 JdbcTemplate
JdbcTemplate是 Spring 对 JDBC 的封装,简化了 JDBC 操作(无需手动释放资源、处理异常),核心是 “模板方法模式”。
1. 核心优势
- 自动管理
Connection、Statement、ResultSet的创建与关闭。 - 内置异常转换(将 JDBC 异常转为 Spring 统一的
DataAccessException)。 - 提供便捷的 CRUD 方法(如
update、query)。
九、一切框架的底层:反射和注解
注解
一、注解(Annotation)介绍
1. 定义
注解(Annotation)是 JDK 5.0 引入的一种元数据(metadata),它是代码级别的 “标签”,可以声明在类、方法、字段、参数等元素上,用于描述代码的附加信息。
注解本身不直接影响程序的执行逻辑,但可以被编译器、虚拟机或框架(如 Spring、MyBatis)通过反射机制读取,从而实现特定功能(如编译检查、自动生成代码、配置映射等)。
2. 与注释的区别
- 注释:用于解释代码,仅给开发者阅读,编译器会忽略。
- 注解:用于给程序(编译器 / 框架)读取,是 “机器可识别的注释”,可影响程序行为(如
@Override会让编译器检查方法是否真的重写了父类方法)。
3. 核心作用
- 编译检查:通过注解让编译器在编译期检测错误(如
@Override)。 - 框架配置:替代 XML 配置文件,简化开发(如 Spring 的
@Controller、MyBatis 的@Select)。 - 代码分析:通过工具(如 IDE、代码生成器)解析注解,生成文档或辅助代码(如
@Documented生成 API 文档)。
二、常见注解标识
根据来源,常见注解可分为JDK 内置注解、框架注解和工具类注解。
1. JDK 内置注解
JDK 自带的基础注解,主要用于编译期检查或标记特殊含义:
| 注解 | 作用 | 示例 |
|---|---|---|
@Override | 标记方法为重写父类 / 接口的方法,编译器会检查是否符合重写规则(如方法名、参数列表一致)。 | java @Override public String toString() { ... } |
@Deprecated | 标记类、方法或字段为 “过时”,编译器会对使用它们的代码发出警告。 | java @Deprecated public void oldMethod() { ... } |
@SuppressWarnings | 抑制编译器警告(如未使用变量、类型转换警告等),需指定警告类型。 | java @SuppressWarnings("unused") int a; // 抑制“未使用变量”警告 |
@FunctionalInterface | 标记接口为 “函数式接口”(仅含一个抽象方法),编译器会检查是否符合规范。 | java @FunctionalInterface interface MyFunc { void run(); } |
2. 框架常用注解
(1)Spring 框架核心注解
-
IoC 容器管理:
@Component:标记类为 Spring 容器的 “组件”(Bean),通用注解。@Controller:@Component的衍生注解,标记类为 MVC 中的 “控制器”(处理 HTTP 请求)。@Service:@Component的衍生注解,标记类为 “业务逻辑层” 组件。@Repository:@Component的衍生注解,标记类为 “数据访问层” 组件(DAO)。
- 依赖注入:
@Autowired:自动注入 Spring 容器中的 Bean(默认按类型匹配,配合@Qualifier按名称匹配)。
@Service
public class UserService {@Autowired // 注入UserDao类型的Beanprivate UserDao userDao;
}
@Resource:JDK 自带的注入注解(默认按名称匹配,可指定name或type)。
Spring MVC 请求映射:
@RequestMapping:映射 HTTP 请求(URL、方法等)到控制器方法,可用于类或方法。@GetMapping/@PostMapping:@RequestMapping的简化版,分别对应 GET/POST 请求。
@Controller
@RequestMapping("/user") // 类级别映射
public class UserController {@GetMapping("/info") // 方法级别映射:GET /user/infopublic String getUserInfo() { ... }
}
事务管理:
@Transactional:标记方法或类需要事务管理(Spring 自动控制事务的提交 / 回滚)。
(2)MyBatis 注解
用于替代 XML 映射文件,直接在接口方法上声明 SQL:
@Mapper:标记接口为 MyBatis 的映射接口(MyBatis 会自动生成实现类)。@Select/@Insert/@Update/@Delete:分别对应查询、新增、更新、删除 SQL。
@Mapper
public interface UserMapper {@Select("SELECT * FROM user WHERE id = #{id}")User getUserById(int id);@Insert("INSERT INTO user(name) VALUES(#{name})")void addUser(String name);
}
(3)Lombok 工具注解
简化 POJO 类代码(自动生成 getter/setter、构造方法等):
@Data:自动生成所有字段的 getter、setter、toString()、equals()、hashCode()。@Getter/@Setter:单独为字段生成 getter/setter。@NoArgsConstructor/@AllArgsConstructor:生成无参 / 全参构造方法。
@Data // 等价于同时添加@Getter、@Setter、@ToString等
@NoArgsConstructor
public class User {private int id;private String name;
}
三、自定义注解
除了使用现成注解,开发者还可以根据需求自定义注解,核心是通过元注解(描述注解的注解)定义注解的特性。
1. 元注解(Meta Annotation)
元注解是用于修饰 “注解” 的注解,JDK 提供 4 个核心元注解:
| 元注解 | 作用 | 常用参数 |
|---|---|---|
@Target | 指定注解可修饰的元素类型(如类、方法、字段等)。 | ElementType.TYPE(类 / 接口)、METHOD(方法)、FIELD(字段)等。 |
@Retention | 指定注解的保留策略(生命周期)。 | RetentionPolicy.SOURCE(仅源码,编译后丢弃)、CLASS(编译后保留,运行时丢弃)、RUNTIME(运行时保留,可通过反射读取)。 |
@Documented | 标记注解会被javadoc工具提取到 API 文档中。 | 无参数。 |
@Inherited | 标记注解可被子类继承(即父类的注解会被子类继承)。 | 无参数。 |
2. 自定义注解的步骤
(1)定义注解
使用@interface关键字声明,结合元注解指定特性,并可定义注解的 “成员变量”(以方法形式声明,可指定默认值)。
示例:定义一个 “用于标记方法作者和版本” 的注解@MethodInfo
import java.lang.annotation.*;// 元注解:指定可修饰方法,运行时保留,可生成文档
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodInfo {// 成员变量:作者(无默认值,使用时必须指定)String author();// 成员变量:版本(有默认值,使用时可省略)double version() default 1.0;// 成员变量:描述(无默认值)String description();
}
(2)使用自定义注解
在目标元素(如方法)上添加注解,并指定成员变量的值(格式:成员名=值,多个用逗号分隔)。
public class AnnotationDemo {// 使用@MethodInfo注解,指定author、description,version使用默认值1.0@MethodInfo(author = "张三", description = "获取用户信息")public void getUserInfo() {System.out.println("获取用户信息...");}
}
(3)解析自定义注解
通过反射机制在运行时读取注解信息(需@Retention(RetentionPolicy.RUNTIME))。
示例:解析AnnotationDemo类中方法的@MethodInfo注解
import java.lang.reflect.Method;public class AnnotationParser {public static void main(String[] args) throws NoSuchMethodException {// 获取类的Class对象Class<AnnotationDemo> clazz = AnnotationDemo.class;// 获取目标方法Method method = clazz.getMethod("getUserInfo");// 判断方法是否有@MethodInfo注解if (method.isAnnotationPresent(MethodInfo.class)) {// 获取注解实例MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);// 读取注解的成员变量值System.out.println("作者:" + methodInfo.author());System.out.println("版本:" + methodInfo.version());System.out.println("描述:" + methodInfo.description());}}
}
// 输出:
// 作者:张三
// 版本:1.0
// 描述:获取用户信息
反射
一、反射(Reflection)概念
反射是 Java 的动态机制,允许程序在运行时获取类的完整信息(如类名、父类、接口、字段、方法、构造器等),并能动态操作类的成员(包括私有成员),而无需在编译期确定具体类。
核心作用:
- 动态获取类信息(如框架中根据配置类名创建对象);
- 动态调用方法 / 操作字段(如 Spring 的依赖注入、MyBatis 的 SQL 映射);
- 突破访问权限限制(可访问私有成员,需谨慎使用)。
底层基础:
每个类被 JVM 加载后,会生成一个唯一的java.lang.Class对象,该对象是反射的 “入口”,包含了类的所有元信息。
二、Class类(反射的核心入口)
Class类是反射的基础,代表 “类的类”(所有类的抽象)。每个类在 JVM 中只有一个Class实例,通过它可获取类的全部信息。
1. 获取Class对象的 3 种方式
// 方式1:通过对象的getClass()方法(需先有对象实例)
User user = new User();
Class<? extends User> clazz1 = user.getClass();// 方式2:通过“类名.class”(无需实例,编译期确定)
Class<User> clazz2 = User.class;// 方式3:通过Class.forName("全类名")(运行时动态加载,最常用)
// 全类名:包名+类名(如"com.example.User")
Class<?> clazz3 = Class.forName("com.example.User");
2. Class类的核心方法
| 方法分类 | 关键方法 | 作用描述 |
|---|---|---|
| 获取类信息 | String getName() | 获取全类名(包名 + 类名) |
String getSimpleName() | 获取简单类名(仅类名) | |
Class<?> getSuperclass() | 获取父类的Class对象 | |
Class<?>[] getInterfaces() | 获取实现的所有接口的Class数组 | |
| 获取字段 | Field getField(String name) | 获取public字段(包括父类的 public 字段) |
Field[] getFields() | 获取所有public字段(包括父类) | |
Field getDeclaredField(String name) | 获取当前类的任意访问权限字段(包括 private,不包括父类) | |
Field[] getDeclaredFields() | 获取当前类的所有字段(任意权限,不包括父类) | |
| 获取方法 | Method getMethod(String name, Class<?>... parameterTypes) | 获取public方法(包括父类的 public 方法),需指定方法名和参数类型 |
Method[] getMethods() | 获取所有public方法(包括父类) | |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 获取当前类的任意访问权限方法(包括 private,不包括父类) | |
Method[] getDeclaredMethods() | 获取当前类的所有方法(任意权限,不包括父类) | |
| 获取构造器 | Constructor<?> getConstructor(Class<?>... parameterTypes) | 获取public构造器,需指定参数类型 |
Constructor<?>[] getConstructors() | 获取所有public构造器 | |
Constructor<?> getDeclaredConstructor(Class<?>... parameterTypes) | 获取当前类的任意访问权限构造器(包括 private) | |
Constructor<?>[] getDeclaredConstructors() | 获取当前类的所有构造器(任意权限) | |
| 创建实例 | T newInstance()(JDK 9 后过时,推荐用构造器的 newInstance ()) | 调用类的无参 public 构造器创建实例(若类无 public 无参构造器则报错) |
三、Field类(操作类的字段)
Field类代表类的成员变量(字段),通过它可动态获取字段信息(类型、名称、访问权限)或修改字段值。
Field类的核心方法
| 方法 | 作用描述 |
|---|---|
String getName() | 获取字段名称(如 “name”) |
Class<?> getType() | 获取字段的数据类型(如String.class) |
Object get(Object obj) | 获取指定对象obj中该字段的值(静态字段可传null) |
void set(Object obj, Object value) | 给指定对象obj的该字段设置值(静态字段可传null,值需符合字段类型) |
void setAccessible(boolean flag) | 设置是否取消访问权限检查(flag=true时可访问私有字段) |
int getModifiers() | 获取字段的修饰符(如private、public,返回值可通过Modifier类解析) |
示例:操作 public 字段
class User {public String name; // public字段private int age; // private字段(后续演示)
}public class FieldDemo {public static void main(String[] args) throws Exception {Class<User> clazz = User.class;User user = new User();// 获取public字段"name"Field nameField = clazz.getField("name");// 设置字段值nameField.set(user, "张三"); // 等价于 user.name = "张三"// 获取字段值String name = (String) nameField.get(user); // 等价于 user.nameSystem.out.println(name); // 输出:张三}
}
四、Constructor类(操作类的构造器)
Constructor类代表类的构造方法,通过它可动态创建类的实例(支持有参 / 私有构造器)。
Constructor类的核心方法
| 方法 | 作用描述 |
|---|---|
Class<?>[] getParameterTypes() | 获取构造器的参数类型数组(如{String.class, int.class}) |
T newInstance(Object... initArgs) | 调用构造器创建实例,initArgs为构造参数(参数顺序需与声明一致) |
void setAccessible(boolean flag) | 设置是否取消访问权限检查(flag=true时可访问私有构造器) |
int getModifiers() | 获取构造器的修饰符(如private、public) |
示例:通过构造器创建实例
class User {private String name;private int age;// 无参构造器(默认public)public User() {}// 有参构造器(public)public User(String name, int age) {this.name = name;this.age = age;}// 私有构造器private User(String name) {this.name = name;}
}public class ConstructorDemo {public static void main(String[] args) throws Exception {Class<User> clazz = User.class;// 1. 调用public有参构造器(String, int)Constructor<User> publicConstructor = clazz.getConstructor(String.class, int.class);User user1 = publicConstructor.newInstance("张三", 20); // 等价于 new User("张三", 20)// 2. 调用private构造器(String)Constructor<User> privateConstructor = clazz.getDeclaredConstructor(String.class);privateConstructor.setAccessible(true); // 取消访问检查(关键)User user2 = privateConstructor.newInstance("李四"); // 成功创建实例}
}
五、Method类(操作类的方法)
Method类代表类的方法,通过它可动态调用方法(包括私有方法)。
Method类的核心方法
| 方法 | 作用描述 |
|---|---|
String getName() | 获取方法名称(如 “setName”) |
Class<?> getReturnType() | 获取方法的返回值类型 |
Class<?>[] getParameterTypes() | 获取方法的参数类型数组 |
Object invoke(Object obj, Object... args) | 调用指定对象obj的该方法,args为方法参数(静态方法obj可传null) |
void setAccessible(boolean flag) | 设置是否取消访问权限检查(flag=true时可访问私有方法) |
示例:调用 public 方法
class User {public void sayHello(String name) {System.out.println("Hello, " + name);}private int add(int a, int b) { // 私有方法(后续演示)return a + b;}
}public class MethodDemo {public static void main(String[] args) throws Exception {Class<User> clazz = User.class;User user = new User();// 获取public方法"sayHello",参数类型为StringMethod sayHelloMethod = clazz.getMethod("sayHello", String.class);// 调用方法:等价于 user.sayHello("张三")sayHelloMethod.invoke(user, "张三"); // 输出:Hello, 张三}
}
六、通过反射访问私有属性和私有方法
私有成员(字段、方法、构造器)默认受访问权限限制,直接操作会抛出IllegalAccessException。需通过setAccessible(true)取消 JVM 的访问检查,从而实现访问。
1. 访问私有字段
class User {private String name; // 私有字段
}public class AccessPrivateField {public static void main(String[] args) throws Exception {Class<User> clazz = User.class;User user = new User();// 1. 获取私有字段"name"(必须用getDeclaredField())Field nameField = clazz.getDeclaredField("name");// 2. 取消访问检查(关键)nameField.setAccessible(true);// 3. 操作字段:设置值nameField.set(user, "私有字段值");// 4. 操作字段:获取值String name = (String) nameField.get(user);System.out.println(name); // 输出:私有字段值}
}
2. 访问私有方法
class User {private int add(int a, int b) { // 私有方法return a + b;}
}public class AccessPrivateMethod {public static void main(String[] args) throws Exception {Class<User> clazz = User.class;User user = new User();// 1. 获取私有方法"add",参数类型为int, int(必须用getDeclaredMethod())Method addMethod = clazz.getDeclaredMethod("add", int.class, int.class);// 2. 取消访问检查(关键)addMethod.setAccessible(true);// 3. 调用方法:等价于 user.add(10, 20)int result = (int) addMethod.invoke(user, 10, 20);System.out.println(result); // 输出:30}
}
总结
- 反射是 Java 的动态机制,通过
Class对象可在运行时获取类信息并操作成员,是框架(如 Spring、MyBatis)的核心技术。 Class类是反射入口,提供获取字段(Field)、方法(Method)、构造器(Constructor)的方法,需区分getXxx()(仅 public)和getDeclaredXxx()(任意权限)。- 访问私有成员的关键是调用
setAccessible(true)取消访问检查,但会破坏封装性,需谨慎使用。 - 反射灵活性高,但性能略低于直接调用(因运行时解析),适合框架开发,不建议在业务代码中滥用。
