Welcome to NICK's BLOG!
At Beginning
iiiii This is my first blog !!!!!
我的肺痒痒
“梯子”通常指用于翻墙或代理的工具,其核心依赖各种协议来传输数据。下面我列出一些常见的协议,包括传统VPN协议(如OpenVPN、WireGuard)和代理协议(如Shadowsocks、Trojan,常用于绕过审查)。
我将使用表格形式比较,便于阅读。数据基于常见协议的通用特性。
| 协议名称 | 基本原理 | 优点 | 缺点 | Clash 是否支持 |
|---|---|---|---|---|
| OpenVPN | 开源协议,使用SSL/TLS加密,建立隧道传输数据。 | 安全性高,支持强加密(如AES-256);开源,可自定义配置;兼容性好。 | 速度较慢(加密开销大);配置复杂,不适合初学者。 | 不直接支持,但可以通过Clash Meta的兼容模式或外部配置间接使用。 |
| WireGuard | 现代轻量级协议,使用ChaCha20加密,专注于速度和简单性。 | 速度快、代码简洁、安全性高;易设置,低延迟。 | 较新协议,可能在某些旧设备上兼容性差;不支持UDP伪装。 | 是,支持WireGuard协议。 |
| IKEv2/IPSec | 结合IKEv2密钥交换和IPSec加密,常用于移动设备。 | 连接稳定、速度快;适合切换网络(如WiFi到移动数据)。 | 可能被防火墙检测阻塞;安全性不如OpenVPN。 | 不支持。 |
| L2TP/IPSec | 第2层隧道协议结合IPSec加密,内置于许多系统。 | 易设置,许多设备原生支持;兼容性广。 | 速度慢;安全性一般,可能有漏洞。 | 不支持。 |
| PPTP | 旧式点对点隧道协议,使用GRE封装。 | 速度快、设置简单。 | 安全性极低,已过时,不推荐使用。 | 不支持。 |
| Shadowsocks (SS) | 代理协议,通过SOCKS5伪装流量,绕过审查。 | 伪装能力强、速度快;简单高效,常用于翻墙。 | 加密不如VPN全面;依赖服务器配置。 | 是,支持SS和SSR(ShadowsocksR)变体。 |
| VMess (V2Ray) | V2Ray框架下的协议,支持多路复用和伪装。 | 高度自定义、绕过检测强;支持多协议 multiplexing。 | 配置复杂;资源消耗较高。 | 是,支持V2Ray/VMess。 |
| Trojan | 基于HTTPS伪装的代理协议,模拟正常Web流量。 | 检测难度高、稳定;加密强。 | 依赖TLS证书;速度中等。 | 是,支持Trojan。 |
| Hysteria/Hysteria2 | 基于QUIC的协议,专注于抗丢包和高吞吐。 | 速度极快、抗干扰强;适合弱网络环境。 | 配置稍复杂;兼容性取决于客户端。 | 是,支持Hysteria和Hysteria2。 |
| SOCKS5 | 通用代理协议,支持UDP/TCP。 | 灵活、兼容多种应用;速度快。 | 无加密,安全性低;易被检测。 | 是,支持SOCKS。 |
附加说明
对于翻墙需求,优先考虑Shadowsocks、Trojan或Hysteria,这些协议在绕过GFW方面表现更好。传统VPN如WireGuard适合隐私保护,但可能被封锁。
优化服务器性能适用于所有代理协议(如 Shadowsocks、Trojan、V2Ray),可提高连接速度、降低延迟并支持更多并发连接。以下是主要优化的用途和原理,以及具体配置步骤。
Red Hat/CentOS
开启 TCP Fast Open:
1 | sudo nano /etc/sysctl.d/99-tcp-fastopen.conf |
添加:
1 | net.ipv4.tcp_fastopen = 3 |
应用:
1 | sudo sysctl -p /etc/sysctl.d/99-tcp-fastopen.conf |
调整文件描述符限制:
1 | sudo nano /etc/security/limits.conf |
添加:
1 | * soft nofile 51200 |
应用:
1 | ulimit -n 51200 |
Ubuntu/Debian
开启 TCP Fast Open:
1 | sudo nano /etc/sysctl.conf |
添加:
1 | net.ipv4.tcp_fastopen = 3 |
应用:
1 | sudo sysctl -p |
调整文件描述符限制:
1 | sudo nano /etc/security/limits.conf |
添加:
1 | * soft nofile 51200 |
应用:
1 | ulimit -n 51200 |
Shadowsocks 是一种基于 SOCKS5
的代理协议,通过在客户端和服务器之间建立加密通道传输数据。其核心优势在于轻量高效和伪装能力。客户端将数据加密后发送到服务器,服务器解密后转发到目标网站,返回的数据则反向加密传输。由于其流量可以伪装为常规
SOCKS5 流量,且支持多种加密算法(如 chacha20-ietf-poly1305
和 aes-256-gcm),Shadowsocks 能有效绕过防火墙检测(如
GFW),特别适合翻墙场景。相比传统 VPN,Shadowsocks
更专注于代理而非全面隧道,配置更灵活但不加密整个网络栈。
Shadowsocks 默认使用 8388
端口,但可以自定义为任意高位端口(1024-65535)。建议选择随机高位端口以降低被扫描或封锁的风险。配置文件中的
server_port 参数用于设置此端口。
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo dnf update -y |
2. 安装 Shadowsocks-libev
RHEL/CentOS 不提供官方的 Shadowsocks-libev 包,需通过源码编译安装。
1 | # 安装编译依赖 |
3. 配置 Shadowsocks
创建配置文件 /etc/shadowsocks-libev/config.json。
1 | sudo mkdir -p /etc/shadowsocks-libev |
粘贴以下配置(根据需要修改):
1 | { |
chacha20-ietf-poly1305 或
aes-256-gcm,安全性高且性能好。保存并退出(Ctrl+O, Enter, Ctrl+X)。
4. 创建 systemd 服务
Shadowsocks-libev 需要手动创建 systemd 服务文件以便管理。
1 | sudo nano /etc/systemd/system/shadowsocks-libev.service |
添加以下内容:
1 | [Unit] |
保存后,启用并启动服务:
1 | sudo systemctl daemon-reload |
检查服务状态:
1 | sudo systemctl status shadowsocks-libev |
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo apt update && sudo apt upgrade -y |
2. 安装 Shadowsocks-libev
Ubuntu/Debian 提供官方 PPA,安装更简单。
1 | # 添加 PPA |
3. 配置 Shadowsocks
创建配置文件 /etc/shadowsocks-libev/config.json。
1 | sudo mkdir -p /etc/shadowsocks-libev |
粘贴以下配置(根据需要修改):
1 | { |
chacha20-ietf-poly1305 或
aes-256-gcm,安全性高且性能好。保存并退出(Ctrl+O, Enter, Ctrl+X)。
4. 启动 Shadowsocks 服务
Shadowsocks-libev 默认以服务形式运行,配置 systemd 服务。
1 | # 启动服务 |
检查服务状态:
1 | sudo systemctl status shadowsocks-libev |
Trojan 是一种基于 HTTPS 的代理协议,设计目标是伪装成常规的 Web 流量以绕过深度包检测(如 GFW)。它利用 TLS/SSL 加密,客户端与服务器之间的通信通过 HTTPS 协议(基于 TCP 443 端口)进行,流量看起来像是普通的 HTTPS 网站访问。Trojan 服务器接收客户端请求后,验证 TLS 证书和密码,解密数据并转发到目标地址。返回的数据同样通过 TLS 加密回传。这种伪装使得 Trojan 在严格审查环境下难以被检测,适合翻墙场景。相比 Shadowsocks,Trojan 的加密更强(依赖 TLS),但需要有效的 SSL 证书。
Trojan 默认使用 443 端口,与标准 HTTPS
一致。这使得其流量难以与常规 Web 流量区分,增强了伪装能力。配置文件中的
local_port 参数设置此端口,可根据需要更改,但建议保留 443
以最大化伪装效果。
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo dnf update -y |
2. 安装 Trojan
Trojan 需要从源码编译安装。
1 | # 安装编译依赖 |
3. 获取 SSL 证书
Trojan 需要有效的 SSL/TLS 证书。
使用 Let’s Encrypt 获取免费证书。
1
2 sudo dnf install -y certbot python3-certbot-nginx
sudo certbot certonly --standalone -d your_domain.com证书通常存储在
/etc/letsencrypt/live/your_domain.com/,记录fullchain.pem和privkey.pem的路径。
4. 配置 Trojan
创建配置文件 /usr/local/etc/trojan/config.json。
1 | sudo mkdir -p /usr/local/etc/trojan |
添加以下配置(根据需要修改):
1 | { |
保存并退出(Ctrl+O, Enter, Ctrl+X)。
5. 创建 systemd 服务
创建 systemd 服务文件以管理 Trojan。
1 | sudo nano /etc/systemd/system/trojan.service |
添加:
1 | [Unit] |
启用并启动服务:
1 | sudo systemctl daemon-reload |
检查服务状态:
1 | sudo systemctl status trojan |
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo apt update && sudo apt upgrade -y |
2. 安装 Trojan
从源码编译安装 Trojan。
1 | # 安装编译依赖 |
3. 获取 SSL 证书
Trojan 需要有效的 SSL/TLS 证书。
使用 Let’s Encrypt 获取免费证书。
1
2 sudo dnf install -y certbot python3-certbot-nginx
sudo certbot certonly --standalone -d your_domain.com证书通常存储在
/etc/letsencrypt/live/your_domain.com/,记录fullchain.pem和privkey.pem的路径。
4. 配置 Trojan
创建配置文件 /usr/local/etc/trojan/config.json。
1 | sudo mkdir -p /usr/local/etc/trojan |
添加(根据需要修改):
1 | { |
保存并退出(Ctrl+O, Enter, Ctrl+X)。
5. 创建 systemd 服务
1 | sudo nano /etc/systemd/system/trojan.service |
添加:
1 | [Unit] |
启用并启动:
1 | sudo systemctl daemon-reload |
检查状态:
1 | sudo systemctl status trojan |
VMess 是 V2Ray 框架下的核心代理协议,通过加密和多路复用(multiplexing)实现高效、安全的流量传输。客户端与服务器通过加密通道通信,支持动态端口、流量伪装(如 HTTP/HTTPS 或 WebSocket)和多路复用以降低检测风险。VMess 流量可伪装为常规 Web 流量,结合 TLS 或其他传输协议(如 WebSocket),使其在严格审查环境下表现优异。相比 Shadowsocks,VMess 配置更复杂但功能更强大;相比 Trojan,它更灵活但伪装能力稍逊。VMess 适合需要高自定义和复杂路由规则的翻墙场景。
VMess 没有固定的默认端口,但通常使用 443(模拟
HTTPS)或 1080(常见代理端口)。建议使用 443
以增强伪装效果,配置文件中的 port 参数用于设置端口。
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo dnf update -y |
2. 安装 V2Ray
从官方发布页面下载并安装 V2Ray。
1 | # 下载最新版 V2Ray |
这会将 V2Ray 安装到 /usr/local/bin/v2ray 和配置文件到
/usr/local/etc/v2ray。
3. 获取 SSL 证书
VMess 若使用 TLS 传输(如 HTTPS 伪装)需要 SSL 证书。使用 Let’s Encrypt 获取免费证书。
1 | sudo dnf install -y certbot python3-certbot-nginx |
证书存储在 /etc/letsencrypt/live/your_domain.com/,记录
fullchain.pem 和 privkey.pem 路径。
4. 配置 V2Ray
创建配置文件 /usr/local/etc/v2ray/config.json。
1 | sudo mkdir -p /usr/local/etc/v2ray |
添加以下配置(使用 VMess + TLS,需修改):
1 | { |
v2ray uuid
生成)。保存并退出(Ctrl+O, Enter, Ctrl+X)。
5. 创建 systemd 服务
V2Ray 默认提供 systemd 服务,确认配置文件。
1 | sudo systemctl enable v2ray |
检查服务状态:
1 | sudo systemctl status v2ray |
环境
步骤
1. 更新系统并安装依赖
确保系统软件包是最新的,并安装必要的工具。
1 | sudo apt update && sudo apt upgrade -y |
2. 安装 V2Ray
使用官方安装脚本。
1 | bash <(curl -L https://github.com/v2fly/v2ray-core/releases/latest/download/install-release.sh) |
安装路径同 Red Hat/CentOS。
3. 获取 SSL 证书
1 | sudo apt install -y certbot python3-certbot-nginx |
证书存储在 /etc/letsencrypt/live/your_domain.com/。
4. 配置 V2Ray
创建配置文件 /usr/local/etc/v2ray/config.json。
1 | sudo mkdir -p /usr/local/etc/v2ray |
添加(与 Red Hat/CentOS 相同,需修改):
1 | { |
保存并退出(Ctrl+O, Enter, Ctrl+X)。
5. 创建 systemd 服务
1 | sudo systemctl enable v2ray |
检查状态:
1 | sudo systemctl status v2ray |
在客户端(如 Clash Verge、V2Ray 客户端)配置:
Clash Verge 支持: Clash 完全支持 Shadowsocks、Trojan 和 VMess 协议。在 Clash 中,导入以下 YAML 配置:
1 | proxies: |
Clash(如 Clash Verge)是一个功能强大的代理客户端,支持多种协议(如 Shadowsocks、Trojan、VMess)。以下是你提供的 Clash 配置文件中各部分的作用和推荐配置方式:
1 | port: 7890 |
各部分作用与配置建议:
基本配置:
127.0.0.1:7890)。false
以提高安全性;若需局域网共享,设为
true,并确保防火墙允许相关端口(如 7890)。rule
表示按规则分流(根据 rules
决定哪些流量走代理)。其他选项包括 global(全部走代理)或
direct(全部直连)。rule
适合国内用户,结合规则实现国内外流量分流;global
适合完全代理场景。info
显示常规日志信息。其他选项:silent(无日志)、warning、error、debug。info;调试时设为 debug;生产环境可设为
warning 或 error 减少日志。true,确保延迟测试准确,方便选择最优节点。chrome
或其他主流浏览器(如 firefox),增强伪装效果。DNS 配置:
true,确保 DNS
请求通过代理,防止泄露。:53,但需确保系统防火墙允许 53 端口,或使用高位端口(如
1053)避免冲突。false(多数网络 IPv6 不稳定);若网络支持 IPv6,可设为
true。198.18.0.1/16),通过 Clash 代理解析真实地址,防止 DNS
污染。fake-ip
适合翻墙场景;若无需防污染,可用 redir-host 模式。198.18.0.1/16,无需修改,确保不与本地网络冲突。dns.alidns.com)。223.5.5.5(阿里
DNS)、8.8.8.8(Google DNS),国内建议优先使用国内
DNS。https://dns.alidns.com/dns-query)。https://1.0.0.1/dns-query、Google DNS)。fallback 服务器。nameserver 解析。fallback。geoip: true 和
geoip-code: CN,确保国内域名走国内 DNS,国外域名走国外
DNS。proxies:
作用:定义代理节点(如 Shadowsocks 节点)。
示例:
1 | proxies: |
推荐配置:确保
server、port、cipher 和
password 与服务器配置完全匹配;udp: true
适合需要 UDP 的场景。
proxy-groups:
作用:定义代理组,控制节点的选择方式(如手动选择、自动测试、负载均衡)。
示例:
1 | - name: 负载均衡 |
type: load-balance):
https://cp.cloudflare.com/generate_204
是一个快速响应地址。consistent-hashing。interval
设为 300-600,url 使用可靠测试地址。type: url-test):
interval 设为
300-600,tolerance 设为 50-100,适合动态选择最优节点。type: select):
负载均衡、自动选择、直接连接或具体节点)。DIRECT,便于灵活切换。rules:
作用:定义流量分流规则,决定哪些流量走代理、哪些直连。
示例(需补充完整规则):
1 | rules: |
google.com 走代理)。DOMAIN-SUFFIX)区分国内外流量。GEOIP,CN,DIRECT 确保国内流量直连。使用 create-react-app 快速创建项目:
1 | npx create-react-app react-basic |
1 | const message = 'this is message' |
使用大括号 {} 嵌入 JavaScript 表达式
支持变量、函数调用等
1 | function App() { |
使用 map 方法渲染列表:
1 | const list = [ |
使用 && 运算符
1 | function App() { |
使用三元表达式 ?:
1 | function App() { |
复杂条件渲染
1 | function App() { |
在 JSX 中,使用 {} 包裹 JavaScript 表达式:
1 | const name = '李四'; |
1 | function Greeting() { |
1 | function formatName(user) { |
1 | function Calculator() { |
1 | const product = { |
1 | const numbers = [1, 2, 3, 4, 5]; |
Lodash 是一个流行的 JavaScript 实用工具库,提供了许多高效、可靠的函数,可以简化数组、对象、字符串等数据类型的操作。
1 | npm install lodash |
1 | import _ from 'lodash'; |
1 | // 深拷贝对象 |
1 | // 转换字符串格式 |
1 | // 防抖函数 |
1 | function App(){ |
1 | const clickHandler = (e)=>{ |
1 | <button onClick={()=>clickHandler('jack')}>click me</button> |
1 | function Button(){ |
1 | function App(){ |
1 | <div style={{ color:'red' }}>text</div> |
这个实际上是react语法的大括号里面套了一个对象
1 | import './index.css' |
表单元素的值完全由React状态控制,每次输入都会触发状态更新,实现数据与UI的同步绑定。
1 | function App() { |
通过ref直接操作DOM获取表单值,React不直接管理输入过程,适合简单场景或集成第三方库。
1 | function App() { |
通过props将父组件数据传递给子组件。
1 | function Son(props) { |
父组件传递回调函数给子组件,子组件调用该函数传回数据。
1 | function Son({ onGetMsg }) { |
通过状态提升,将共享状态放在共同的父组件中管理。
1 | function App() { |
使用Context API实现跨层级数据传递。
1 | const MsgContext = createContext(); |
副作用是指组件渲染过程中与外部系统的交互操作,这些操作可能会影响其他组件或产生外部可观察的变化。
React组件的主要工作是渲染UI,但有时需要执行数据获取、订阅事件、手动修改DOM等”副作用”操作。
在以下场景中需要使用useEffect处理副作用:
1 | useEffect(() => { |
1 | useEffect(() => { |
依赖项数组决定了副作用函数何时重新执行:
无依赖数组:
1 | useEffect(() => { |
空依赖数组:
1 | useEffect(() => { |
有依赖项:
1 | useEffect(() => { |
封装可复用的逻辑,必须以use开头命名。
1 | //这里分装了一个只会true false变换的变量 |
1 | // 错误示例 |
Redux 是 React 最常用的集中状态管理工具,类似于 Vue 中的 Pinia/Vuex,可以独立于框架运行。它的主要作用是通过集中管理的方式管理应用的状态。
Redux 的数据修改遵循严格的单向数据流规则:
React官方要求安装Redux Toolkit和react-redux插件
前者是工具集合简化书写,后者用来链接react组件和redux
1 | npm install @reduxjs/toolkit react-redux |
index.js负责组合modules里的所有子模块,并导出store
1 | /store |
1 | import { configureStore } from '@reduxjs/toolkit' |
1 | import { Provider } from 'react-redux' |
1 | import { createSlice } from '@reduxjs/toolkit' |
1 | import { useSelector, useDispatch } from 'react-redux' |
1 | const fetchUserData = () => { |
1 | const dispatch = useDispatch() |
1 | reducers: { |
1 | // 组件中使用 |
Redux DevTools 提供了强大的调试功能,可以实时查看 state 变化和 action 提交记录。
前端路由实现了不同URL路径显示不同组件的功能,所有的页面跳转都在浏览器端完成,无需向服务器请求完整页面。
1 | npm install react-router-dom |
将路由配置单独抽离为模块,保持项目结构清晰
推荐目录结构:
1 | /src |
使用<Link>组件实现导航,类似HTML的<a>标签但不会刷新页面
1 | <Link to="/about">关于我们</Link> |
通过useNavigatehook在代码中控制跳转,适合表单提交后等场景
1 | const navigate = useNavigate(); |
参数会显示在URL中,适合传递筛选条件等简单参数
1 | // 传参代码 |
参数会成为URL路径的一部分,适合传递资源ID等必要参数
1 | // 路由配置中需要定义参数占位符 |
参数不会显示在URL中,适合传递敏感数据或复杂对象
1 | // 传参代码 |
通过children属性定义嵌套关系,Outlet作为子路由的渲染出口
1 | const router = createBrowserRouter([ |
1 | { |
1. History 模式 (createBrowserRouter)
特点与配置
1 | // 创建方式 |
优势:
注意事项:
服务器配置要求:
常见服务器配置:
1 | # Nginx生产配置 |
适用场景:
Hash 模式 (createHashRouter)
特点与配置
1 | // 创建方式 |
优势:
特点:
URL格式:
1 | http://example.com/#/path |
工作原理:
适用场景:
生产境建议
首选History模式:
Hash模式备用方案:
当无法控制服务器配置时使用
添加兼容性提示:
1 | // 检测浏览器兼容性 |
混合模式策略:
1 | // 根据环境自动选择路由模式 |
1 | # 使用 npm |
1 | -src |
1 | // 基础类型 - 依赖类型推断 |
1 | // DOM引用 |
修改 vite.config.ts
1 | import { defineConfig } from 'vite' |
创建 tsconfig.json配置
1 | { |
1 | npm install antd @ant-design/icons |
按需加载样式配置
修改 vite.config.ts:
1 | import { defineConfig } from 'vite' |
安装 react-router-dom
1 | npm install react-router-dom @types/react-router-dom |
路由配置
1 | src/router/index.tsx |
修改 main.tsx
1 | import React from 'react' |
1 | interface ButtonProps { |
1 | interface DataTableProps<T> { |
1 | // 表单事件 |
1 | interface ApiResponse<T> { |
1 | interface ThemeContextType { |
1 | interface WithLoadingProps { |
安装axios
1 | npm i axios |
简易封装
1 | import axios from 'axios' |
可以在一个index.ts里面把所有工具类都给导出:
1 | import requestInstance from './http' |
封装泛型
1 | export type ResType<T> = { |
封装请求函数
1 | import { http } from '@/utils' |
测试API函数
1 | fetchChannelAPI().then((res) => { |
修复时间
2025/06/30 22:00
故障现象
系统启动后黑屏,仅左上角显示_光标,无法进入图形界面。
进入Recovery Mode
Advanced options → Recovery Mode → root shell检查近期修改记录
出现图形环境崩溃,大概率是因为图形环境或者显卡驱动配置被修改过了
/etc/apt/sources.list(APT源列表)
/etc/environment(环境变量)
/etc/X11/xorg.conf(X11配置)显卡驱动相关配置(如NVIDIA的
/etc/modprobe.d/下的文件)
1 | ls -lt /etc/apt/sources.list* /etc/environment /etc/X11 /etc/modprobe.d/ |
sources.list被手动修改过,混合了jammy(22.04)和noble(24.04)的源Err:5 http://ppa.launchpad.net/webkit/team/ippa/ubuntu jammy Release 404 Not Found1 | # 备份原有配置 |
1 | # 生成纯净的官方源配置(Ubuntu 24.04) |
1 | # 取消所有代理环境变量 |
libstdc++.so.6缺失错误:
apt: error while loading shared libraries: libstdc++.so.6: cannot open shared object file
1 | # 查找系统中是否残留库文件(可能路径不同) |
1 | # 如果找到库文件(例如在 `/usr/lib/x86_64-linux-gnu/`),重建符号链接 |
1 | # 如果完全缺失,强制从其他来源复制(需Live USB或网络) |
1 | # 验证库是否修复 |
错误:
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 9639 (unattended-upgr))
自动更新服务占用APT锁
1 | sudo kill -9 9639 # 强制结束占用锁的unattended-upgr进程(进程号也可以用$(pgrep unattended-upgr) |
1 | sudo apt reinstall ubuntu-desktop gnome-session gdm3 |
| 问题层级 | 具体原因 | 修复手段 |
|---|---|---|
| 系统级 | 混合Ubuntu版本软件源(jammy+noble) | 统一为noble官方源 |
| 环境级 | 代理配置残留导致网络连接失败 | 清除代理变量和配置文件 |
| 库依赖 | libstdc++.so.6链接损坏/缺失 |
手动重建库链接 |
| 进程级 | 自动更新服务占用APT锁 | 终止进程并临时禁用服务 |
sources.list,应使用add-apt-repositorysystemctl stop clash)我这一年的考试题目:
山东大学软件学院众智科学2025年期末考试试卷-CSDN博客
众智这门课要速通还是很快的,当然深挖也是挺有意思的X)
图 = 事物(节点) + 联系(边)
同构
邻接矩阵
关联矩阵:m*n的矩阵,横坐标是边集\(v_1到v_n\),纵坐标是点集\(e_1到e_m\),每列上都有两个\(1\) ,\(A_{i,j_1}=A_{i,j_2}=1\)表示\(e_i\)连接了\(v_{j_1},v_{j_2}\)
图的直径:任意两个节点之间的最大距离
连通、连通分量
割点、割边(桥)
捷径:AB相连且AB没有共同的相邻节点,则边AB为捷径(删除捷径会将AB间的距离增加2以上)
欧拉路,欧拉回路
二部图(二分图):
现实中若干典型网络:
This paper introduces Abductive Reflection (ABL-Refl), a novel neuro-symbolic (NeSy) learning framework that efficiently detects and corrects inconsistencies between neural network outputs and symbolic reasoning. Inspired by human dual-process cognition (System 1 & System 2), ABL-Refl uses a reflection mechanism to flag errors in neural predictions and invokes abductive reasoning to fix them.
Why it matters?
Dataset
stores data samples and expected values
dataset = MyDataset(file)
Dataloader
groups data in batches, enables multiprocessing
dataloader = DataLoader(dataset, batch_size, shuffle=True)
| 热键 | 功能描述 |
|---|---|
| 鼠标左键 | 选择或操作组件。 |
| 鼠标滚轮 | 启用手型工具,可拖拽界面(平移视图)。 |
| 鼠标右键 | 旋转视角,配合 WASD 键可自由移动相机(飞行模式)。 |
| Q | 手形工具:平移整个 Scene 视图。 |
| W | 移动工具:移动选中的游戏对象。 |
| E | 旋转工具:按任意角度旋转选中的游戏对象。 |
| R | 缩放工具:调整选中对象的大小(整体或单轴缩放)。 |
补充说明
W、E、R)。1 | npm i pinia |
在main.js中:
1 | import { createPinia } from 'pinia' |
核心步骤:
1- 定义store
1 | import { defineStore } from 'pinia' |
2- 组件使用store
1 | <script setup> |
“信息安全的核心不是完美的技术,而是有效的管理与人性化的考虑。” —— Bruce Schneier
在项目开源的过程中,不可避免地会碰到敏感信息,例如:
AppID、AppSecretAPI_KEY,如支付网关、地图服务、邮件发送等。.env 文件中明文存储的敏感数据。这些敏感信息如果在开源项目中暴露,可能会引发数据泄露、财务损失、甚至法律责任等严重后果,因此在项目中需要妥善处理和保护。
more >>WebSocket是一种在基于TCP连接上进行全双工通信的协议
全双工(Full Duplex):允许数据在两个方向上同时传输。
半双工(Half Duplex):允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输。
请求数据
1 | GET ws://localhost/chat HTTP/1.1 |
响应数据
1 | HTTP/1.1 101 Switching Protocols |
Get、Head、Post和PostForm函数发出HTTP/HTTPS请求。
1 | resp, err := http.Get("http://example.com/") |
程序在使用完response后必须关闭回复的主体。
1 | resp, err := http.Get("http://example.com/") |
Download Anaconda Distribution | Anaconda
下载安装后,搜索Anaconda Navigator,打开后运行JupyterLab,在里面敲代码就行
The corresponding dictionary:
1 | letterToDna = { |
encode name
1 | #Replace with dictionary |
创建文件 hello.go
1 | package main |
命令行运行
1 | go run hello.go |
在 Go 语言里,命名为 main 的包具有特殊的含义。 Go 语言的编译程序会试图把这种名字的包编译为二进制可执行文件。所有用 Go 语言编译的可执行程序都必须有一个名叫 main 的包。一个可执行程序有且仅有一个 main 包。
当编译器发现某个包的名字为 main 时,它一定也会发现名为
main()的函数,否则不会创建可执行文件。
main()函数是程序的入口,所以,如果没有这个函数,程序就没有办法开始执行。
程序编译时,会使用声明 main 包的代码所在的目录的目录名作为二进制可执行文件的文件名。
more >>| 类型 | 位 | 范围 |
|---|---|---|
| char | 1 个字节 | -128 到 127 或者 0 到 255 |
| unsigned char | 1 个字节 | 0 到 255 |
| signed char | 1 个字节 | -128 到 127 |
| int | 4 个字节 | -2147483648 到 2147483647 |
| unsigned int | 4 个字节 | 0 到 4294967295 |
| signed int | 4 个字节 | -2147483648 到 2147483647 |
| short int | 2 个字节 | -32768 到 32767 |
| unsigned short int | 2 个字节 | 0 到 65,535 |
| signed short int | 2 个字节 | -32768 到 32767 |
| long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
| signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
| unsigned long int | 8 个字节 | 0 到 18,446,744,073,709,551,615 |
| float | 4 个字节 | 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字) |
| double | 8 个字节 | 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字) |
| long long | 8 个字节 | 双精度型占8 个字节(64位)内存空间,表示 -9,223,372,036,854,775,807 到 9,223,372,036,854,775,807 的范围 |
| long double | 16 个字节 | 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。 |
sizeof()运算符sizeof(<类型>)或者sizeof(<变量>)
1 | int a; sizeof(a) |
C++的转换语法不像Java那么严格,例如,double可以自动(隐式)转换出float,浮点数自动转换得到整数,但自由的代价是风险增大
1 | int x = 1.7;//居然可以 |
C++支持的强制类型转化语法
Java风格,C风格 (类型名)(表达式):(double)a,(int)(x+y),(int)6.2%4
C++风格 类型作为函数名,()作为参数:double(a),int(x+y),int(6.2)%4
复杂风格,引入注目,突出这儿有类型转换:static_cast \
1 | int ii = static_cast<int>(5.5)://等效于 int ii = 5.5;等效于 imt ii = int(5.5); |
在强制类型运算后原变量不变,但得到一个所需类型的中间变量。
C/C++系统函数是C/C++编译系统(开发包)已预定义的函数。
#include <iostream>#include <iomanip>#include <bits/stdc++.h>函数的参数
在C++中,函数的参数计算顺序是未定义的。
这意味着在表达式 max(f(x), g(x)) 中,f(x) 和 g(x) 的计算顺序是不确定的。
内联函数(inline) , 编译时,就将被调函数体的代码直接插到调用处(c/c++特色)
1 | inline int max (int x, int y) |
重载函数是相同的作用域,两个或更多具有相同函数名,不同参数列表的函数。编译器根据调用时的实参个数以及类型来确定调用淮。
重载函数必须具有不同参数列表,理解为:不同的参数个数,或不同的参数类型。
形参缺省值与重载有些类似,注意区分
```c++
int func(int xl, int x2 = 2, int x3 = 3) {
return xl + x2 + x3;
}// int
void main(void){
cont<< func(10) <<endl; // 10, 2,3
cout<< func(10,8} <<endl; //10,8,3
1
1 |
|
赋值时不可以中间缺省
1 | cout<< func(lO, , 8) <<endl; II 错误 |
不可以定义与声明同时赋值
如果一个函数有原型声明,则默认参数值应在函数原型声明中给出
1 | int func(int X 1 , int x2 = 3, int x3=4); //声明 |
当形参缺省多处声明+重载相遇
1
2
3
4
5
6
7
8 int f(int x,int y=3); int f(int);
int main(){
int f(int x,int y=4); //如果去掉,则因为二义性导致编译错误
cont<< f(3) << endl; //结果7
return O;
}
int f(int x,int y){ return x+y;} //重载
int f(int x){ return x;} //重载
以类型作为变量
1 | template<typename T>//作用域就是下面的一个方法 |
变量具有生命周期和作用域。函数具有作用域。
生命周期指变量的空间分配到释放的周期,三类生命周期,为方便管理,C++将不同生命周期的变量存储在不同性质的存储空间中。
存储类别
静态局部变量:作用范围局部(函数内部),生命周期:静态。
static int i =3 ; 但i 其实不—定是3 , i只是最初是3 。作用域
局部作用域:在函数内部或在块(复合语句)中定义的变量都是局部变量,其作用域开始于声明处,结束于函数/块的结尾处。
全局作用域(物理文件)
在函数外定义的变量称为全局变量,对应文件作用域。其缺省的作用范围是: 从定义位置开始到该源程序文件结束。
全局变量与局部变量同名时,局部变量优先。可以利用域作用符::标记全局变量
1 | #include <iostream.h> |
函数要访问当前源文件中定义的全局变量:
通常在前面定义全局变量,后面定义函数,函数可以访问全局变量
如果在后面定义全局变量,需要在前面利用extern声明全局变量,声明之后可以使用
1 | #include < iostream > |
声明可以在函数内部或者外部,声明的位置决定了其作用范围
static修饰函数
static 也可以修饰函数,描述其为内部函数
内部函数:函数只局限于在本文件中调用,其它文件不能调用,用static 定义该函数。
外部函数:是函数的默认形式,即可以被外部调用。
在调用文件中用extern 声明,把该函数的作用域扩展到调用文件。
1 | static float fac1(float n) |
1 | extern float fac(int n); |
命名空间作用域
c++提供了名空间(namespace) 机制来解决名冲突问题。
在—个名空间中定义的全局标识符(函数/全局变呈),其作用域为该名空间。
当在—个名空间外部需要使用该名空间中定义的全局标识符时,可用该名空间的名字来修饰或受 限。
1 | namespace A |
1 、不使用using
1 | ...A::x... //A中的x |
2、使用using省略整个命名空间
1 | using namespace A; |
3、使用using省略命名空间的某个函数
1 | using A::f; |
文件包含
一个源文件也可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。
#include "文件名“
通常情况下, 很少在一个cpp中include另一个cpp , 而应当include其声明文件h , 即包含file的声明(引用),而不是其全部。(可以使用,但不拥有)
mathx.h
1 | const double Pl = 3.14; |
mathx.cpp
1 | #include "mathx.h" |
file1.cpp
1 | #include < iostream > |
.h文件中能包含:
不能包含:
因为一个头文件的内容实际上是会被引入到多个不同的 .cpp 文件中的,并且它们都会被编译。放声明当然没事,如果放了定义,那么也就相当于在多个文件中出现了对于一个符号(变量或函数)的定义,纵然这些定义都是相同的,但对于编译器来说,这样做不合法。
数组是同一类型的一组值(例如, 10个int或15个char) , 在内存中顺序存放。
整个数组共用—个名字,其中的每—项称为—个元素,对应一个下标,下标从0开始。
C++规定,数组元素个数必须是常量,不能通过赋值修改数组长度。(VC 会报错,但MinGW 允许)
C++ 中, 数组不是对象,不用new去创建
int a [10]; a [15] = 1 ; 可能带病工作,可能立即崩溃定义数组时,可以用{}给数组元素赋值而不指定长度
赋值元素的个数为数组的长度。int a[]= {0,1, 2, 3}; 则a的长度为4 。
定义时,同时指定长度与{ }赋值,应该保证指定长度>=赋值元素个数
未赋值的元素为0 、0.0或者null 。int b[10]= {1};
int c[2]= {1, 2, 3}; 挑衅编译器,规范的编译器捉示语法错;
不赋初值时, static 或全局数组均默认其为0或’\0 ‘ , 其他局部数组赋值随机
在C++中,数组名被认为是数组在内存中存放的首地址。Java中也是这样认为的
用数组名作函数参数。实参传递的是数组在内存中的地址
被调函数内外,共用同一段内存被调函数内可以修改数组的内容
用数组名作函数参数,实参/形参类型一致。
如果是一维数组,形参通常省略元素个数,例如int a[]
如果是二维数组,形参通常省略第一维,但不可省略第二维,且第二维必须与实参中的维数相等。
如果是高维数组,形参通常省略第一维,但不可省略低维,其他维度必须一致
约定:用可作为字符串的结束标志,它占内存空间,但不计入串长度。C程序依据’\0’ 判断字符串是否结束,而不是根据字符数组的长度。
1 | char a[]= {'C','H','I','N','A'}; |
a是字符数组,非字符串; b是字符数组也是字符串,数组长度6B, 字符串长度5B
c是字符数组也是字符串,数组长度为10, 串长度为5, 串占有6个字节
字符串处理
要求有足够空间可以存放运笢结果
1 | #include <string.h> |
包含头文件#include<vector>
标准操作:初始化、添加、遍历、插入、删除、查找、排序、释放(动态数据管理)
vector 的初始化:可以有五种方式
vector< int> a(10); //10个整型元素的向最,初始长度,可变长vector< int> a(10, 1); //10个整型元素的向鼠,且初值均为1vector< int> b(a); //用b 向量来创建a 向量,整体复制性赋值vector< int> a(b.begin(),b.begin()+ 3);//从容器b中获取第0个到第2个(共3个)元素int b[7] ={1,2,3,4,5,9,8}; vector< int> a(b,b+ 7); //从数组b中获得初值添加,插入,删除: 对千连续空间的数据结构,插入,删除代价巨大
vector< int> vec1; for(int i =O;i < 1 O;i + +) vec1 .push_back(i); //后端插入vec1.insert(vec1.end(),5,3); //从vec1.back位置插入5个值为3 的元素vec1 .erase(vec1.begin(),vec1.begin()+3);//删除之间的元素,其他元素前移遍历:
for(int i =O;i < =v.size()-1 ;i + +) cout < <v[i] < < " "for (const int i:v)cout < < i < < "\t";排序,查找:需要include<algorithm>
1 | int a[10] = { 9, 0, 1, 2, 3, 7, 4, 5, 100, 10 }; |
1 | bool cmp(const student &a, const student &b){ |
指针变量的定义(语法)
类型标识符*指针变量名
1 | int * p; // *用于定义指针p, int定义指针p指向整数 |
1 | void * p; //无类型指针、空类型指针、万能指针,只考虑指向,不考虑类型 |
空类型指针可以指向任何带类型的指针(地址)。
void * p= &x;
x可以是基本数据类型,或者对象/结构体类型;
p = &table; p = &book;
空指针在使用(获取内容)时必须转换成具体的类型,一般是原本的类型
1 | int a=3; |
数组在内存中占据—段连续的单元,通过首地址+偏移可以访问到每个元素。
C++规定: 数组名就是数组的起始地址,同时也是指针,也是0号元素的地址;
实际应用中,通常额外引入一个新的指针变量p, 通过++,—在数组上游走
例如:
1 | int a[10],*p; |
可以通过p++指向下一个整数,但不可以a++因为数组名a是常量指针,只能指向数组首地址
C++ 规定,可以通过指针引用数组元素, 即a [i]可以理解为*(a+i) 。
1 | const int pi=3; |
如果&a 的前面有类型符, 则是对引用的声明/定义;
int &a
如果&a 的前面无类型符,则是取变量的地址运算。
int a, * p; p = &a;
作用
用作函数的(形式)参数,实现参数的双向传递。
1 | void swap(int &x, int &y) |
引用作为形参,实参是变呈而不是地址,这与指针变呈作形参不一样
但实际效果—样,习惯上,双向传递一个参数,使用引用,传递数组,使用指针(数组首地址)
引用可以部分代替指针的操作,例如双向传递参数,降低程序设计的难度
引用更符合数据结构代码风格
常量引用:用canst修饰的引用,防止通过引用修改数值
1 | void m3(const int& x){ ~ } // 传递常引用,通过x修改数值, 语法错误 |
在创建对象时,负责将对象初始化(成员变量赋初值)。
任何类都会有构造函数,无论你是否主动定义
没有主动定义的情况,编译器自动产生一个缺省构造函数className::classNameO { },对所有数据成员置0, 或者NULL
主动显式的定义了类的(任何)构造函数,编译器不再产生缺省构造函数。
但可以使用” = default “ 强制编译器生成缺省构造函数
1
2 Clock() = default;//指示编译器提供默认构造函数
Clock(int newH, int newM, int newS); //构造函数
参数初始化表
在函数头部对数据成员初始化。
1 | Box::Box(int h,int w,int len) : height(h), width(w), length(len){} |
其中, height; width; length是成员变量;
相当于
1 | Box::Box(int h,int w,int len) {height=h; width=w; length=len;} |
委托构造函数
构造函数调用其他构造函数
1 | Clock(): Clock(0, 0, 0) {}//委托构造函数(delegat i ng constructor) |
析构函数在对象的生命期结束时,被系统自动调用,作用是收回为对象分配的存储空间。
析构函数名必须是在类名前面加上字符”~” 。
1 | ClassNaine::~ClassName( ){ …… } |
析构函数不能带有任何参数,没有返回值,不指定函数类型。
如果没有显式定义析构函数,则编译器自动产生一个缺省的析构函数, 其函数体为空。
分配空间时,构造;回收空间时,析构
如果在构造函数中用new为对象动态分配了存储空间,则在类中应该定义一个析构函数,使用delete,收回由new申请的存储空间。
编译器自动生成的复制构造函数,用初始值对象的每个成员数据的值
1 | A::A(const A &a){ |
C++11: 用”= delete” 指示编译器不要生成默认的复制构造函数。
1 | class Point{//Point 类的定义 |
缺省的复制函数为浅拷贝,如果数据成员是指针,则复制函数拷贝指针,不拷贝指针指向的内存空间。推论:使用new 申谓空间, 通常需要自定义复制函数,实现深拷贝
1 | class Str{ |
复制构造函数被自动调用的三种情况
定义—个对象时,以本类另—个对象作为初始值,发生复制构造; A a2 = a1;//Aa2(a1)
如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;
1 |
|
复制构造函数被自动调用的三种情况
定义—个对象时,以本类另—个对象作为初始值,发生复制构造;
1 |
|
如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化—个临时无名对象,传递给主调函数,此时发生复制构造。
Box box1(12,15,18);
return box1;//返回值是Box类的对象
}Box box2;//定义Box类的对象box2
box2=f();//返回Box类的临时对象,并将它赋值给box2
}1 |
|
返回引用,调用和被调函数对同一个对象进行操作,节省时间和内存。
静态成员被该类的所有对象所共有、共享。用关键字static声明
在内存中只占一份空间。如果改变它的值,则各对象中这个数据成员的值都同时改变。
只要在类中定义了静态数据成员,即使不创建对象,也为静态数据成员分配空间,可以通过类名访间到类的静态成员。
全生命周期,在程序启动时被分配空间,到程序结束时释放;不随对象的建立而分配空间,也不随对象的撤销而释放。
成员函数
1 | static int volume(); |
在类外调用静态成员函数, 可以用类名或者对象名。如
1 | Box::volume(); |
静态成员函数可以访问本类中的静态数据成员,不能访问本类中的非静态成员。
友元将—个模块(函数、类)声明为本类的朋友,从而可以访问本类中隐截内容。
友元函数
外部函数f () , 在类A中用friend修饰声明,则f()为类A的友元函数, f可以访问A的所有成员。
friend void f () ;
友元函数不一定是独立函数,也可以是另外一个类的函数
友元类
在类A中声明另—个类B声明为friend , 则类A所有数据类B可以访问: friend class B;
友元的关系是单向的而不是双向的,友元关系不能传递
作用:增加灵活性,使程序员可以在数据安全性和使用便利性之间做选择。指明哪些是朋友,对淮开放。
副作用: 破坏数据封装和隐截。建议尽呈不要使用友元类。
副作用2 : 程序设计更加复杂了,—些额外的问题,例如父亲的朋友是不是朋友
const修饰的变量称为常量。
const修饰的成员数据称为常数据成员
只能通过构造函数的参数初始化表对常数据成员进行初始化
```cpp
const int hour; //声明hour为常数据成员
Time::Time (int h):hour(h){ hour = h; //错误}
1 |
|
const声明的对象为常对象。
常对象必须进行初始化,不能被更新(修改)。
对象的数据成员的值不被改变
```c++
const Time tl(l2,34,46); //tl是常对象
1 |
|
重载运算符+=
+=重载函数,其返回值类型为void。因为+=-=*=等修正运算,不会产生新的对象
1 | void operator+=(const A &a){//重载运算符+= |
自增运算符
1 | A& operator++() { // 重载前置自增运算符 |
运算符重载为成员函数的参数只能有二种情况:没有参数或带有—个参数。
对于单目运算符(如++),正载时通常不能有参数;
对于双目运算符,只能带有—个参数。参数可以是对象,对象的引用,或其它类型的参数。
只能对C++ 中已定义了的运筒符进行重载。但有5个不允许重载
.(点运算符)s.length,但是->(箭头运算符)是可以重载的Math::P.*(点星运算符,)s.*p,即成员是指针类型?:(条件运算符)sizeof,不可以重载禁止重载(修改)基本类型数据的运算符。参数不能全部是C++的基本标准类型。
静态成员函数不能是运算符重载函数
运算符重载为类成员函数, 成员函数所属对象是一个操作数,另一个操作数是函数参数
1 | A a ,b, c; |
运算符重载为类的友元函数(或者普通函数), 参与运算的对象全部成为函数参数。
1 | c=a+b; //实际上是c=operator+(a, b); 两个参数friend A operator+ (A &a, A &b) |
如果参数有基本数据类型时,则运算符建议重载为友元函数。例如:
1 | Complex Complex::operator+(int &i){ |
则限制使用者必须写作c3 = c2+100;
而不能写成c3=100+c2; 因为100不是类对象,100.operator+(c2)编译错误
1 | #include<iostream> |
1 | A& A::operator =(A &b){//重载赋值运算符 |
注意
1 | A a2;//调用了默认构造函数 |
1 | class SubClass: public/protected/private BaseClass |
公有派生,基类中所有成员在派生类中保持访问权限。
保护派生时,基类中公有成员在派生类中均变为保护的;
私有派生时,基类中所有成员在派生类中均变为私有的;
1 | class A{ |
派生类的构造函数要么调用基类的构造函数,要么调用自己的另一个构造函数二选一。
派生类的构造函数调用不能出现递归
派生类如果有多个构造函数,必然有至少一个调用基类的构造函数
必须在初始化列表中调用
1 | class A{ |
构造的顺序
1.执行参数初始化列表
A)调用基类的构造函数,初始化基类成员
A(i+1)
B)按照成员变量定义顺序,依次初始化
int b;A a; 则b(i++),a(i)A a;int b; 则执行a(i),b(i++)
2.调用自身构造函数的实现部分
析构顺序相反
1 | class D: public A, protected B, private C { |
二义性冲突
当派生类中新增加的数据或函数与基类中成员同名时,缺省优先调用派生类中的成员。
多个基类数据或函数同名时,利用类作用域符号来指明数据或函数的来源。
同一个公共的基类在派生类中产生多个拷贝,多占用空间,容易混淆。
int x;
A(int a) { x=a;}};
class B: public A{int y;
B(int a=0,int b=0):A(a) { y=b;}};
class C: public A{int z;
C(int a,int c):A(a){ z=c; }};
class D: public B, public C{int dx;
D(int al,int b,int c,int d,int a2):B(al,b),C(a2,c)
{dx=d;}};
void main(void){D dl(l0,20,30,40,50);
cout<<dl.x<<endl;//模糊引用错误
cout<<dl.B::x<<endl; //10
cout<<dl.C::x<<endl; //50
}1 |
|
• 非虚函数:静态绑定(编译时确定),根据指针/引用的静态类型调用。
1
2
3void non_virtual() { ... } // 非虚函数
Base* obj = new Derived();
obj->non_virtual(); // 始终调用 Base::non_virtual()
• 必要性:基类析构函数必须为 virtual,确保通过基类指针删除派生类对象时,调用完整的析构链。
1
2
3
4
5
6
7
8
9
10
11class Base {
public:
virtual ~Base() { cout << "Base析构"; }
};
class Derived : public Base {
public:
~Derived() { cout << "Derived析构"; }
};
Base* obj = new Derived();
delete obj; // 输出 Derived析构 → Base析构
• 未使用虚析构的后果
仅调用基类析构函数,导致派生类资源泄漏。
• 同名成员变量:派生类变量会遮蔽基类同名变量。
1
2
3
4
5
6
7
8
9class Base { protected: int a = 10; };
class Derived : public Base {
protected: int a = 20; // 遮蔽 Base::a
public:
void print() {
cout << a; // 20(访问派生类变量)
cout << Base::a; // 10(显式访问基类变量)
}
};
• 自动调用:派生类析构函数执行完毕后,编译器自动调用基类析构函数。
1
2
3
4~Derived() {
// 析构派生类成员
// 编译器自动插入 Base::~Base();
}
• 禁止显式调用:手动调用基类析构函数(如 Base::~Base())会导致重复析构。
• 允许修改权限:派生类虚函数的访问权限可以不同于基类。
1
2
3
4class Base { public: virtual void foo() {} };
class Derived : private Base {
private: void foo() override {} // 合法但不推荐
};
• 设计建议:保持派生类虚函数权限不低于基类(如 public → public)。
使用 override 关键字(C++11+):明确标记重写,避免签名错误。
1 | void foo() override { ... } // 编译器检查签名匹配 |
虚析构函数规则:
• 基类必须有虚析构函数。
• 若类可能被继承,析构函数默认声明为 virtual。
避免变量遮蔽:
• 基类和派生类避免同名变量。
• 若需访问基类变量,使用 Base::variable。
多态接口设计:
• 虚函数用于定义接口(如 calculate())。
• 非虚函数用于工具方法(如 validate())。
| 场景 | 关键点 |
|---|---|
| 虚函数与动态绑定 | 通过虚函数表实现运行时多态 |
| 虚析构函数 | 必须为虚,避免资源泄漏 |
| 变量遮蔽 | 派生类变量遮蔽基类变量,需显式指定作用域访问基类成员 |
| 虚函数覆盖权限 | 可以修改权限,但应保持接口一致性 |
override 关键字 |
强制编译器检查重写签名,避免隐藏(Shadowing)错误 |
1 | // 基类析构函数非虚 |
掌握 virtual 的机制,是写出安全、灵活 C++ 代码的关键!
1 | class Point{ |
1 | class A{ |
父类(基类)的析构方法一般为virtual, 保证派生类的析构方法得到执行
• 抽象类:包含至少一个 纯虚函数(Pure Virtual Function) 的类,无法被实例化。
• 纯虚函数:声明时在函数末尾添加 = 0,表示该函数无默认实现,必须由派生类重写。
1
2
3
4class Shape {
public:
virtual double area() const = 0; // 纯虚函数
};
• 定义接口规范:强制派生类实现特定方法,统一多态行为。
• 实现多态性:通过基类指针或引用调用不同派生类的实现。
• 代码复用:抽象类可包含部分已实现的成员函数或数据成员,供派生类继承使用。
1 | #include <iostream> |
输出结果:1
2Woof!
Meow!
• 无法实例化:直接创建抽象类对象会引发编译错误。
1
Animal animal; // 错误:Animal 是抽象类
• 派生类必须实现所有纯虚函数:否则派生类仍为抽象类。
1
class Bird : public Animal {}; // 错误:未实现 makeSound(),Bird 仍是抽象类
• 纯虚函数可以有实现:可在类外为纯虚函数提供默认实现(需显式调用)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Shape {
public:
virtual void draw() const = 0;
};
void Shape::draw() const { // 纯虚函数的默认实现
cout << "Drawing a shape." << endl;
}
class Circle : public Shape {
public:
void draw() const override {
Shape::draw(); // 显式调用基类实现
cout << "Drawing a circle." << endl;
}
};
• 接口(C++模拟):抽象类的特例,所有函数均为纯虚函数,无数据成员。
1
2
3
4
5class IPrintable {
public:
virtual void print() const = 0;
virtual ~IPrintable() = default;
};
• 抽象类:可包含数据成员和已实现的方法,更灵活。
• 虚析构函数:抽象类应定义虚析构函数,确保派生类对象通过基类指针删除时正确释放资源。
• 避免过度抽象:合理设计抽象层级,防止继承结构过于复杂。
• 组合优先于继承:若无需多态行为,优先使用组合而非继承抽象类。
• 框架设计:定义通用接口(如 GUI 控件的渲染方法)。
• 插件系统:规定插件必须实现的函数。
• 算法策略:通过抽象基类定义算法骨架,派生类实现具体步骤(模板方法模式)。
定义:通过模板继承在编译期实现多态,无需虚函数表(vtable),实现零运行时开销。
核心思想:基类模板以派生类作为模板参数,通过 static_cast 将 this 转换为派生类指针,直接调用派生类方法。
1 | template <typename Derived> |
关键点:
• 零运行时开销:所有函数调用在编译期静态绑定。
• 应用场景:
• 静态多态(替代虚函数)。
• Mixins(为类动态添加功能)。
• 优化性能敏感代码(如数学库中的向量运算)。
std::variant + std::visit定义:基于类型安全的联合体(std::variant)和访问者模式(std::visit),在编译期根据实际存储的类型分发操作。
1 | #include <variant> |
关键点:
• 类型安全:std::variant 替代传统联合体,避免类型错误。
• 访问者模式:通过重载的 operator() 为不同类型定义操作。
• 编译期分发:std::visit 在编译期生成类型分发代码,无运行时类型检查(RTTI)开销。
• 应用场景:
• 状态机(不同状态对应不同类型)。
• 解析器(处理多种类型的语法节点)。
• 替代继承体系的多态设计。
JPA全称Java Persistence API(2019年重新命名为 Jakarta Persistence API ),是Sun官方提出的一种ORM规范。
O:Object R: Relational M:mapping
作用
1.简化持久化操作的开发工作:让开发者从繁琐的 JDBC 和 SQL 代码中解脱出来,直接面向对象持久化操作。
2.Sun希望持久化技术能够统一,实现天下归一:如果你是基于JPA进行持久化你可以随意切换数据库。
该规范为我们提供了:
1)ORM映射元数据:JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对
象持久化到数据库表中;
如:@Entity 、 @Table 、@Id 与
@Column等注解。
2)JPA 的API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和
SQL代码中解脱出来。
如:entityManager.merge(T t);
3)JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
如:from Student s where s.name = ?
So: JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。
Hibernate与JPA:
所以底层需要某种实现,而Hibernate就是实现了JPA接口的ORM框架。
和mybatis区别:
mybatis:
小巧、方便?、高效、简单、直接、半自动
半自动的ORM框架,
小巧: mybatis就是jdbc封装
在国内更流行。
场景: 在业务比较复杂系统进行使用,
hibernate:
强大、方便、高效、(简单)复杂、绕弯子、全自动
全自动的ORM框架,
强大:根据ORM映射生成不同SQL
在国外更流。
场景: 在业务相对简单的系统进行使用,随着微服务的流行。
Log4j有三个主要的组件/对象:Loggers(记录器),Appenders (输出源)和Layouts(布局)。这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出。
每条日志语句都要设置一个等级(DEBUG、INFO、WARN、ERROR和FATAL)。
其中DEBUG < INFO < WARN < ERROR < FATAL。fatal等级最高
对应调试信息 一般信息 警告信息 错误信息 严重错误信息
1、Loggers 在设置日志输出位置的时候,会给那个位置设置一个级别,只有大于等于那个级别的日志才会打印输出到指定位置。
例如:某个Loggers(日志输出位置的等级记录器)级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出到那个文件,而级别比INFO低的DEBUG则不会输出。
2、Appenders
禁用和使用日志请求只是Log4j的基本功能,Log4j日志系统还提供许多强大的功能,比如允许把日志输出到不同的地方,如控制台(Console)、文件(Files)等,可以根据天数或者文件大小产生新的文件,可以以流的形式发送到其它地方等等。
常使用的类如下:
org.apache.log4j.ConsoleAppender(控制台) org.apache.log4j.FileAppender(文件) org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件) org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件) org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
基本上可以满足我们的日常需求,当然如果你的需求比较特殊,可以自己实现Appender输出路径。只需要定义一个类,实现Appender接口就可以了。Appender接口中定义了一系列记录日志的方法,按照自己的规则实现这些方法即可
3、Layouts
用户可以根据自己的喜好格式化自己的日志输出,Layouts提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
常使用的类如下:
org.apache.log4j.HTMLLayout(以HTML表格形式布局) org.apache.log4j.PatternLayout(可以灵活地指定布局模式) org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串) org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
在pom.xml
1 | <dependency> |
创建配置文件
在src/main/resources目录下创建log4j.properties
1 | ### 日志的输出级别是dubug,输出位置名字叫stdout,D,E |
1 | public class HttpRequestUtil { |
同理还有:
1 | package com.log4j; |
控制台信息:
1 | [DEBUG] |
输出的文件:
1 | 2019-03-31 11:33:36 [ main:0 ] - [ DEBUG ] 调试信息. |
<details style="margin: 10px 0;">
<summary style="font-weight: bold; border: 2px solid rgb(240, 98, 146); border-radius: 5px; background-color: rgb(252, 228, 236); padding: 5px;">PROOF</summary>
<img style="display: block; margin: 0 auto;" src="image-20240406184732973.png"></img>
</details>
Java 序列化是一种将对象转换为字节流的过程,以便可以将对象保存到磁盘上,将其传输到网络上,或者将其存储在内存中,以后再进行反序列化,将字节流重新转换为对象。
序列化在 Java 中是通过 java.io.Serializable 接口来实现的,该接口没有任何方法,只是一个标记接口,用于标识类可以被序列化。
当你序列化对象时,你把它包装成一个特殊文件,可以保存、传输或存储。反序列化则是打开这个文件,读取序列化的数据,然后将其还原为对象,以便在程序中使用。
实现 Serializable 接口:
要使一个类可序列化,需要让该类实现 java.io.Serializable
接口,这告诉 Java 编译器这个类可以被序列化.
1 | public class User implements java.io.Serializable { //该接口没有任何方法,只是一个标记接口,用于标识类可以被序列化。 |
java.io.ObjectOutputStream 代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obi对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream 代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
通过Socket传输对象
客户端
1 | public class userClient { |
服务端
1 | public class ServerHandleThread implements Runnable{ //表示该类的实例可以被一个线程执行 |
1 | public class simpleServer { |
序列化对象
1 | import java.io.*; |
反序列化对象
1 | import java.io.*; |
在BlessingChess项目中,想要做一个图片实时上传和读取的需求,第一想到的是直接把文件放在static目录下,然后实时上传和读取。
读取资源(也就是web端访问static资源)其实就很简单,Spring Boot 默认就配置了 /static/** 映射,所以无需任何配置就能访问。
很快啊,工具类起手:
1 | package com.example.BlessingChess.utils; |
这还没有问题,但是当实际测试的时候,发现新上传的文件,无法访问到,会报错NoResourceFoundException
资源的实时访问问题,比如上传图片后,然后再访问,可能需要重启才能继续访问,
jar对resources目录进行保护措施,可能读取不到上传的资源
但是有些极少量的文件需要存储到resources目录下,这就需要先获取到reources下的相应目录,此时应该考虑将来运行jar包时不能出错
因此推荐一下两种方式获取static目录:
通过ResourceUtils工具获取static目录
1 | try { |
通过 ClassPathResource 获取
1 | // 具体到要访问的文件,然后拿到文件流对象 |
当然,更好的方法是避开static目录存储,考虑做成本地硬盘上的映射目录:
添加配置文件WebMVCConfig,然后在添加资源映射
1 | package com.example.BlessingChess.config; |
这里会把**通配符给映射到后面那个目录后面
比如:http://localhost:8080/image/icon/0/icon.jpg会被映射成file:E:/StudentOnline/BlessingChess/image/icon/0/icon.jpg
这时候,你可以实时修改该目录里的内容,也可以实时访问。
还没完,当你用浏览器访问的时候,控制台又报错了:
corg.springframewOPk.web.servlet.nesounce.NOResourceFoundException: No static resource favicon.ico.
这不是代码的问题,而是用浏览器请求资源时,都会同时请求标签页图标,当你在postman请求资源时就不会发生这个问题了,或者直接在static目录下放一个favicon.ico文件也行
关于相对路径:
./**代表的是E:,src的同级路径,这个./也可以直接省略
我们来看一下出现异常之后,最终服务端给前端响应回来的数据长什么样。
1 | {"timestamp:"2022-12-09Te6:05:34.323+00:00","status":"500","error ":"Internal Server Error","path" "/depts""] |
响应回来的数据是一个JSON格式的数据。但这种JSON格式的数据显然并不是我们开发规范当中所提到的统一响应结果Result
由于返回的数据不符合开发规范,所以前端并不能解析出响应的JSON数据。
出现异常之后,当前案例项目的异常没有做任何的异常处理
当我们没有做任何的异常处理时,我们三层架构处理异常的方案:
解决方案
那么在三层构架项目中,出现了异常,该如何处理?
我们该怎么样定义全局异常处理器?
@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。1 | @RestControllerAdvice //表示当前类为全局异常处理器 |
@RestControllerAdvice=@ControllerAdvice+@ResponseBody处理异常的方法返回值会转换为json后再响应给前端
此时,出现异常之后,异常已经被全局异常处理器捕获了。然后返回的错误信息,被前端程序正常解析,然后提示出了对应的错误提示信息。
LoginController
1 | @RestController |
EmpService
1 | public interface EmpService { |
EmpServiceImpl
1 | @Slf4j |
EmpMapper
1 | @Mapper |
以上的功能无论用户是否登录,都可以访问部门管理以及员工管理的相关数据。所以我们目前所开发的登录功能,它只是徒有其表。而我们要想解决这个问题,我们就需要完成一步非常重要的操作:登录校验。
more >>文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程。
文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。
在前端程序中要完成哪些代码:
1 | <form action="/upload" method="post" enctype="multipart/form-data"> |
上传文件的原始form表单,要求表单必须具备以下三点(上传文件页面三要素):
表单必须有file域,用于选择要上传的文件
1 <input type="file" name="image"/>
表单提交方式必须为POST
通常上传的文件会比较大,所以需要使用 POST 提交方式
表单的编码类型enctype必须要设置为:multipart/form-data
普通默认的编码格式是不适合传输大型的二进制数据的,所以在文件上传时,表单的编码格式必须设置为multipart/form-data
删除form表单中enctype属性值,会是什么情况?
此时表单的编码格式为默认值,提交后仅仅提交了该文件的名字
首先在服务端定义这么一个controller,用来进行文件上传,然后在controller当中定义一个方法来处理/upload
请求
在定义的方法中接收提交过来的数据 (方法中的形参名和请求参数的名字保持一致)
Spring中提供了一个API:MultipartFile,使用这个API就可以来接收到上传的文件
问题:如果表单项的名字和方法中形参名不一致,该怎么办?
- ~~~javascript public Result upload(String username, Integer age, MultipartFile file) //file形参名和请求参数名image不一致
1
2
3
4
5
6
7
解决:使用@RequestParam注解进行参数绑定
- ~~~java
public Result upload(String username,
Integer age,
@RequestParam("image") MultipartFile file)
UploadController代码:
1 | @Slf4j |
什么是MyBatis?
MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。
MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
官网:https://mybatis.org/mybatis-3/zh/index.html
在上面我们提到了两个词:一个是持久层,另一个是框架。
持久层:指的是就是数据访问层(dao),是用来操作数据库的。
框架:是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础上进行软件开发更加高效、规范、通用、可拓展。
创建springboot工程,并导入 mybatis的起步依赖、mysql的驱动包。
项目工程创建完成后,自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖
1 | <!-- 仅供参考:只粘贴了pom.xml中部分内容 --> |
创建用户表user,并创建对应的实体类User。
1 | -- 用户表 |
关系型数据库(RDBMS)
概念:建立在关系模型基础上,由多张相互连接的二维表组成的数据库。
而所谓二维表,指的是由行和列组成的表。
二维表的优点:
使用表存储数据,格式统一,便于维护
使用SQL语言操作,标准统一,使用方便,可用于复杂查询
我们之前提到的MySQL、Oracle、DB2、SQLServer这些都是属于关系型数据库,里面都是基于二维表存储数据的。
基于二维表存储数据的数据库就成为关系型数据库,不是基于二维表存储数据的数据库,就是非关系型数据库(比如Redis,就属于非关系型数据库)。
2. 数据模型
MySQL是关系型数据库,是基于二维表进行数据存储的,具体的结构图下:
SQL:结构化查询语言。一门操作关系型数据库的编程语言,定义操作所有关系型数据库的统一标准。
1、SQL语句可以单行或多行书写,以分号结尾。
1 | mysql> create |
2、SQL语句可以使用空格/缩进来增强语句的可读性。
3、MySQL数据库的SQL语句不区分大小写。
4、注释:
## 1.前端开发介绍
那在讲解web前端开发之前,我们先需要对web前端开发有一个整体的认知。主要明确一下三个问题:
1). 网页有哪些部分组成 ?
文字、图片、音频、视频、超链接、表格等等。
2). 我们看到的网页,背后的本质是什么 ?
程序员写的前端代码 (备注:在前后端分离的开发模式中,)
3). 前端的代码是如何转换成用户眼中的网页的 ?
通过浏览器转化(解析和渲染)成用户看到的网页
浏览器中对代码进行解析和渲染的部分,称为 浏览器内核
而市面上的浏览器非常多,比如:IE、火狐Firefox、苹果safari、欧朋、谷歌Chrome、QQ浏览器、360浏览器等等。 而且我们电脑上安装的浏览器可能都不止一个,有很多。
但是呢,需要大家注意的是,不同的浏览器,内核不同,对于相同的前端代码解析的效果也会存在差异。 那这就会造成一个问题,同一段前端程序,不同浏览器展示出来的效果是不一样的,这个用户体验就很差了。而我们想达到的效果则是,即使用户使用的是不同的浏览器,解析同一段前端代码,最终展示出来的效果都是相同的。
要想达成这样一个目标,我们就需要定义一个统一的标准,然后让各大浏览器厂商都参照这个标准来实现即可。 而这套标准呢,其实早都已经定义好了,那就是我们接下来,要介绍的web标准。
Web标准也称为网页标准,由一系列的标准组成,大部分由W3C( World Wide Web Consortium,万维网联盟)负责制定。由三个组成部分:
超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容。
标记语言:由标签构成的语言
当然了,随着技术的发展,我们为了更加快速的开发,现在也出现了很多前端开发的高级技术。例如:vue、elementui、Axios等等。
Maven是Apache旗下的一个开源项目,是一款用于管理和构建java项目的工具。
它基于项目对象模型(Project Object Model , 简称: POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。
官网:https://maven.apache.org/
Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性组织。
开源项目:https://www.apache.org/index.html#projects-list
依赖管理:
方便快捷的管理项目依赖的资源(jar包,平时使用需要手动下载并导入),避免版本冲突问题
当使用maven进行项目依赖(jar包)管理,则很方便的可以解决这个问题。 我们只需要在maven项目的pom.xml文件中,添加一段配置即可实现。
统一项目结构 :
在项目开发中,当你使用不同的开发工具 (如:Eclipse、Idea),创建项目工程时,目录结构是不同的
若我们创建的是一个maven工程,是可以帮我们自动生成统一、标准的项目目录结构:

more >>目录说明:
- src/main/java: java源代码目录
- src/main/resources: 配置文件信息
- src/test/java: 测试代码
- src/test/resources: 测试配置文件信息
1 | public class HelloWorld { //创建类 “HelloWorld”需要与文件名一致 |
使用cmd运行:(需要在该文件目录中)
1 | $ javac HelloWorld.java |
1 | int a, b, c; // 声明三个int型整数:a、 b、c |
命名规则:
只能包含 大小写字母、数字、下划线 和 $ ,且不能由数字开头。
保留字不等于关键字,保留字中的const,goto都不是关键字,是java保留以供后续版本使用的
整数类型
| 整型 | 占用字节空间大小 | 取值范围 | 默认值 |
|---|---|---|---|
| byte | 1字节 | \([-128 , 127]\) | 0 |
| short | 2字节 | \([-32768 , 32767]\) | 0 |
| int | 4字节 | $[-2^{31} , 2^{31} - 1 ] $ \(\approx 2\times{10}^9\) | 0 |
| long | 8字节 | \([-2^{63} , 2^{63} - 1]\) \(\approx 9\times{10}^{18}\) | 0L |
1 | public class HelloWorld { //创建类 “HelloWorld”需要与文件名一致 |
使用cmd运行:(需要在该文件目录中)
1 | $ javac HelloWorld.java |
1 | int a, b, c; // 声明三个int型整数:a、 b、c |
命名规则:
只能包含 大小写字母、数字、下划线 和 $ ,且不能由数字开头。
保留字不等于关键字,保留字中的const,goto都不是关键字,是java保留以供后续版本使用的
整数类型
| 整型 | 占用字节空间大小 | 取值范围 | 默认值 |
|---|---|---|---|
| byte | 1字节 | \([-128 , 127]\) | 0 |
| short | 2字节 | \([-32768 , 32767]\) | 0 |
| int | 4字节 | $[-2^{31} , 2^{31} - 1 ] $ \(\approx 2\times{10}^9\) | 0 |
| long | 8字节 | \([-2^{63} , 2^{63} - 1]\) \(\approx 9\times{10}^{18}\) | 0L |
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia-plus根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent:
meta: false
pages: false
posts:
title: true
date: true
path: true
text: false
raw: false
content: false
slug: false
updated: false
comments: false
link: false
permalink: false
excerpt: false
categories: false
tags: true