[{"data":1,"prerenderedAt":233},["ShallowReactive",2],{"posts-{\"page\":1,\"pageSize\":10,\"sort\":\"recommended\"}":3,"sidebar-tags":182,"donations-sidebar":232},{"list":4,"total":179,"page":180,"pageSize":181},[5,25,42,58,75,92,108,127,145,159],{"id":6,"title":7,"slug":8,"content":9,"excerpt":10,"coverImage":11,"status":12,"isPinned":13,"pinnedAt":14,"viewCount":15,"publishedAt":16,"createdAt":16,"updatedAt":14,"category":17,"author":21,"tags":24},"10ddeb0d-9d91-450d-a20b-10fc0fff35f5","uv 使用教程","uv-使用教程","# uv 使用教程\n\n`uv` 是一个由 Rust 编写的极速 Python 包和项目管理工具，由 Ruff 的创建者 Astral 团队开发。它旨在用单一工具替代 `pip`、`pip-tools`、`pipx`、`poetry`、`pyenv`、`twine` 和 `virtualenv` 等工具。\n\n## 🌟 核心优势\n\n- **极速体验**：比普通的 `pip` 快 10-100 倍。\n- **多合一工具**：提供全面的项目管理、通用 lockfile 支持。\n- **节省空间**：支持全局缓存，实现依赖去重。\n- **兼容性强**：包含与 `pip` 兼容的接口，零成本迁移即可获得性能提升。\n- **跨平台**：完美支持 macOS、Linux 和 Windows。\n\n---\n\n## 🚀 1. 安装与更新\n\n`uv` 提供了独立安装脚本，无需预装 Rust 或 Python。\n\n**macOS 和 Linux:**\n```bash\ncurl -LsSf https:\u002F\u002Fastral.sh\u002Fuv\u002Finstall.sh | sh\n```\n\n**Windows:**\n```powershell\npowershell -ExecutionPolicy ByPass -c \\\"irm https:\u002F\u002Fastral.sh\u002Fuv\u002Finstall.ps1 | iex\\\"\n```\n\n**使用 pip 或 pipx 安装:**\n```bash\npip install uv\n# 或者\npipx install uv\n```\n\n**自我更新:**\n```bash\nuv self update\n```\n\n---\n\n## 📦 2. 项目管理 (Projects)\n\n`uv` 可以像 `poetry` 或 `rye` 一样管理项目依赖和虚拟环境。\n\n**初始化新项目:**\n```bash\nuv init example\ncd example\n```\n\n**添加依赖包:**\n```bash\n# 这会自动创建虚拟环境（如果不存在），并更新 pyproject.toml 和 uv.lock\nuv add ruff\n```\n\n**运行代码\u002F检查:**\n```bash\nuv run ruff check\n```\n\n**锁定与同步依赖:**\n```bash\nuv lock  # 解析并生成\u002F更新 uv.lock\nuv sync  # 同步当前环境与 lockfile 保持一致\n```\n\n---\n\n## 📜 3. 脚本执行 (Scripts)\n\n`uv` 支持管理单文件脚本的依赖和环境（支持内联依赖元数据）。\n\n**创建脚本并添加依赖:**\n```bash\necho 'import requests; print(requests.get(\\\"https:\u002F\u002Fastral.sh\\\"))' > example.py\n\n# 将依赖写入脚本的内联元数据中\nuv add --script example.py requests\n```\n\n**运行脚本:**\n```bash\n# uv 会在隔离的临时虚拟环境中自动安装依赖并运行\nuv run example.py\n```\n\n---\n\n## 🛠️ 4. 工具管理 (Tools)\n\n类似于 `pipx`，`uv` 可以执行和安装由 Python 包提供的命令行工具。\n\n**在临时环境中运行工具 (使用 `uvx` 或 `uv tool run`):**\n```bash\nuvx pycowsay 'hello world!'\n```\n\n**全局安装工具:**\n```bash\nuv tool install ruff\nruff --version\n```\n\n---\n\n## 🐍 5. Python 版本管理\n\n`uv` 可以自动下载、安装并切换 Python 版本。\n\n**安装多个 Python 版本:**\n```bash\nuv python install 3.12 3.13 3.14\n```\n\n**使用特定版本创建虚拟环境:**\n```bash\nuv venv --python 3.12.0\n```\n\n**在当前目录固定 Python 版本:**\n```bash\nuv python pin 3.11\n# 这会生成一个 .python-version 文件\n```\n\n**使用特定版本直接运行:**\n```bash\nuv run --python pypy@3.8 -- python --version\n```\n\n---\n\n## 🔄 6. pip 兼容接口\n\n如果你暂时不想改变现有的工作流，可以直接使用 `uv pip` 接口，它不仅完全兼容 `pip` 和 `pip-tools`，还能带来百倍的性能提升。\n\n**编译 requirements (替代 pip-compile):**\n```bash\nuv pip compile requirements.in --universal --output-file requirements.txt\n```\n\n**创建虚拟环境 (替代 virtualenv):**\n```bash\nuv venv\nsource .venv\u002Fbin\u002Factivate  # macOS\u002FLinux\n# .venv\\Scripts\\activate   # Windows\n```\n\n**安装依赖 (替代 pip install):**\n```bash\nuv pip install -r requirements.txt\nuv pip install flask\n```",null,"https:\u002F\u002Fcdn.xiaolong0418.com\u002Fapp%2Fbase%2FScreenShot_2026-04-02_181145_384_4aa08558cacf4ac18586a87a92f37d80.png","PUBLISHED",true,"2026-05-25T02:21:44.000Z",347,"2026-04-02T10:12:25.000Z",{"id":18,"name":19,"slug":20},"73a5f62c-3c47-45b9-9ae2-f29953ae8dc0","Node","node",{"id":22,"name":23,"avatar":10},"f9d0f2de-c700-4f90-b535-afd3dbe78128","Admin",[],{"id":26,"title":27,"slug":28,"content":29,"excerpt":10,"coverImage":30,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":32,"publishedAt":33,"createdAt":33,"updatedAt":34,"category":35,"author":36,"tags":37},"84e57f46-a67a-4a44-ba12-bc889f751ed9","MySQL 9 快速入门教程：从零开始掌握核心操作","mysql-9-快速入门教程-从零开始掌握核心操作","MySQL 9作为最新的版本，引入了一些新特性，不过对于初学者来说，核心的SQL操作和数据库设计思想依然是一脉相承的。这篇教程旨在帮你快速建立对MySQL 9的整体认识，并掌握最基本的操作。\n\n---\n\n# MySQL 9 快速入门教程：从零开始掌握核心操作\n\nMySQL 是目前世界上最受欢迎的开源关系型数据库之一。因其体积小、速度快、总体拥有成本低，被大量中小型企业和互联网公司采用 。2024年，MySQL 9 正式推出，带来了性能优化和细微的功能增强 。本教程将基于最新的 MySQL 9 版本，带你从零开始，完成安装、连接、建库、建表以及基本的增删改查操作。\n\n## 1. MySQL 9 的安装与连接\n\n在开始学习 SQL 语句之前，我们需要先在电脑上安装 MySQL 并成功连接上它。\n\n### 1.1 快速安装指南\n\n你可以根据你的操作系统选择不同的安装方式：\n\n- **Windows \u002F macOS \u002F Linux**：最推荐的方式是前往 MySQL 官方网站下载针对你操作系统的**安装包（MSI \u002F DMG \u002F RPM）**。对于初学者，使用图形化安装向导可以省去很多配置的麻烦 。\n- **通过 Docker 安装（推荐尝鲜）**：如果你想保持电脑环境的整洁，或者想快速体验 MySQL 9 而避免复杂的安装步骤，Docker 是最佳选择。\n    1. 拉取 MySQL 9 镜像（以 9.4 为例）：\n        ```bash\n        docker pull mysql:9.4.0\n        ```\n    2. 运行容器并设置 root 用户的密码：\n        ```bash\n        docker run -d --name mysql9-demo -p 3306:3306 -e MYSQL_ROOT_PASSWORD=你的密码 mysql:9.4.0\n        ```\n        这样，一个 MySQL 9 服务就在后台启动，并映射在本机的 3306 端口了 。\n\n### 1.2 登录 MySQL\n\n安装完成后，我们需要使用 MySQL 自带的命令行客户端连接服务器。\n\n打开终端（或 Windows 的命令提示符），输入以下命令：\n\n```bash\nmysql -u root -p\n```\n\n按回车后，系统会提示你输入密码。输入安装时设置的 root 密码，即可看到欢迎界面，进入 `mysql>` 命令行模式 。\n\n## 2. 数据库和数据表的基本操作\n\n进入 MySQL 客户端后，我们就可以开始操作数据库了。如果把数据库比作一个仓库，那么数据表就是仓库里一个个货架。\n\n### 2.1 数据库操作\n\n- **查看现有数据库**：\n  ```sql\n  SHOW DATABASES;\n  ```\n  初始化后，通常会看到 `information_schema`、`mysql`、`performance_schema` 和 `sys` 这几个系统库 。\n\n- **创建新数据库**：\n  我们来创建一个名为 `shop` （商城）的数据库。\n  ```sql\n  CREATE DATABASE shop;\n  ```\n\n- **选择使用数据库**：\n  接下来的所有操作都会在 `shop` 数据库中进行。\n  ```sql\n  USE shop;\n  ```\n\n### 2.2 数据类型简介\n\n在创建表之前，需要了解几个最常用的数据类型，这决定了“货架”上可以放什么类型的“商品” ：\n- **数值类型**：`INT` 整数，`DECIMAL(10,2)` 小数（如价格）。\n- **字符串类型**：`VARCHAR(255)` 可变长度字符串（如用户名、地址），`TEXT` 长文本（如商品详情）。\n- **日期类型**：`DATE` （日期），`DATETIME` （日期和时间，如订单创建时间）。\n\n### 2.3 创建数据表\n\n我们创建一个简单的 `users` （用户表）和 `orders` （订单表）来演示。\n\n- **创建用户表 （users）**：\n  ```sql\n  CREATE TABLE users (\n      id INT AUTO_INCREMENT PRIMARY KEY, -- 用户ID，自动增长，并设为主键\n      username VARCHAR(50) NOT NULL UNIQUE, -- 用户名，非空，且唯一\n      email VARCHAR(100) NOT NULL, -- 邮箱，非空\n      created_at DATETIME DEFAULT CURRENT_TIMESTAMP -- 创建时间，默认为当前时间\n  );\n  ```\n  `AUTO_INCREMENT` 可以让 `id` 字段在插入新记录时自动加1，`PRIMARY KEY` 则保证了每条记录的唯一性 。\n\n- **查看表结构**：\n  ```sql\n  DESCRIBE users;\n  ```\n  或者\n  ```sql\n  SHOW CREATE TABLE users \\G\n  ```\n  前者显示简要结构，后者显示建表的详细 SQL 语句 。\n\n## 3. 数据的增删改查（CRUD）\n\n有了表和字段，接下来就是最核心的数据操作：**增（Create）、删（Delete）、改（Update）、查（Retrieve）**。\n\n### 3.1 插入数据 （INSERT）\n\n向 `users` 表中添加几条用户记录。\n\n```sql\n-- 插入一条数据，id 会自动生成，created_at 会自动填充当前时间\nINSERT INTO users (username, email) VALUES ('张三', 'zhangsan@email.com');\n\n-- 插入多条数据\nINSERT INTO users (username, email) VALUES\n    ('李四', 'lisi@email.com'),\n    ('王五', 'wangwu@email.com');\n```\n\n### 3.2 查询数据 （SELECT）\n\n查询是数据库中使用最频繁的操作。\n\n- **查询所有数据**：\n  ```sql\n  SELECT * FROM users;\n  ```\n  `*` 代表所有字段，但在实际生产环境中，建议只列出需要查询的字段名。\n\n- **查询特定字段并添加条件**：\n  查询用户名为“张三”的邮箱和注册时间。\n  ```sql\n  SELECT email, created_at FROM users WHERE username='张三';\n  ```\n  `WHERE` 关键字用于过滤数据，类似编程中的 `if` 条件 。\n\n### 3.3 更新数据 （UPDATE）\n\n如果用户信息发生变化，需要修改数据。\n\n- **修改数据**：\n  把用户 “李四” 的邮箱更新为新地址。\n  ```sql\n  UPDATE users SET email = 'lisi_new@email.com' WHERE username = '李四';\n  ```\n  **⚠️ 警告**：`UPDATE` 语句中一定要加 `WHERE` 条件，否则会**修改表中所有行的数据**！\n\n### 3.4 删除数据 （DELETE）\n\n删除不再需要的用户记录。\n\n- **删除数据**：\n  删除用户名为 “王五” 的用户。\n  ```sql\n  DELETE FROM users WHERE username = '王五';\n  ```\n  **⚠️ 警告**：同样，`DELETE` 语句也必须加 `WHERE` 条件，否则会清空整个表！\n\n## 4. 高级查询初步\n\n掌握基础增删改查后，我们来了解一下稍微复杂一点的查询，为后续深入学习做准备。\n\n### 4.1 条件与排序\n\n- **查询并排序**：\n  查询所有用户，并按照注册时间倒序排列（最新的在前）。\n  ```sql\n  SELECT * FROM users ORDER BY created_at DESC;\n  ```\n  `DESC` 表示降序，`ASC` 表示升序（默认）。\n\n- **限制结果数量**：\n  只查询前2条数据。\n  ```sql\n  SELECT * FROM users LIMIT 2;\n  ```\n\n### 4.2 简单的表连接 （JOIN）\n\n假设我们创建了订单表 `orders`，里面包含 `user_id` 字段指向 `users` 表的 `id`。如果我们想查看每个订单对应的用户名，就需要将两张表连接起来查询。\n\n这是一个**内连接（INNER JOIN）**的例子：\n```sql\nSELECT orders.id AS order_id, users.username, orders.amount\nFROM orders\nINNER JOIN users ON orders.user_id = users.id;\n```\n这个查询会返回订单表中的订单ID、金额，以及通过 `user_id` 匹配到的用户表中的用户名 。\n\n### 4.3 使用聚合函数\n\n如果想统计用户总数或订单总金额，就需要使用聚合函数。\n\n```sql\n-- 统计用户总数\nSELECT COUNT(*) FROM users;\n\n-- 计算所有订单的总金额\nSELECT SUM(amount) FROM orders;\n```\n\n## 5. 总结与下一步\n\n至此，你已经完成了 MySQL 9 的快速入门。回顾一下，我们学习了：\n1.  如何安装和连接 MySQL 9。\n2.  数据库和数据表的新建与查看。\n3.  最核心的增删改查（CRUD）操作。\n4.  初步了解了排序、连接和聚合等进阶查询技巧。\n\n这些基础知识是你操作 MySQL 的基石。在后续的学习中，你可以继续探索 ：\n- **索引的设计和使用**：用于提升海量数据下的查询速度。\n- **视图与存储过程**：封装复杂的 SQL 逻辑。\n- **事务控制**：保证数据的一致性和完整性（如银行转账）。\n- **权限与安全管理**：控制不同用户的访问权限。\n\n希望这篇教程能帮你快速开启 MySQL 9 的学习之旅！","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fapp%2Fbase%2F1_5f66853e84fb465e82b1f8e7fc8be5bd.png",false,384,"2026-03-06T11:10:56.000Z","2026-05-25T03:00:10.000Z",{"id":18,"name":19,"slug":20},{"id":22,"name":23,"avatar":10},[38],{"id":39,"name":40,"slug":41},"c95bbe84-bdd0-410a-86a9-e87958c55f4f","Redis","redis",{"id":43,"title":44,"slug":45,"content":46,"excerpt":44,"coverImage":47,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":48,"publishedAt":49,"createdAt":49,"updatedAt":50,"category":51,"author":54,"tags":55},"ad58bc75-cc41-46ae-9b4e-b989508b9289","Reqable抓包教程","reqable抓包教程","# win安装adb,将文件下载下来，解压缩到自定义的安装目录\n### Windows版本\n\n```language\nhttps:\u002F\u002Fdl.google.com\u002Fandroid\u002Frepository\u002Fplatform-tools-latest-windows.zip\n```\n\n### Mac版本：\n```language\nhttps:\u002F\u002Fdl.google.com\u002Fandroid\u002Frepository\u002Fplatform-tools-latest-darwin.zip\n```\n\n### Linux版本：\n```language\nhttps:\u002F\u002Fdl.google.com\u002Fandroid\u002Frepository\u002Fplatform-tools-latest-linux.zip\n```\n\n\n# 配置环境变量\n![](https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F6d5c7654ae4b80cc052c3fe08180f5e1_1765157264912.png)\n\n\n# MuMu模拟器\n\n```language\nhttps:\u002F\u002Fmumu.163.com\u002F\n```\n\n\n\n\n# 电脑和模拟器安装reqable\n\n```language\nhttps:\u002F\u002Freqable.com\u002Fzh-CN\u002F\n```\n\n# 模拟器启动\n![](https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002Fc6e16cd9160316db4826dc1dd313d4ba_1765158631619.png)\n\n\n# adb连接\n\n```language\nadb connect 127.0.0.1:5557\n```\n\n\n# 安装证书\n![](https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F8a1bfdef0d3e4c0abfc67f4701995476_1765158526195.png)\n![](https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F35c98167dde928846546a19430e07fe1_1765158531940.png)\n\n\n\n\n\n\n","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F54904d03e74b54f5a05ea883bcfe449e_1765158486377.png",832,"2025-12-08T01:48:15.000Z","2026-05-25T06:10:42.000Z",{"id":52,"name":53,"slug":53},"9ed9827c-9cbb-42da-80e4-d04c7fdba886","开发工具",{"id":22,"name":23,"avatar":10},[56],{"id":57,"name":53,"slug":53},"f32faa96-f2ec-45c6-9a17-2c76062edcb0",{"id":59,"title":60,"slug":60,"content":61,"excerpt":62,"coverImage":63,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":64,"publishedAt":65,"createdAt":65,"updatedAt":66,"category":67,"author":71,"tags":72},"5ae0e368-2586-4030-847d-8fa2c78e839a","前端蒙层中间挖空效果","# 前端蒙层中间挖空效果实现\n\n我将创建一个带有中间挖空效果的蒙层，这种效果常用于引导用户注意力或突出显示特定区域。\n\n## 设计思路\n- 创建一个覆盖全屏的半透明蒙层\n- 使用CSS的`clip-path`或`mask`属性在中间创建透明区域\n- 添加交互功能，如点击蒙层关闭\n\n## 实现代码\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml lang=\\\"zh-CN\\\">\n\u003Chead>\n    \u003Cmeta charset=\\\"UTF-8\\\">\n    \u003Cmeta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\">\n    \u003Ctitle>蒙层中间挖空效果\u003C\u002Ftitle>\n    \u003Cstyle>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        \n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);\n            color: #333;\n            line-height: 1.6;\n            padding: 20px;\n            min-height: 100vh;\n        }\n        \n        .container {\n            max-width: 1200px;\n            margin: 0 auto;\n        }\n        \n        header {\n            text-align: center;\n            margin-bottom: 40px;\n            color: white;\n        }\n        \n        h1 {\n            font-size: 2.5rem;\n            margin-bottom: 10px;\n            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);\n        }\n        \n        .subtitle {\n            font-size: 1.2rem;\n            opacity: 0.9;\n        }\n        \n        .content {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n            gap: 20px;\n            margin-bottom: 40px;\n        }\n        \n        .card {\n            background: white;\n            border-radius: 10px;\n            padding: 20px;\n            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);\n            transition: transform 0.3s ease;\n        }\n        \n        .card:hover {\n            transform: translateY(-5px);\n        }\n        \n        .card h3 {\n            margin-bottom: 15px;\n            color: #2575fc;\n        }\n        \n        .demo-area {\n            display: flex;\n            flex-wrap: wrap;\n            gap: 15px;\n            justify-content: center;\n            margin-top: 30px;\n        }\n        \n        .btn {\n            background: #2575fc;\n            color: white;\n            border: none;\n            padding: 12px 25px;\n            border-radius: 5px;\n            cursor: pointer;\n            font-size: 1rem;\n            transition: background 0.3s ease;\n            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);\n        }\n        \n        .btn:hover {\n            background: #1a68e8;\n        }\n        \n        .btn-secondary {\n            background: #6a11cb;\n        }\n        \n        .btn-secondary:hover {\n            background: #5a0db3;\n        }\n        \n        \u002F* 蒙层样式 *\u002F\n        .overlay {\n            position: fixed;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.7);\n            display: none;\n            z-index: 1000;\n        }\n        \n        \u002F* 方法1: 使用clip-path *\u002F\n        .overlay-clip {\n            clip-path: polygon(\n                0% 0%, \n                0% 100%, \n                25% 100%, \n                25% 25%, \n                75% 25%, \n                75% 75%, \n                25% 75%, \n                25% 100%, \n                100% 100%, \n                100% 0%\n            );\n        }\n        \n        \u002F* 方法2: 使用mask *\u002F\n        .overlay-mask {\n            -webkit-mask: radial-gradient(circle at center, transparent 100px, black 100px);\n            mask: radial-gradient(circle at center, transparent 100px, black 100px);\n        }\n        \n        \u002F* 方法3: 使用多个div组合 *\u002F\n        .overlay-complex {\n            background: none;\n        }\n        \n        .overlay-complex::before,\n        .overlay-complex::after {\n            content: '';\n            position: absolute;\n            background: rgba(0, 0, 0, 0.7);\n        }\n        \n        .overlay-complex::before {\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 40%;\n        }\n        \n        .overlay-complex::after {\n            bottom: 0;\n            left: 0;\n            width: 100%;\n            height: 40%;\n        }\n        \n        .overlay-left, .overlay-right {\n            position: absolute;\n            background: rgba(0, 0, 0, 0.7);\n            top: 40%;\n            height: 20%;\n        }\n        \n        .overlay-left {\n            left: 0;\n            width: 40%;\n        }\n        \n        .overlay-right {\n            right: 0;\n            width: 40%;\n        }\n        \n        .highlight-box {\n            position: absolute;\n            top: 40%;\n            left: 40%;\n            width: 20%;\n            height: 20%;\n            border: 2px dashed #ffcc00;\n            border-radius: 5px;\n            box-shadow: 0 0 0 9999px rgba(255, 204, 0, 0.2);\n        }\n        \n        .overlay-content {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            background: white;\n            padding: 20px;\n            border-radius: 10px;\n            text-align: center;\n            max-width: 400px;\n            width: 90%;\n            z-index: 1001;\n        }\n        \n        .close-btn {\n            position: absolute;\n            top: 10px;\n            right: 15px;\n            background: none;\n            border: none;\n            font-size: 1.5rem;\n            cursor: pointer;\n            color: #666;\n        }\n        \n        footer {\n            text-align: center;\n            color: white;\n            margin-top: 40px;\n            padding-top: 20px;\n            border-top: 1px solid rgba(255, 255, 255, 0.2);\n        }\n    \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Cdiv class=\\\"container\\\">\n        \u003Cheader>\n            \u003Ch1>前端蒙层中间挖空效果\u003C\u002Fh1>\n            \u003Cp class=\\\"subtitle\\\">多种方法实现蒙层中间挖空，突出显示特定内容区域\u003C\u002Fp>\n        \u003C\u002Fheader>\n        \n        \u003Cdiv class=\\\"content\\\">\n            \u003Cdiv class=\\\"card\\\">\n                \u003Ch3>方法一：使用 clip-path\u003C\u002Fh3>\n                \u003Cp>通过 CSS clip-path 属性裁剪出中间透明区域，实现挖空效果。\u003C\u002Fp>\n                \u003Cp>优点：代码简洁，性能较好。\u003C\u002Fp>\n                \u003Cp>缺点：形状固定，不够灵活。\u003C\u002Fp>\n                \u003Cdiv class=\\\"demo-area\\\">\n                    \u003Cbutton class=\\\"btn\\\" id=\\\"showClipOverlay\\\">演示效果\u003C\u002Fbutton>\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n            \n            \u003Cdiv class=\\\"card\\\">\n                \u003Ch3>方法二：使用 mask\u003C\u002Fh3>\n                \u003Cp>使用 CSS mask 属性创建径向渐变，中间透明，四周不透明。\u003C\u002Fp>\n                \u003Cp>优点：可以实现圆形挖空，代码简洁。\u003C\u002Fp>\n                \u003Cp>缺点：兼容性稍差。\u003C\u002Fp>\n                \u003Cdiv class=\\\"demo-area\\\">\n                    \u003Cbutton class=\\\"btn\\\" id=\\\"showMaskOverlay\\\">演示效果\u003C\u002Fbutton>\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n            \n            \u003Cdiv class=\\\"card\\\">\n                \u003Ch3>方法三：使用多个元素组合\u003C\u002Fh3>\n                \u003Cp>通过多个 div 元素组合，模拟中间挖空效果。\u003C\u002Fp>\n                \u003Cp>优点：兼容性最好，形状灵活。\u003C\u002Fp>\n                \u003Cp>缺点：代码较多，结构复杂。\u003C\u002Fp>\n                \u003Cdiv class=\\\"demo-area\\\">\n                    \u003Cbutton class=\\\"btn btn-secondary\\\" id=\\\"showComplexOverlay\\\">演示效果\u003C\u002Fbutton>\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \n    \u003C\u002Fdiv>\n    \n    \u003C!-- 方法1: clip-path 蒙层 -->\n    \u003Cdiv class=\\\"overlay overlay-clip\\\" id=\\\"clipOverlay\\\">\n        \u003Cdiv class=\\\"overlay-content\\\">\n            \u003Cbutton class=\\\"close-btn\\\">&times;\u003C\u002Fbutton>\n            \u003Ch3>clip-path 挖空效果\u003C\u002Fh3>\n            \u003Cp>这种方法使用 CSS clip-path 属性裁剪出中间的矩形区域。\u003C\u002Fp>\n            \u003Cp>点击蒙层或关闭按钮可以关闭此提示。\u003C\u002Fp>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \n    \u003C!-- 方法2: mask 蒙层 -->\n    \u003Cdiv class=\\\"overlay overlay-mask\\\" id=\\\"maskOverlay\\\">\n        \u003Cdiv class=\\\"overlay-content\\\">\n            \u003Cbutton class=\\\"close-btn\\\">&times;\u003C\u002Fbutton>\n            \u003Ch3>mask 挖空效果\u003C\u002Fh3>\n            \u003Cp>这种方法使用 CSS mask 属性创建径向渐变，实现圆形挖空。\u003C\u002Fp>\n            \u003Cp>点击蒙层或关闭按钮可以关闭此提示。\u003C\u002Fp>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \n    \u003C!-- 方法3: 多个元素组合蒙层 -->\n    \u003Cdiv class=\\\"overlay overlay-complex\\\" id=\\\"complexOverlay\\\">\n        \u003Cdiv class=\\\"overlay-left\\\">\u003C\u002Fdiv>\n        \u003Cdiv class=\\\"overlay-right\\\">\u003C\u002Fdiv>\n        \u003Cdiv class=\\\"highlight-box\\\">\u003C\u002Fdiv>\n        \u003Cdiv class=\\\"overlay-content\\\">\n            \u003Cbutton class=\\\"close-btn\\\">&times;\u003C\u002Fbutton>\n            \u003Ch3>多个元素组合挖空效果\u003C\u002Fh3>\n            \u003Cp>这种方法使用多个 div 元素组合，模拟中间挖空效果。\u003C\u002Fp>\n            \u003Cp>点击蒙层或关闭按钮可以关闭此提示。\u003C\u002Fp>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \n    \u003Cscript>\n        \u002F\u002F 获取元素\n        const clipOverlay = document.getElementById('clipOverlay');\n        const maskOverlay = document.getElementById('maskOverlay');\n        const complexOverlay = document.getElementById('complexOverlay');\n        \n        const showClipBtn = document.getElementById('showClipOverlay');\n        const showMaskBtn = document.getElementById('showMaskOverlay');\n        const showComplexBtn = document.getElementById('showComplexOverlay');\n        \n        const closeButtons = document.querySelectorAll('.close-btn');\n        \n        \u002F\u002F 显示蒙层函数\n        function showOverlay(overlay) {\n            overlay.style.display = 'block';\n        }\n        \n        \u002F\u002F 隐藏蒙层函数\n        function hideOverlay(overlay) {\n            overlay.style.display = 'none';\n        }\n        \n        \u002F\u002F 绑定事件\n        showClipBtn.addEventListener('click', () => showOverlay(clipOverlay));\n        showMaskBtn.addEventListener('click', () => showOverlay(maskOverlay));\n        showComplexBtn.addEventListener('click', () => showOverlay(complexOverlay));\n        \n        \u002F\u002F 点击蒙层关闭\n        clipOverlay.addEventListener('click', (e) => {\n            if (e.target === clipOverlay) hideOverlay(clipOverlay);\n        });\n        \n        maskOverlay.addEventListener('click', (e) => {\n            if (e.target === maskOverlay) hideOverlay(maskOverlay);\n        });\n        \n        complexOverlay.addEventListener('click', (e) => {\n            if (e.target === complexOverlay) hideOverlay(complexOverlay);\n        });\n        \n        \u002F\u002F 点击关闭按钮关闭\n        closeButtons.forEach(btn => {\n            btn.addEventListener('click', () => {\n                hideOverlay(clipOverlay);\n                hideOverlay(maskOverlay);\n                hideOverlay(complexOverlay);\n            });\n        });\n    \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n## 功能说明\n\n这个页面展示了三种实现蒙层中间挖空的方法：\n\n1. **使用 clip-path**：通过CSS的clip-path属性裁剪出中间透明区域\n2. **使用 mask**：通过CSS的mask属性创建径向渐变实现圆形挖空\n3. **使用多个元素组合**：通过多个div元素组合模拟中间挖空效果\n\n每种方法都有其优缺点：\n- clip-path：代码简洁但形状固定\n- mask：可以实现圆形挖空但兼容性稍差\n- 多个元素组合：兼容性最好但代码较多\n\n用户可以通过点击页面上的按钮来查看每种方法的效果，点击蒙层或关闭按钮可以关闭提示。\n\n这个设计采用了现代化UI，包含卡片布局、渐变背景和交互动画，提供了良好的用户体验。","多种方法实现蒙层中间挖空，突出显示特定内容区域","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F62a992ec61723351b751f2a2d844fb99_1762916206941.png",455,"2025-11-12T03:00:54.000Z","2026-05-24T20:42:31.000Z",{"id":68,"name":69,"slug":70},"11d4d397-685c-4180-a7b3-9b0e3a1e411e","Css","css",{"id":22,"name":23,"avatar":10},[73],{"id":74,"name":69,"slug":70},"b084ddd8-09be-4e57-98f0-cf4e376aecd7",{"id":76,"title":77,"slug":78,"content":79,"excerpt":77,"coverImage":80,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":81,"publishedAt":82,"createdAt":82,"updatedAt":83,"category":84,"author":87,"tags":88},"a4e1d5e8-65a8-4e34-bafc-415f013cc83e","Java 21 零基础入门教程","java-21-零基础入门教程","# Java 21 零基础入门教程\n\n> 本文档提供完整的 Java 21 入门教程，从环境搭建到面向对象编程，包含最新的 Java 21 特性。\n\n## 目录\n\n- [第一部分：Java 简介与环境搭建](#第一部分java-简介与环境搭建)\n- [第二部分：第一个 Java 程序](#第二部分第一个-java-程序)\n- [第三部分：Java 基础语法](#第三部分java-基础语法)\n- [第四部分：流程控制](#第四部分流程控制)\n- [第五部分：数组](#第五部分数组)\n- [第六部分：方法（函数）](#第六部分方法函数)\n- [第七部分：面向对象编程](#第七部分面向对象编程)\n- [第八部分：Java 21 新特性](#第八部分java-21-新特性)\n- [第九部分：异常处理](#第九部分异常处理)\n- [第十部分：集合框架](#第十部分集合框架)\n- [学习建议](#学习建议)\n- [下一步学习方向](#下一步学习方向)\n\n## 第一部分：Java 简介与环境搭建\n\n### 1.1 什么是 Java？\n\nJava 是一种广泛使用的编程语言，具有以下特点：\n\n- **跨平台**：一次编写，到处运行\n- **面向对象**：支持封装、继承、多态\n- **简单易学**：语法类似 C++，但更简洁\n- **开源免费**：拥有强大的社区支持\n\n### 1.2 安装 Java 开发环境\n\n#### 下载 JDK 21\n\n1. 访问 [Oracle 官网](https:\u002F\u002Fwww.oracle.com\u002Fjava\u002Ftechnologies\u002Fjavase-jdk21-downloads.html)\n2. 选择对应操作系统的 JDK 21\n3. 下载并安装\n\n#### 配置环境变量\n\n**Windows**：\n\n```bash\n# 添加系统变量\nJAVA_HOME = C:\\Program Files\\Java\\jdk-21\nPATH = %JAVA_HOME%\\bin;...\n```\n\n**Mac\u002FLinux**：\n\n```bash\n# 编辑 ~\u002F.bashrc 或 ~\u002F.zshrc\nexport JAVA_HOME=\u002FLibrary\u002FJava\u002FJavaVirtualMachines\u002Fjdk-21.jdk\u002FContents\u002FHome\nexport PATH=$JAVA_HOME\u002Fbin:$PATH\n```\n\n#### 验证安装\n\n打开终端\u002F命令提示符，输入：\n\n```bash\njava -version\njavac -version\n```\n\n## 第二部分：第一个 Java 程序\n\n### 2.1 Hello World 程序\n\n创建文件 `HelloWorld.java`：\n\n```java\npublic class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\\\"Hello, World!\\\");\n    }\n}\n```\n\n编译和运行：\n\n```bash\njavac HelloWorld.java    # 编译\njava HelloWorld          # 运行\n```\n\n### 2.2 程序结构解析\n\n- `public class HelloWorld`：定义类，类名必须与文件名相同\n- `public static void main(String[] args)`：程序入口点\n- `System.out.println()`：输出语句\n\n## 第三部分：Java 基础语法\n\n### 3.1 变量和数据类型\n\n```java\npublic class Variables {\n    public static void main(String[] args) {\n        \u002F\u002F 基本数据类型\n        int age = 25;                    \u002F\u002F 整数\n        double price = 19.99;            \u002F\u002F 浮点数\n        char grade = 'A';                \u002F\u002F 字符\n        boolean isJavaFun = true;        \u002F\u002F 布尔值\n        String name = \\\"张三\\\";            \u002F\u002F 字符串\n        \n        \u002F\u002F 输出变量\n        System.out.println(\\\"姓名: \\\" + name);\n        System.out.println(\\\"年龄: \\\" + age);\n        System.out.println(\\\"价格: \\\" + price);\n    }\n}\n```\n\n### 3.2 数据类型详解\n\n| 类型 | 大小 | 范围 | 示例 |\n|------|------|------|------|\n| byte | 8位 | -128 ~ 127 | `byte b = 100;` |\n| short | 16位 | -32768 ~ 32767 | `short s = 1000;` |\n| int | 32位 | -2³¹ ~ 2³¹-1 | `int i = 100000;` |\n| long | 64位 | -2⁶³ ~ 2⁶³-1 | `long l = 100000L;` |\n| float | 32位 | 单精度浮点数 | `float f = 3.14f;` |\n| double | 64位 | 双精度浮点数 | `double d = 3.14;` |\n| char | 16位 | Unicode字符 | `char c = 'A';` |\n| boolean | 1位 | true\u002Ffalse | `boolean flag = true;` |\n\n### 3.3 运算符\n\n```java\npublic class Operators {\n    public static void main(String[] args) {\n        int a = 10, b = 3;\n        \n        \u002F\u002F 算术运算符\n        System.out.println(\\\"a + b = \\\" + (a + b));  \u002F\u002F 13\n        System.out.println(\\\"a - b = \\\" + (a - b));  \u002F\u002F 7\n        System.out.println(\\\"a * b = \\\" + (a * b));  \u002F\u002F 30\n        System.out.println(\\\"a \u002F b = \\\" + (a \u002F b));  \u002F\u002F 3\n        System.out.println(\\\"a % b = \\\" + (a % b));  \u002F\u002F 1\n        \n        \u002F\u002F 比较运算符\n        System.out.println(\\\"a == b: \\\" + (a == b)); \u002F\u002F false\n        System.out.println(\\\"a > b: \\\" + (a > b));   \u002F\u002F true\n        \n        \u002F\u002F 逻辑运算符\n        boolean x = true, y = false;\n        System.out.println(\\\"x && y: \\\" + (x && y)); \u002F\u002F false\n        System.out.println(\\\"x || y: \\\" + (x || y)); \u002F\u002F true\n        System.out.println(\\\"!x: \\\" + (!x));         \u002F\u002F false\n    }\n}\n```\n\n## 第四部分：流程控制\n\n### 4.1 条件语句\n\n```java\npublic class Conditionals {\n    public static void main(String[] args) {\n        int score = 85;\n        \n        \u002F\u002F if-else 语句\n        if (score >= 90) {\n            System.out.println(\\\"优秀\\\");\n        } else if (score >= 80) {\n            System.out.println(\\\"良好\\\");\n        } else if (score >= 60) {\n            System.out.println(\\\"及格\\\");\n        } else {\n            System.out.println(\\\"不及格\\\");\n        }\n        \n        \u002F\u002F switch 语句 (Java 14+ 新语法)\n        String grade = switch (score \u002F 10) {\n            case 10, 9 -> \\\"A\\\";\n            case 8 -> \\\"B\\\";\n            case 7 -> \\\"C\\\";\n            case 6 -> \\\"D\\\";\n            default -> \\\"F\\\";\n        };\n        System.out.println(\\\"等级: \\\" + grade);\n    }\n}\n```\n\n### 4.2 循环语句\n\n```java\npublic class Loops {\n    public static void main(String[] args) {\n        \u002F\u002F for 循环\n        System.out.println(\\\"for 循环:\\\");\n        for (int i = 1; i \u003C= 5; i++) {\n            System.out.println(\\\"数字: \\\" + i);\n        }\n        \n        \u002F\u002F while 循环\n        System.out.println(\\\"\\\nwhile 循环:\\\");\n        int j = 1;\n        while (j \u003C= 3) {\n            System.out.println(\\\"计数: \\\" + j);\n            j++;\n        }\n        \n        \u002F\u002F do-while 循环\n        System.out.println(\\\"\\\ndo-while 循环:\\\");\n        int k = 1;\n        do {\n            System.out.println(\\\"值: \\\" + k);\n            k++;\n        } while (k \u003C= 3);\n        \n        \u002F\u002F 增强 for 循环\n        System.out.println(\\\"\\\n增强 for 循环:\\\");\n        int[] numbers = {1, 2, 3, 4, 5};\n        for (int num : numbers) {\n            System.out.println(\\\"数组元素: \\\" + num);\n        }\n    }\n}\n```\n\n## 第五部分：数组\n\n### 5.1 数组的基本操作\n\n```java\npublic class ArraysDemo {\n    public static void main(String[] args) {\n        \u002F\u002F 数组声明和初始化\n        int[] numbers = new int[5];           \u002F\u002F 长度为5的数组\n        int[] scores = {90, 85, 78, 92, 88}; \u002F\u002F 直接初始化\n        \n        \u002F\u002F 访问和修改数组元素\n        numbers[0] = 10;\n        numbers[1] = 20;\n        \n        System.out.println(\\\"第一个分数: \\\" + scores[0]);\n        System.out.println(\\\"数组长度: \\\" + scores.length);\n        \n        \u002F\u002F 遍历数组\n        System.out.println(\\\"所有分数:\\\");\n        for (int i = 0; i \u003C scores.length; i++) {\n            System.out.println(\\\"分数 \\\" + i + \\\": \\\" + scores[i]);\n        }\n        \n        \u002F\u002F 使用增强 for 循环\n        System.out.println(\\\"使用增强 for 循环:\\\");\n        for (int score : scores) {\n            System.out.println(score);\n        }\n    }\n}\n```\n\n### 5.2 多维数组\n\n```java\npublic class MultiDimArray {\n    public static void main(String[] args) {\n        \u002F\u002F 二维数组\n        int[][] matrix = {\n            {1, 2, 3},\n            {4, 5, 6},\n            {7, 8, 9}\n        };\n        \n        \u002F\u002F 遍历二维数组\n        for (int i = 0; i \u003C matrix.length; i++) {\n            for (int j = 0; j \u003C matrix[i].length; j++) {\n                System.out.print(matrix[i][j] + \\\" \\\");\n            }\n            System.out.println();\n        }\n    }\n}\n```\n\n## 第六部分：方法（函数）\n\n### 6.1 方法的定义和使用\n\n```java\npublic class Methods {\n    \n    \u002F\u002F 无返回值的方法\n    public static void greet(String name) {\n        System.out.println(\\\"你好, \\\" + name + \\\"!\\\");\n    }\n    \n    \u002F\u002F 有返回值的方法\n    public static int add(int a, int b) {\n        return a + b;\n    }\n    \n    \u002F\u002F 方法重载\n    public static double add(double a, double b) {\n        return a + b;\n    }\n    \n    public static void main(String[] args) {\n        \u002F\u002F 调用方法\n        greet(\\\"世界\\\");\n        \n        int sum1 = add(5, 3);\n        double sum2 = add(2.5, 3.7);\n        \n        System.out.println(\\\"整数和: \\\" + sum1);\n        System.out.println(\\\"浮点数和: \\\" + sum2);\n        \n        \u002F\u002F 递归方法示例\n        int factorial = factorial(5);\n        System.out.println(\\\"5的阶乘: \\\" + factorial);\n    }\n    \n    \u002F\u002F 递归方法\n    public static int factorial(int n) {\n        if (n == 0 || n == 1) {\n            return 1;\n        }\n        return n * factorial(n - 1);\n    }\n}\n```\n\n## 第七部分：面向对象编程\n\n### 7.1 类和对象\n\n```java\n\u002F\u002F 定义类\nclass Student {\n    \u002F\u002F 属性（字段）\n    String name;\n    int age;\n    String studentId;\n    \n    \u002F\u002F 构造方法\n    public Student(String name, int age, String studentId) {\n        this.name = name;\n        this.age = age;\n        this.studentId = studentId;\n    }\n    \n    \u002F\u002F 方法\n    public void study() {\n        System.out.println(name + \\\"正在学习...\\\");\n    }\n    \n    public void displayInfo() {\n        System.out.println(\\\"学生信息:\\\");\n        System.out.println(\\\"姓名: \\\" + name);\n        System.out.println(\\\"年龄: \\\" + age);\n        System.out.println(\\\"学号: \\\" + studentId);\n    }\n}\n\npublic class OOPDemo {\n    public static void main(String[] args) {\n        \u002F\u002F 创建对象\n        Student student1 = new Student(\\\"张三\\\", 20, \\\"2023001\\\");\n        Student student2 = new Student(\\\"李四\\\", 19, \\\"2023002\\\");\n        \n        \u002F\u002F 使用对象\n        student1.displayInfo();\n        student1.study();\n        \n        student2.displayInfo();\n        student2.study();\n    }\n}\n```\n\n### 7.2 封装\n\n```java\nclass BankAccount {\n    \u002F\u002F 私有字段（封装）\n    private String accountNumber;\n    private double balance;\n    private String owner;\n    \n    \u002F\u002F 构造方法\n    public BankAccount(String accountNumber, String owner, double initialBalance) {\n        this.accountNumber = accountNumber;\n        this.owner = owner;\n        this.balance = initialBalance;\n    }\n    \n    \u002F\u002F Getter 方法\n    public String getAccountNumber() {\n        return accountNumber;\n    }\n    \n    public double getBalance() {\n        return balance;\n    }\n    \n    public String getOwner() {\n        return owner;\n    }\n    \n    \u002F\u002F 存款方法\n    public void deposit(double amount) {\n        if (amount > 0) {\n            balance += amount;\n            System.out.println(\\\"存款成功! 当前余额: \\\" + balance);\n        } else {\n            System.out.println(\\\"存款金额必须大于0\\\");\n        }\n    }\n    \n    \u002F\u002F 取款方法\n    public void withdraw(double amount) {\n        if (amount > 0 && amount \u003C= balance) {\n            balance -= amount;\n            System.out.println(\\\"取款成功! 当前余额: \\\" + balance);\n        } else {\n            System.out.println(\\\"取款失败: 余额不足或金额无效\\\");\n        }\n    }\n}\n\npublic class EncapsulationDemo {\n    public static void main(String[] args) {\n        BankAccount account = new BankAccount(\\\"123456\\\", \\\"张三\\\", 1000);\n        \n        account.deposit(500);\n        account.withdraw(200);\n        account.withdraw(2000); \u002F\u002F 应该失败\n        \n        System.out.println(\\\"最终余额: \\\" + account.getBalance());\n    }\n}\n```\n\n### 7.3 继承\n\n```java\n\u002F\u002F 父类\nclass Animal {\n    String name;\n    int age;\n    \n    public Animal(String name, int age) {\n        this.name = name;\n        this.age = age;\n    }\n    \n    public void eat() {\n        System.out.println(name + \\\"正在吃东西\\\");\n    }\n    \n    public void sleep() {\n        System.out.println(name + \\\"正在睡觉\\\");\n    }\n}\n\n\u002F\u002F 子类\nclass Dog extends Animal {\n    String breed;\n    \n    public Dog(String name, int age, String breed) {\n        super(name, age); \u002F\u002F 调用父类构造方法\n        this.breed = breed;\n    }\n    \n    \u002F\u002F 方法重写\n    @Override\n    public void eat() {\n        System.out.println(name + \\\"正在吃狗粮\\\");\n    }\n    \n    \u002F\u002F 子类特有方法\n    public void bark() {\n        System.out.println(name + \\\"在汪汪叫\\\");\n    }\n}\n\npublic class InheritanceDemo {\n    public static void main(String[] args) {\n        Dog dog = new Dog(\\\"旺财\\\", 3, \\\"金毛\\\");\n        dog.eat();      \u002F\u002F 调用重写的方法\n        dog.sleep();    \u002F\u002F 调用继承的方法\n        dog.bark();     \u002F\u002F 调用子类特有方法\n    }\n}\n```\n\n### 7.4 多态\n\n```java\nclass Shape {\n    public void draw() {\n        System.out.println(\\\"绘制形状\\\");\n    }\n    \n    public double calculateArea() {\n        return 0;\n    }\n}\n\nclass Circle extends Shape {\n    double radius;\n    \n    public Circle(double radius) {\n        this.radius = radius;\n    }\n    \n    @Override\n    public void draw() {\n        System.out.println(\\\"绘制圆形，半径: \\\" + radius);\n    }\n    \n    @Override\n    public double calculateArea() {\n        return Math.PI * radius * radius;\n    }\n}\n\nclass Rectangle extends Shape {\n    double width, height;\n    \n    public Rectangle(double width, double height) {\n        this.width = width;\n        this.height = height;\n    }\n    \n    @Override\n    public void draw() {\n        System.out.println(\\\"绘制矩形，宽: \\\" + width + \\\", 高: \\\" + height);\n    }\n    \n    @Override\n    public double calculateArea() {\n        return width * height;\n    }\n}\n\npublic class PolymorphismDemo {\n    public static void main(String[] args) {\n        \u002F\u002F 多态示例\n        Shape[] shapes = new Shape[2];\n        shapes[0] = new Circle(5);\n        shapes[1] = new Rectangle(4, 6);\n        \n        for (Shape shape : shapes) {\n            shape.draw();\n            System.out.println(\\\"面积: \\\" + shape.calculateArea());\n            System.out.println(\\\"---------\\\");\n        }\n    }\n}\n```\n\n## 第八部分：Java 21 新特性\n\n### 8.1 记录类 (Records)\n\n```java\n\u002F\u002F 传统方式\nclass Person {\n    private final String name;\n    private final int age;\n    \n    public Person(String name, int age) {\n        this.name = name;\n        this.age = age;\n    }\n    \n    public String name() { return name; }\n    public int age() { return age; }\n    \n    @Override\n    public boolean equals(Object o) {\n        \u002F\u002F 复杂的equals实现...\n    }\n    \n    @Override\n    public int hashCode() {\n        \u002F\u002F 复杂的hashCode实现...\n    }\n    \n    @Override\n    public String toString() {\n        \u002F\u002F toString实现...\n    }\n}\n\n\u002F\u002F Java 16+ 记录类 (简化版)\nrecord PersonRecord(String name, int age) {\n    \u002F\u002F 自动生成构造方法、getter、equals、hashCode、toString\n}\n\npublic class RecordsDemo {\n    public static void main(String[] args) {\n        PersonRecord person = new PersonRecord(\\\"张三\\\", 25);\n        System.out.println(person.name());    \u002F\u002F 自动生成的getter\n        System.out.println(person.age());     \u002F\u002F 自动生成的getter\n        System.out.println(person);           \u002F\u002F 自动生成的toString\n    }\n}\n```\n\n### 8.2 文本块 (Text Blocks)\n\n```java\npublic class TextBlocks {\n    public static void main(String[] args) {\n        \u002F\u002F 传统字符串\n        String oldStyle = \\\"{\\\n\\\" +\n                         \\\"  \\\\\"name\\\\\": \\\\\"张三\\\\\",\\\n\\\" +\n                         \\\"  \\\\\"age\\\\\": 25\\\n\\\" +\n                         \\\"}\\\";\n        \n        \u002F\u002F Java 15+ 文本块\n        String json = \\\"\\\"\\\"\n                      {\n                        \\\"name\\\": \\\"张三\\\",\n                        \\\"age\\\": 25\n                      }\n                      \\\"\\\"\\\";\n        \n        String html = \\\"\\\"\\\"\n                      \u003Chtml>\n                          \u003Cbody>\n                              \u003Ch1>欢迎\u003C\u002Fh1>\n                          \u003C\u002Fbody>\n                      \u003C\u002Fhtml>\n                      \\\"\\\"\\\";\n        \n        System.out.println(json);\n        System.out.println(html);\n    }\n}\n```\n\n### 8.3 模式匹配\n\n```java\npublic class PatternMatching {\n    public static void main(String[] args) {\n        Object obj = \\\"Hello, Java 21!\\\";\n        \n        \u002F\u002F 传统的 instanceof\n        if (obj instanceof String) {\n            String str = (String) obj;\n            System.out.println(str.toUpperCase());\n        }\n        \n        \u002F\u002F Java 16+ 模式匹配 instanceof\n        if (obj instanceof String str) {\n            System.out.println(str.toUpperCase());\n        }\n        \n        \u002F\u002F switch 表达式 + 模式匹配\n        Object value = 42;\n        String result = switch (value) {\n            case Integer i -> \\\"整数: \\\" + i;\n            case String s -> \\\"字符串: \\\" + s;\n            case Double d -> \\\"浮点数: \\\" + d;\n            default -> \\\"未知类型\\\";\n        };\n        System.out.println(result);\n    }\n}\n```\n\n## 第九部分：异常处理\n\n```java\npublic class ExceptionHandling {\n    \n    public static int divide(int a, int b) {\n        return a \u002F b;\n    }\n    \n    public static void main(String[] args) {\n        \u002F\u002F 基本的异常处理\n        try {\n            int result = divide(10, 0);\n            System.out.println(\\\"结果: \\\" + result);\n        } catch (ArithmeticException e) {\n            System.out.println(\\\"发生算术异常: \\\" + e.getMessage());\n        } finally {\n            System.out.println(\\\"这是finally块，总是会执行\\\");\n        }\n        \n        \u002F\u002F 多个catch块\n        try {\n            int[] numbers = {1, 2, 3};\n            System.out.println(numbers[5]); \u002F\u002F 数组越界\n        } catch (ArrayIndexOutOfBoundsException e) {\n            System.out.println(\\\"数组索引越界: \\\" + e.getMessage());\n        } catch (Exception e) {\n            System.out.println(\\\"发生其他异常: \\\" + e.getMessage());\n        }\n        \n        \u002F\u002F 抛出异常\n        try {\n            checkAge(15);\n        } catch (IllegalArgumentException e) {\n            System.out.println(\\\"捕获异常: \\\" + e.getMessage());\n        }\n    }\n    \n    public static void checkAge(int age) {\n        if (age \u003C 18) {\n            throw new IllegalArgumentException(\\\"年龄必须大于等于18岁\\\");\n        }\n        System.out.println(\\\"年龄验证通过\\\");\n    }\n}\n```\n\n## 第十部分：集合框架\n\n```java\nimport java.util.*;\n\npublic class CollectionsDemo {\n    public static void main(String[] args) {\n        \u002F\u002F List - 有序集合\n        List\u003CString> list = new ArrayList\u003C>();\n        list.add(\\\"苹果\\\");\n        list.add(\\\"香蕉\\\");\n        list.add(\\\"橙子\\\");\n        System.out.println(\\\"List: \\\" + list);\n        \n        \u002F\u002F Set - 无序不重复集合\n        Set\u003CInteger> set = new HashSet\u003C>();\n        set.add(1);\n        set.add(2);\n        set.add(1); \u002F\u002F 重复元素，不会被添加\n        System.out.println(\\\"Set: \\\" + set);\n        \n        \u002F\u002F Map - 键值对\n        Map\u003CString, Integer> map = new HashMap\u003C>();\n        map.put(\\\"张三\\\", 25);\n        map.put(\\\"李四\\\", 30);\n        map.put(\\\"王五\\\", 28);\n        System.out.println(\\\"Map: \\\" + map);\n        System.out.println(\\\"张三的年龄: \\\" + map.get(\\\"张三\\\"));\n        \n        \u002F\u002F 遍历集合\n        System.out.println(\\\"\\\n遍历List:\\\");\n        for (String fruit : list) {\n            System.out.println(fruit);\n        }\n        \n        System.out.println(\\\"\\\n遍历Map:\\\");\n        for (Map.Entry\u003CString, Integer> entry : map.entrySet()) {\n            System.out.println(entry.getKey() + \\\": \\\" + entry.getValue());\n        }\n        \n        \u002F\u002F 使用 Stream API (Java 8+)\n        System.out.println(\\\"\\\n使用Stream:\\\");\n        list.stream()\n            .filter(fruit -> fruit.length() > 2)\n            .forEach(System.out::println);\n    }\n}\n```\n\n## 学习建议\n\n1. **多动手实践**：每个示例代码都要亲自编写和运行\n2. **理解概念**：不要死记硬背，要理解面向对象的思想\n3. **阅读文档**：学会查看 [Java 官方文档](https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002F)\n4. **做项目**：尝试编写小项目来巩固知识\n5. **参与社区**：加入编程社区，向他人学习和提问\n\n## 下一步学习方向\n\n- Java 高级特性（泛型、反射、注解）\n- Java 网络编程\n- 数据库连接（JDBC）\n- 多线程编程\n- Spring 框架\n- 微服务开发\n\n---\n\n**下载说明**：您可以将此文档保存为 `Java21-入门教程.md` 文件，方便随时查阅和学习。\n\n这个教程涵盖了 Java 21 的基础知识，适合零基础学习者。建议按照顺序学习每个部分，并完成相应的练习。祝你学习顺利！","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002Fab7fca936a4c2c439faaa731c55a8da4_1762759275356.png",895,"2025-11-10T07:21:19.000Z","2026-05-25T03:19:09.000Z",{"id":85,"name":86,"slug":86},"d10456a5-e649-4741-a38f-f07f266ce5f2","开发环境",{"id":22,"name":23,"avatar":10},[89],{"id":90,"name":91,"slug":91},"692d5d68-b188-4e5c-aca8-65d0229399a1","渐变",{"id":93,"title":94,"slug":95,"content":96,"excerpt":94,"coverImage":97,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":98,"publishedAt":99,"createdAt":99,"updatedAt":100,"category":101,"author":102,"tags":103},"9bd0f6b6-1b70-4b33-976b-26633d4842af","pm2 入门教程","pm2-入门教程","## 切换成root\n\n```language\nsudo -i\n```\n\n\n## 安装node命令(CentOS)\n\n```language\nsudo yum install nodejs npm\n```\n\n## 安装node命令(Ubuntu)\n\n```language\ncurl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_22.x | sudo -E bash -\nsudo apt install -y nodejs\n```\n\n\n\n\n## 安装pm2(CentOS)\n\n```language\nsudo npm install -g pm2\n```\n\n## 查看node安装目录\n\n```language\nwhich node\n```\n\n\n\n## 配置ecosystem.config.cjs文件\n\n```shell\nmodule.exports = {\n  apps: [\n    \u002F\u002F 测试环境\n    {\n      name: 'web-dev',\n      instances: 1,\n      exec_mode: 'fork', \u002F\u002F 改为 fork 模式而不是 cluster\n      cwd: '\u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5',\n      script: '.\u002Fserver\u002Findex.mjs',\n      interpreter: '\u002Fusr\u002Fbin\u002Fnode', \u002F\u002F 或者实际的 node 路径\n      log_date_format: 'YYYY-MM-DD HH:mm Z',\n      error_file: '.\u002Flogs\u002Ferr.log',\n      out_file: '.\u002Flogs\u002Fout.log',\n      log_file: '.\u002Flogs\u002Fcombined.outerr.log',\n      time: true,\n      autorestart: true,\n      max_restarts: 10,\n      min_uptime: '10s',\n      max_memory_restart: '2G',\n      watch: false,\n      ignore_watch: ['node_modules', 'logs', '.nuxt', '.output'],\n      env: {\n        PORT: 6999\n      }\n    },\n  ]\n}\n\n```\n# 服务器部署命令\n\n```language\nrm -rf \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5\u002F;\nmkdir \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5\u002F;\nmkdir \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5\u002Flogs\u002F;\ntar -zxvf \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5-bf\u002Fdist.tgz  -C \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5;\ncd \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5\npm2 delete quicking-web\n#PORT=6999 pm2 start server\u002Findex.mjs --name quicking-web\npm2 start \u002Fhome\u002Fec2-user\u002Fserver\u002Fquicking-h5\u002Fecosystem.config.cjs --only quicking-web\n```\n\n\n\n## pm2常用命令\n\n\n```language\n# 启动应用\npm2 start ecosystem.config.cjs\n\n# 启动应用并指定名称\npm2 start aecosystem.config.cjs --name my-app\n\n# 启动应用并监听文件变化（开发模式）\npm2 start ecosystem.config.cjs --watch\n\n# 启动应用并指定实例数（集群模式）\npm2 start ecosystem.config.cjs -i max\n\n# 从 package.json 启动\npm2 start npm --name \\\"my-app\\\" -- start\n\n# 列出所有应用\npm2 list\npm2 ls\npm2 status\n\n# 查看特定应用信息\npm2 show app-name\n\n# 监控所有应用\npm2 monit\n\n# 停止应用\npm2 stop app-name\npm2 stop all\n\n# 重启应用\npm2 restart app-name\npm2 restart all\n\n# 重载应用（零停机重启）\npm2 reload app-name\npm2 reload all\n\n# 删除应用\npm2 delete app-name\npm2 delete all\n\n\n# 查看所有应用日志\npm2 logs\n\n# 查看特定应用日志\npm2 logs app-name\n\n# 查看最后 N 行日志\npm2 logs --lines 200\n\n# 清空日志\npm2 flush\n\n# 以 JSON 格式输出日志\npm2 logs --json\n\n\n# 查看系统信息\npm2 show pm2\n\n# 生成启动脚本\npm2 startup\n\n# 保存当前进程列表\npm2 save\n\n# 恢复保存的进程列表\npm2 resurrect\n\n# 更新 PM2\npm2 update\n\n# 查看详细的应用信息\npm2 show app-name\n\n# 查看 PM2 自身日志\npm2 logs pm2\n\n# 检查 PM2 版本和状态\npm2 --version\npm2 info\n\n# 重置 PM2（清除所有应用）\npm2 kill\n```\n\n","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F20c0f16b5ae0af7383a13df4f51b8ba6_1761125154013.jpg",414,"2025-10-22T09:34:41.000Z","2026-05-24T20:01:49.000Z",{"id":85,"name":86,"slug":86},{"id":22,"name":23,"avatar":10},[104],{"id":105,"name":106,"slug":107},"b73007a8-bb5c-42a8-9fd9-163033a5b45d","Linux","linux",{"id":109,"title":110,"slug":110,"content":111,"excerpt":112,"coverImage":113,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":114,"publishedAt":115,"createdAt":115,"updatedAt":116,"category":117,"author":121,"tags":122},"2a43973c-c93c-46fa-b5dc-676a66efa3e6","danmu弹幕","## 官方文档\n> https:\u002F\u002Fimtaotao.github.io\u002Fdanmu\u002Fdocument\u002Fen\u002F\n\n## 官方示例\n> https:\u002F\u002Fimtaotao.github.io\u002Fdanmu\u002F\n\n\n```language\nimport { create } from 'danmu';\nimport { useEffect, useRef, useState } from 'react';\nimport ReactDOM from 'react-dom\u002Fclient';\nimport { getArgFromUrl, _T, useIsEn } from '@dana\u002Fcomponents';\nimport { getBullet } from 'api';\n\nconst BulletItem = ({\n  data,\n}: {\n  data: {\n    avatar1: string;\n    avatar2: string;\n    gift: string;\n  };\n}) => {\n  const isEn = useIsEn();\n\n  const bulletMap: Record\u003Cnumber, string> = {\n    1: _T('我对你的爱是永恒的。'),\n    2: _T('我想和你分享每一刻。'),\n    3: _T('你是我心中的珍宝。'),\n    4: _T('我的爱像海一样深。'),\n    5: _T('爱你是我生命的目的。'),\n    6: _T('你是我无法忘记的梦想。'),\n    7: _T('我们可以一起面对一切。'),\n    8: _T('你的笑容照亮了我的生活。'),\n    9: _T('我想永远和你一起旅行。'),\n  };\n\n  const bullet = bulletMap[Math.floor(Math.random() * 9) + 1];\n\n  return (\n    \u003Cdiv className=\\\"flex justify-between items-center\\\">\n      \u003Cdiv className=\\\"flex relative z-10 justify-center items-center\\\">\n        \u003Cdiv className=\\\"relative w-[52px] h-[52px] flex justify-center items-center\\\">\n          \u003Cdiv\n            className=\\\"w-[40px] h-[40px] bg-contain bg-center rounded-full\\\"\n            style={{\n              backgroundImage: `url(${data.avatar1})`,\n            }}\n          >\u003C\u002Fdiv>\n          \u003Cdiv className=\\\"bg-love-head w-[52px] h-[52px] bg-cover absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%]\\\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv className=\\\"mx-[-10px] w-[38px] h-[39px] flex justify-center items-center relative z-10\\\">\n          \u003Cdiv className=\\\"bg-mask w-[38px] h-[38px] bg-cover  absolute top-0 left-0\\\">\u003C\u002Fdiv>\n          \u003Cdiv\n            className=\\\"w-[30px] h-[30px] bg-cover\\\"\n            style={{\n              backgroundImage: `url(${data.gift})`,\n            }}\n          >\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv className=\\\"relative w-[52px] h-[52px] flex justify-center items-center\\\">\n          \u003Cdiv\n            className=\\\"w-[40px] h-[40px] bg-contain bg-center rounded-full\\\"\n            style={{\n              backgroundImage: `url(${data.avatar2})`,\n            }}\n          >\u003C\u002Fdiv>\n          \u003Cdiv className=\\\"bg-love-head w-[52px] h-[52px] bg-cover absolute left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%]\\\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cdiv\n        className={`flex  items-center w-[276px] h-[38px] bg-bullet-item bg-cover pl-[35px] ${isEn ? 'justify-start ml-[-30px]' : 'justify-end scale-x-[-1] mr-[-30px]'}`}\n        dir=\\\"\\\"\n      >\n        \u003Cdiv\n          className={`text-[#FDFF83]\n                text-[13px] font-bold [text-shadow:0px_2px_2px_rgba(0,0,0,0.60)] ${isEn ? 'text-right' : 'text-left scale-x-[-1]'} text-nowrap`}\n        >\n          {bullet}\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  );\n};\n\nconst Bullet = () => {\n  const screenRef = useRef\u003Cany>();\n  const isEn = useIsEn();\n  const [bulletData, setBulletData] = useState\u003Cany[]>([]);\n\n  const fetchData = async () => {\n    const res = await getBullet();\n    let bulletData = res;\n\n    setBulletData(bulletData);\n\n    bulletData.forEach((item, index) => {\n      screenRef.current.push(\n        {\n          avatar1: item.sendAvatar,\n          avatar2: item.receiveAvatar,\n          gift: item.giftSpic,\n        },\n        {\n          plugin: {\n            destroyed(danmaku: any) {\n              danmaku.loops = 0; \u002F\u002F\n              screenRef.current.push(danmaku);\n            },\n          },\n        },\n      );\n    });\n  };\n\n  const checkElementAndInit = () => {\n    const container = document.getElementById('bullet');\n    if (container) {\n      \u002F\u002F 元素存在，初始化弹幕\n      screenRef.current = create({\n        rate: 0.5,\n        trackHeight: 52,\n        mode: 'strict',\n        direction: isEn ? 'right' : 'left',\n        limits: {\n          stash: Infinity,\n          view: 20,\n        },\n        plugin: {\n          $beforeMove(danmaku) {\n            \u002F\u002F 设置循环\n            \u002F\u002F danmaku.setloop();\n          },\n          $createNode(danmaku) {\n            if (danmaku.node) {\n              ReactDOM.createRoot(danmaku.node).render(\n                \u003CBulletItem\n                  data={{\n                    avatar1: (danmaku.data as any).avatar1,\n                    avatar2: (danmaku.data as any).avatar2,\n                    gift: (danmaku.data as any).gift,\n                  }}\n                \u002F>,\n              );\n            }\n          },\n        },\n      });\n\n      \u002F\u002F 挂载并开始渲染\n      screenRef.current.mount(container);\n      screenRef.current.startPlaying();\n      fetchData();\n    } else {\n      \u002F\u002F 元素不存在，继续轮询\n      setTimeout(checkElementAndInit, 100); \u002F\u002F 每100ms检查一次\n    }\n  };\n\n  useEffect(() => {\n    \u002F\u002F 给页面中某个元素初始化弹幕屏幕，一般为一个大区块。此处的配置项全局生效\n    screenRef.current = create({\n      rate: 0.5,\n      trackHeight: 52,\n      direction: isEn ? 'right' : 'left',\n      mode: 'strict',\n      \u002F\u002F durationRange: [5000, 5000],\n      limits: {\n        stash: Infinity,\n        view: 20,\n      },\n      plugin: {\n        $beforeMove(danmaku) {\n          \u002F\u002F 设置循环\n          \u002F\u002F danmaku.setloop();\n        },\n        $createNode(danmaku) {\n          if (danmaku.node) {\n            ReactDOM.createRoot(danmaku.node).render(\n              \u003CBulletItem\n                data={{\n                  avatar1: (danmaku.data as any).avatar1,\n                  avatar2: (danmaku.data as any).avatar2,\n                  gift: (danmaku.data as any).gift,\n                }}\n              \u002F>,\n            );\n          }\n        },\n      },\n    });\n\n    \u002F\u002F -----------------------------\n    setTimeout(checkElementAndInit);\n  }, []);\n\n  return (\n    \u003Cdiv>\n      \u003Cdiv className=\\\"relative pt-[28px] w-[375px] h-[507px] bg-cover bg-bullet-bg\\\">\n        \u003Cdiv className=\\\"text-primary\\\">{_T('CP世界')}\u003C\u002Fdiv>\n        \u003Cdiv className=\\\"w-[265px] h-[48px] bg-cover bg-bullet-title flex justify-center items-center mx-auto mt-[20px]\\\">\n          \u003Cdiv className=\\\"text-[#fff] text-[12px] font-normal text-center px-[10px] leading-tight\\\">\n            {_T('发送单个CP礼物>=5000金币到你的CP将显示')}\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv className=\\\"mx-auto w-[350px] h-[320px] pt-[20px]\\\">\n          \u003Cdiv id=\\\"bullet\\\" className=\\\"w-[350px] h-[320px]\\\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  );\n};\n\nexport default Bullet;\n\n```\n","强大且用户友好的弹幕解决方案","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002Fbea159c4e2228d4f44013ec3ad8cac4e_1756211380682.png",489,"2025-08-26T12:29:56.000Z","2026-05-24T20:02:20.000Z",{"id":118,"name":119,"slug":120},"f1701085-b8c1-413a-8750-58e7a0a33832","React","react",{"id":22,"name":23,"avatar":10},[123],{"id":124,"name":125,"slug":126},"399d1d38-cc0d-43ce-8baf-c769447a2ebd","React生态","react生态",{"id":128,"title":129,"slug":129,"content":130,"excerpt":129,"coverImage":131,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":132,"publishedAt":133,"createdAt":133,"updatedAt":134,"category":135,"author":139,"tags":140},"8c77a787-1db3-424c-89c2-7c7db65a7bca","阿里图标动态化导入","## 动态请求接口，获取CDN地址\n```language\n    \u002F\u002F 从CDN链接提取font ID的函数\n    const extractFontId = (url: string): string => {\n      const match = url.match(\u002Ffont_(\\d+)_\u002F);\n      return match ? match[1] : ''; \u002F\u002F 默认值\n    };\n\n    \u002F\u002F 初始化iconfont\n    async function initIconfont(): Promise\u003Cvoid> {\n      try {\n        \u002F\u002F 从API获取CDN链接\n        const res: any = await getAttrConfigDetail('iconfont_url');\n\n        const url = res.data?.value;\n\n        console.log('url', url);\n\n        if (!url) {\n          return;\n        }\n\n        \u002F\u002F 根据CDN链接动态更新iconfontGlobalName\n        const fontId = extractFontId(url);\n        iconfontGlobalName.value = `_iconfont_svg_string_${fontId}`;\n\n        console.log('fontId', fontId);\n\n        \u002F\u002F 动态创建script标签加载iconfont\n        const script = document.createElement('script');\n        script.src = url;\n        script.async = true;\n\n        \u002F\u002F 设置script标签id\n        script.id = 'iconfont';\n\n        \u002F\u002F 监听加载完成事件\n        script.onload = () => {\n          \u002F\u002F 获取图标名称列表\n          iconfontIcons.value = getIconfontNames();\n          console.log('iconfontIcons', iconfontIcons.value);\n        };\n\n        script.onerror = () => {\n          console.error('Failed to load iconfont:', url);\n        };\n\n        \u002F\u002F 添加到head标签;\n        document.head.appendChild(script);\n      } catch (error) {\n        console.error('Error initializing iconfont:', error);\n      }\n    }\n\n    \u002F\u002F 获取iconfont图标名称列表\n    const getIconfontNames = (): string[] => {\n      const globalName = iconfontGlobalName.value as keyof Window;\n      const text = window[globalName] || '';\n      return text.match(\u002F(?\u003C=id=\\\")[^\\\"]+(?=\\\")\u002Fg) || [];\n    };\n```\n\n\n\n## 渲染组件\n```language\nimport SvgIcon from '@\u002Fcomponents\u002FSvgIcon\u002Findex.vue';\nimport { ElIcon } from 'element-plus';\nimport { h, type Component, type PropType } from 'vue';\n\nexport default defineComponent({\n  name: 'BaseIcon',\n  props: {\n    name: {\n      type: [String, Object] as PropType\u003Cstring | Component | unknown>\n    },\n    size: {\n      type: [String, Number]\n    },\n    color: {\n      type: String\n    }\n  },\n  setup(props) {\n    return () => {\n      return h(\n        ElIcon,\n        {\n          size: props.size,\n          color: props.color\n        },\n        () => {\n          if (!props.name) {\n            return;\n          }\n          if (typeof props.name === 'string') {\n            if (props.name.startsWith('svg-')) {\n              return h(SvgIcon, {\n                name: props.name.slice(4)\n              });\n            }\n            if (props.name.startsWith('icon-')) {\n              return h(\n                'svg',\n                {\n                  ariaHidden: true\n                },\n                h('use', {\n                  'xlink:href': `#${props.name}`\n                })\n              );\n            }\n            return h(resolveComponent(props.name));\n          }\n          return h(props.name);\n        }\n      );\n    };\n  }\n});\n\n```\n","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002Ff375b202fdd16c85c3361027f467b095_1754476720058.png",431,"2025-08-06T10:40:42.000Z","2026-05-24T20:02:50.000Z",{"id":136,"name":137,"slug":138},"5ed5cc62-43ea-49a2-b0b2-38bc7aae52a0","Vue3","vue3",{"id":22,"name":23,"avatar":10},[141],{"id":142,"name":143,"slug":144},"20bff9cd-7848-4c16-8775-42cf12b44b30","Vue3生态","vue3生态",{"id":146,"title":147,"slug":148,"content":149,"excerpt":150,"coverImage":151,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":152,"publishedAt":153,"createdAt":153,"updatedAt":154,"category":155,"author":156,"tags":157},"0c17ce84-1fb0-46d1-a6e7-35c8d6b90228","App中内嵌H5页面实现通信","app中内嵌h5页面实现通信","> 在App中内嵌H5页面并实现通信是常见的混合开发场景，主要通过JavaScript与原生代码交互实现。以下是主流平台的核心实现方案：\n\n\n## 一、通信原理\n### H5 → App\n> H5调用原生能力（如相机、存储等）\n\n### App → H5\n> 原生触发H5页面更新（如传递用户信息）\n\n### 双向通信\n> 通过约定协议实现数据交换\n\n\n## 二、Android实现方案\n### 方法1：JavaScriptInterface（官方推荐）\n\n```language\n\u002F\u002F 1. 定义接口类\npublic class JsBridge {\n    @JavascriptInterface\n    public void showToast(String msg) {\n        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();\n    }\n}\n\n\u002F\u002F 2. 注册到WebView\nwebView.getSettings().setJavaScriptEnabled(true);\nwebView.addJavascriptInterface(new JsBridge(), \\\"AndroidBridge\\\");\n\n\u002F\u002F 3. H5调用\nwindow.AndroidBridge.showToast(\\\"Hello from H5!\\\");\n```\n\n### 方法2：URL Scheme拦截\n\n```language\n\u002F\u002F H5触发自定义协议\nlocation.href = \\\"myapp:\u002F\u002Faction?param1=value1\\\";\n```\n\n```language\n\u002F\u002F Android拦截请求\nwebView.setWebViewClient(new WebViewClient() {\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, String url) {\n        if (url.startsWith(\\\"myapp:\u002F\u002F\\\")) {\n            \u002F\u002F 解析并执行原生操作\n            return true; \u002F\u002F 拦截请求\n        }\n        return false;\n    }\n});\n```\n\n## 三、iOS实现方案\n### 方法1：WKScriptMessageHandler（推荐）\n\n```language\n\u002F\u002F 1. 注册消息处理器\nlet contentController = WKUserContentController()\ncontentController.add(self, name: \\\"iosBridge\\\")\n\n\u002F\u002F 2. H5发送消息\nwindow.webkit.messageHandlers.iosBridge.postMessage({data: \\\"Hello\\\"})\n\n\u002F\u002F 3. 原生处理消息\nfunc userContentController(_ controller: WKUserContentController, \n                          didReceive message: WKScriptMessage) {\n    if message.name == \\\"iosBridge\\\", \n       let data = message.body as? [String: Any] {\n        print(\\\"Received:\\\", data[\\\"data\\\"] ?? \\\"\\\")\n    }\n}\n```\n\n### 方法2：JavaScriptCore（UIWebView旧方案）\n\n```language\n\u002F\u002F 1. 获取JSContext\nJSContext *context = [webView valueForKeyPath:@\\\"documentView.webView.mainFrame.javaScriptContext\\\"];\n\n\u002F\u002F 2. 注入对象\ncontext[@\\\"nativeBridge\\\"] = ^(NSString *msg) {\n    NSLog(@\\\"H5消息: %@\\\", msg);\n};\n\n\u002F\u002F H5调用\nnativeBridge(\\\"Hello iOS!\\\");\n```\n\n## 四、通用增强方案\n### 1. 封装JS Bridge库\n\n```language\n\u002F\u002F 统一调用入口\nfunction callNative(method, params, callback) {\n    \u002F\u002F Android\n    if (window.AndroidBridge) {\n        window.AndroidBridge[method](JSON.stringify(params));\n    } \n    \u002F\u002F iOS\n    else if (window.webkit?.messageHandlers?.iosBridge) {\n        window.webkit.messageHandlers.iosBridge.postMessage({\n            func: method,\n            data: params\n        });\n    }\n}\n```\n### 2. 安全加固\n\n```language\n通信双方验证来源（防止恶意页面调用）\n\niOS启用requiresUserActionForMediaPlayback\n\nAndroid禁用setAllowUniversalAccessFromFileURLs\n```\n\n## 五、完整通信流程示例\n![](https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F7dea58c61e36f6cc5a088c261e46f8bd_1749700455493.png)\n\n## 六、调试技巧\n### Android\n> 使用Chrome chrome:\u002F\u002Finspect 调试WebView\n\n### iOS\n> Safari开发者工具调试WebView\n\n### 抓包工具\n> 使用Charles分析通信数据\n\n\n### 注意事项\n> 异步处理：所有原生操作需异步返回结果\n\n> 版本兼容：Android 4.2+ 必须使用@JavascriptInterface\n\n> 参数类型：复杂数据使用JSON序列化\n\n> 内存泄漏：iOS的WKScriptMessageHandler需及时移除\n\n### 建议使用成熟开源库简化开发：\n\n> Android：JockeyJS\n\n> iOS：WebViewJavascriptBridge\n\n> 通过标准化通信协议，可实现H5与App的高效、安全交互，同时保持H5的动态更新能力。\n\n\n## H5 端通用实现（JockeyJS + WebViewJavascriptBridge）\n### 1. 初始化 Bridge\n\n```language\n\u002F\u002F 兼容 Android 和 iOS 的初始化函数\nfunction initBridge(callback) {\n    if (window.WebViewJavascriptBridge) {\n        return callback(window.WebViewJavascriptBridge);\n    }\n    if (window.Jockey) { \u002F\u002F Android 的 JockeyJS\n        callback({ \n            callHandler: Jockey.trigger,\n            registerHandler: Jockey.on\n        });\n    } else { \u002F\u002F iOS 的 WebViewJavascriptBridge\n        document.addEventListener('WebViewJavascriptBridgeReady', () => {\n            callback(window.WebViewJavascriptBridge);\n        }, false);\n    }\n}\n\ninitBridge(function(bridge) {\n    \u002F\u002F 注册 JS 处理器供原生调用\n    bridge.registerHandler(\\\"changeTheme\\\", function(data, responseCallback) {\n        document.body.className = data.theme;\n        responseCallback(\\\"Theme applied!\\\"); \u002F\u002F 通知原生完成\n    });\n\n    \u002F\u002F 调用原生方法（如获取用户信息）\n    bridge.callHandler(\\\"getUserInfo\\\", { userId: \\\"1001\\\" }, function(response) {\n        console.log(\\\"用户数据:\\\", response.name, response.email);\n    });\n});\n```\n\n## 封装方法参考\n\n```language\n\u002F\u002F ios终端\nconst u = navigator.userAgent;\nexport const isWeiXin = !!\u002FMicroMessenger\u002Fi.test(u);\nexport const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; \u002F\u002F android终端\nexport const isiOS = !!u.match(\u002F\\(i[^;]+;( U;)? CPU.+Mac OS X\u002F);\n\u002F\u002F import VConsole from 'vconsole'\n\u002F\u002F new VConsole()\n\nconst printException = (func: () => void) => {\n  try {\n    func();\n    return true;\n  } catch (e) {\n    console.error('非客户端环境，无法调用');\n    return false;\n  }\n};\n\n\u002F**\n * @function 客户端回调传参\n * @androidFuncName 安卓方法\n * @iosFuncName 苹果方法\n * @iosCallbackName 苹果回调\n * *\u002F\nconst waitAppCallback = (androidFuncName: string, iosFuncName: string, iosCallbackName: string) => {\n  return new Promise((resolve, reject) => {\n    try {\n      if (isAndroid) {\n        (window as any).Android[androidFuncName] &&\n          resolve((window as any).Android[androidFuncName]());\n      } else if (isiOS) {\n        (window as any)[iosCallbackName] = (appArgs: string) => {\n          resolve(appArgs);\n        };\n        (window as any).webkit &&\n          (window as any).webkit.messageHandlers[iosFuncName].postMessage('');\n      } else {\n        reject();\n      }\n    } catch (e) {\n      console.error('非客户端环境，无法调用', `这是${androidFuncName}方法`);\n      reject(e);\n    }\n  });\n};\n```\n\n\n## 2. 主动发送消息给原生\n\n```language\n\u002F\u002F H5 触发扫码功能\ndocument.getElementById(\\\"scan-btn\\\").addEventListener(\\\"click\\\", () => {\n    bridge.callHandler(\\\"scanQRCode\\\", \\\"qrcode\\\", (result) => {\n        alert(`扫码结果: ${result.code}`);\n    });\n});\n```\n\n## 移动端调试神器eruda使用详解(推荐)\n\n```language\npnpm add eruda\n```\n\n\n## 移动端调试神器vConsole使用详解\n\n```language\npnpm add vconsole\n```\n\n\n\n\n## H5在ios浏览器全屏显示\n> viewport-fit=cover 可以全屏显示\n\n```language\n\u003Cmeta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover\\\">\n```\n\n\n## 禁用ios 禁止网页回弹效果\n\n```language\nhtml {\n  overscroll-behavior: none;\n}\n```\n\n\n","在App中内嵌H5页面并实现通信是常见的混合开发场景，主要通过JavaScript与原生代码交互实现。以下是主流平台的核心实现方案：","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002Ff35e50644ba29b4972e502faa6a72254_1749699171524.png",757,"2025-06-12T04:13:30.000Z","2026-05-25T08:19:31.000Z",{"id":136,"name":137,"slug":138},{"id":22,"name":23,"avatar":10},[158],{"id":142,"name":143,"slug":144},{"id":160,"title":161,"slug":162,"content":163,"excerpt":164,"coverImage":165,"status":12,"isPinned":31,"pinnedAt":10,"viewCount":166,"publishedAt":167,"createdAt":167,"updatedAt":168,"category":169,"author":173,"tags":174},"354600df-6e7c-4735-b928-9f5692f2c4f6","JavaScript 中的class写法","javascript-中的class写法","\n## 1. 基本写法\n\n```js\nclass Person {\n    constructor(name, age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    sayHello() {\n        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);\n    }\n}\n\n```\n\n## 创建实例\n\n```js\nconst person = new Person('John', 30);\nperson.sayHello(); \u002F\u002F 输出: Hello, my name is John and I am 30 years old.\n```\n\n> constructor 方法：是类的构造函数，用于初始化类的实例。它会在创建对象时自动调用。\n实例方法：在类中定义的普通方法，可以通过实例调用。\n## 2. 类的继承\n> JavaScript 的类支持继承，使用 extends 关键字可以实现子类继承父类。\n\n```js\nclass Animal {\n    constructor(name) {\n        this.name = name;\n    }\n\n    speak() {\n        console.log(`${this.name} makes a noise.`);\n    }\n}\n\nclass Dog extends Animal {\n    constructor(name, breed) {\n        super(name); \u002F\u002F 调用父类的构造函数\n        this.breed = breed;\n    }\n\n    speak() {\n        console.log(`${this.name} barks.`);\n    }\n}\n\nconst dog = new Dog('Buddy', 'Golden Retriever');\ndog.speak(); \u002F\u002F 输出: Buddy barks.\n```\n\n> extends 关键字：用于声明子类继承父类。\nsuper() 方法：在子类的构造函数中调用父类的构造函数，用于初始化继承的属性。\n## 3. 静态方法\n>静态方法是属于类本身的方法，而不是类的实例。静态方法可以通过类名直接调用，而不需要创建实例。\n\n\n```js\nclass MathUtils {\n    static add(a, b) {\n        return a + b;\n    }\n\n    static multiply(a, b) {\n        return a * b;\n    }\n}\n\nconsole.log(MathUtils.add(5, 3)); \u002F\u002F 输出: 8\nconsole.log(MathUtils.multiply(5, 3)); \u002F\u002F 输出: 15\n```\n\n## 4. Getter 和 Setter\n> 类可以使用 get 和 set 关键字来定义属性的访问器方法，用于封装属性的读取和设置逻辑。\n\n```js\nclass User {\n    constructor(name) {\n        this._name = name;\n    }\n\n    get name() {\n        return this._name.toUpperCase();\n    }\n\n    set name(value) {\n        this._name = value.trim();\n    }\n}\n\nconst user = new User(' John Doe ');\nconsole.log(user.name); \u002F\u002F 输出: JOHN DOE\nuser.name = 'Jane Smith ';\nconsole.log(user.name); \u002F\u002F 输出: JANE SMITH\n```\n\n> get 方法：定义了如何读取属性的值。\nset 方法：定义了如何设置属性的值。\n## 5. 静态属性和实例属性\n> 实例属性：通过 this 定义在构造函数中，每个实例都有自己的副本。\n静态属性：通过类名直接访问，属于类本身，而不是实例。\n\n\n```language\nclass MyClass {\n    constructor(value) {\n        this.instanceProperty = value; \u002F\u002F 实例属性\n    }\n\n    static staticProperty = 'Static Value'; \u002F\u002F 静态属性\n}\n\nconst instance = new MyClass('Instance Value');\nconsole.log(instance.instanceProperty); \u002F\u002F 输出: Instance Value\nconsole.log(MyClass.staticProperty); \u002F\u002F 输出: Static Value\n```\n\n## 6. 私有属性和方法（ES2020+）\n> 私有属性和方法只能在类的内部访问，不能通过类的实例或子类直接访问。\n\n\n```js\nclass Counter {\n    #count = 0; \u002F\u002F 私有属性\n\n    increment() {\n        this.#count++;\n        console.log(`Count is now ${this.#count}`);\n    }\n\n    #reset() { \u002F\u002F 私有方法\n        this.#count = 0;\n        console.log('Count has been reset.');\n    }\n}\n\nconst counter = new Counter();\ncounter.increment(); \u002F\u002F 输出: Count is now 1\ncounter.increment(); \u002F\u002F 输出: Count is now 2\n\u002F\u002F counter.#reset(); \u002F\u002F 错误：私有方法无法从外部访问\n```\n\n## 总结\n> class 是一种语法糖，底层仍然是基于原型链实现的。\n类的写法更加直观，适合面向对象编程。\n使用 class 可以实现继承、封装、多态等面向对象的特性。","在 JavaScript 中，class 是一种基于原型的面向对象编程语法，用于创建对象的构造函数和定义对象的属性与方法。ES6 引入了 class 语法，使得类的定义更加简洁和直观。以下是关于 class 的基本写法和一些常见用法的介绍。","https:\u002F\u002Fcdn.xiaolong0418.com\u002Fmyblog\u002Fimages\u002F42606f265746d42a5b80c653a2868095_1740048554225.png",237,"2025-02-20T11:07:35.000Z","2026-05-24T03:38:14.000Z",{"id":170,"name":171,"slug":172},"da130ba9-d4f4-49f3-aa0f-149078097ef0","JavaScript","javascript",{"id":22,"name":23,"avatar":10},[175],{"id":176,"name":177,"slug":178},"994cc226-578b-4a72-a57e-a47a63d2793e","JavaScript生态","javascript生态",103,1,10,[183,189,195,197,202,207,208,212,214,219,224,229],{"id":184,"name":185,"slug":186,"createdAt":187,"updatedAt":188},"076bd8b9-293e-45cb-9dc3-e162007ca474","Axios","axios","2022-06-05T07:41:56.000Z","2025-12-30T07:26:21.000Z",{"id":190,"name":191,"slug":192,"createdAt":193,"updatedAt":194},"2aa7f6d0-1fac-4ed1-b9bb-f3afc813f42c","Axure","axure","2022-06-21T03:35:15.000Z","2023-02-08T02:49:14.000Z",{"id":74,"name":69,"slug":70,"createdAt":196,"updatedAt":194},"2022-05-21T09:59:55.000Z",{"id":198,"name":199,"slug":200,"createdAt":201,"updatedAt":194},"78a62bff-ff77-4878-8c25-3e6aae18c668","Docker","docker","2022-07-16T14:34:37.000Z",{"id":203,"name":204,"slug":205,"createdAt":206,"updatedAt":194},"2de16806-ef3f-4e54-a259-d1e1e182468c","Git","git","2022-07-16T14:25:15.000Z",{"id":176,"name":177,"slug":178,"createdAt":196,"updatedAt":194},{"id":209,"name":210,"slug":211,"createdAt":196,"updatedAt":194},"5086e93c-23b9-43d3-9643-cc87f0e9ee94","JenKins","jenkins",{"id":105,"name":106,"slug":107,"createdAt":213,"updatedAt":194},"2022-07-16T14:40:17.000Z",{"id":215,"name":216,"slug":217,"createdAt":218,"updatedAt":194},"0b658b92-dd6b-4db3-a398-9f6d69950a02","Markdown","markdown","2022-07-16T14:39:25.000Z",{"id":220,"name":221,"slug":222,"createdAt":223,"updatedAt":194},"ab034d3a-6e5b-4db5-a2dc-faf4ccbb63f5","Nest","nest","2022-07-16T13:15:49.000Z",{"id":225,"name":226,"slug":227,"createdAt":228,"updatedAt":194},"52c41978-da06-4962-9636-45bbaeedda80","Nginx","nginx","2022-05-21T09:59:56.000Z",{"id":230,"name":231,"slug":231,"createdAt":228,"updatedAt":194},"0f1cc678-40e4-44b1-b2cf-a6fd8a1c867a","npm",[],1782463005784]