知行道 - 知行道
type: channel
link: https://blog.frunoob.cn/author/1/
description: 知行道
items: Arrays.asList()的陷阱
items: 理解KMP算法
items: Nginx allow/deny 不生效？可能是 return 指令的坑
items: chrome禁用更新
items: claude code快速上手
items: Android 应用分身|双开|隐私空间 下用户证书不生效
items: JVM 调优实践
items: linux宝塔面板彻底清理
items: docker实操指南
items: Windows 远程桌面连接（RDP）要求的函数不受支持| CredSSP 加密 Oracle 修正

Arrays.asList()的陷阱
type: item
date: 2026-06-24T10:49:08+08:00
description: 1. 是什么（What）—— 直击本质Arrays.asList() 的本质陷阱：它期望的是泛型可变参数 T...，而基本类型数组 int[] 会被当成单个对象元素，导致 List<int[]> 而非 List<Integer>。类比：你本人校招面试，HR说"带简历来"，结果你拿了一个"文件夹"（数组整体）而不是"一张张简历"（数组展开）。直接上代码 List<Integer> list = Arrays.asList(new int[]{1,2,3,5,2}); // 会报错，因为Arrays.asList将`int[]`识别为单个对象，所以泛型不应该是`Integer`，而是`int[]`。 // 当然也可以直接写成 List<Integer> list = Arrays.asList(new Integer[]{1,2,3,5,2}); // 但这样便掉入了另一个陷阱，`Arrays.asList()` 返回的List不能增删！这个在进阶拓展里有详细说明 2. 怎么做（How）—— 代码与场景落地复现// ❌ 编译报错：不兼容的类型，无法将 int[] 转为 Integer[] List<Integer> list = Arrays.asList(new int[]{1, 2, 3, 5, 2}); // 错误信息（Java 8+）： // 错误: 无法将引用 List<int[]> 转换为 List<Integer> // List<Integer> list = Arrays.asList(new int[]{1,2,3,5,2}); // ^ // 需要: List<Integer> // 找到: List<int[]>四种正确转换方式（从面试到生产）int[] arr = {1, 2, 3, 5, 2}; // ========== 方案1：手动装箱（最原始，面试应急）========== List<Integer> list1 = new ArrayList<>(); for (int val : arr) { list1.add(val); // 自动装箱 int → Integer } // ========== 方案2：Stream（Java 8+，代码简洁，性能一般）========== List<Integer> list2 = Arrays.stream(arr) // IntStream .boxed() // Stream<Integer> .collect(Collectors.toList()); // 可变List // 不可变版本（更高效，内存友好） List<Integer> list2Immutable = Arrays.stream(arr) .boxed() .toList(); // Java 16+，返回ImmutableList // ========== 方案3：Apache Commons Lang（项目依赖可用）========== // List<Integer> list3 = org.apache.commons.lang3.ArrayUtils.toObject(arr); // ========== 方案4：Guava（Google出品，推荐）========== // List<Integer> list4 = com.google.common.primitives.Ints.asList(arr); // 返回的是 ImmutableList<Integer>，底层用 int[] 存储，省空间！ // ⚠️ 注意：Ints.asList() 返回的List不支持add/remove，set可行对比表：哪种场景用什么场景推荐方案原因算法题/面试手写方案1（手动）或方案2（Stream）无依赖，最安全大数据量（>10^6）方案4 Guava Ints.asList()避免 boxing 对象爆炸，内存省4倍频繁修改元素方案2 Collectors.toList()返回 ArrayList，支持set只读遍历方案2 .toList() Java 16+Immutable，线程安全提示3. 为什么（Why）—— 源码与底层原理3.1 为什么 int[] 会被当成单个元素？// JDK源码：Arrays.asList 的签名 public static <T> List<T> asList(T... a) { return new ArrayList<>(a); // 内部类 java.util.Arrays.ArrayList } // 泛型的本质：编译期擦除，运行时没有 T 的区别 // T... 在运行时实际上是 Object[]关键洞察：泛型 T 的上限…

理解KMP算法
type: item
date: 2026-06-17T20:59:00+08:00
description: 是什么？有道经典算法题：给你一个文本串 T ，一个非空模板串 S ，问 S 在 T 中出现了多少次 数据范围：1≤𝑙𝑒𝑛(𝑆)≤500000,1≤𝑙𝑒𝑛(𝑇)≤1000000 要求：空间复杂度 𝑂(𝑙𝑒𝑛(𝑆))，时间复杂度 𝑂(𝑙𝑒𝑛(𝑆)+𝑙𝑒𝑛(𝑇))我第一次写的答案import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可 * * 计算模板串S在文本串T中出现了多少次 * @param S string字符串 模板串 * @param T string字符串 文本串 * @return int整型 */ public int kmp (String S, String T) { int n = 0; int l = (T.length() - S.length()) + 1; for (int i = 0; i < l; i++) { if (T.startsWith(S,i)) { n++; } } return n; } }运行发现时间复杂度太大，速度超过了2s。这是标准答案public int kmp(String S, String T) { if (S.length() > T.length()) return 0; // ========== 第一步：预处理模式串 ========== int[] next = new int[S.length()]; // i: 当前字符位置，j: 已匹配的前缀长度（也是下一个要比的位置） for (int i = 1, j = 0; i < S.length(); i++) { while (j > 0 && S.charAt(i) != S.charAt(j)) { j = next[j - 1]; // 失配：回退到次优候选 } if (S.charAt(i) == S.charAt(j)) { j++; // 匹配：扩展前缀 } next[i] = j; // 记录：S[0..i]的最长公共前后缀长度 } // ========== 第二步：KMP匹配 ========== int count = 0; for (int i = 0, j = 0; i < T.length(); i++) { while (j > 0 && T.charAt(i) != S.charAt(j)) { j = next[j - 1]; // 失配：S滑动，T不动 } if (T.charAt(i) == S.charAt(j)) { j++; // 匹配：双双前进 } if (j == S.length()) { count++; j = next[j - 1]; // 成功：保留重叠，继续搜索 } } return count; }[scode type="yellow"]光知道代码没用，得理解才行，为什么要这样写。[/scode]怎么做理解next数组的物理意义next[i] = 模式串S[0..i]中，最长相等"真前缀"与"真后缀"的长度术语定义例子（S="ababab"）真前缀不包含自身的所有前缀"a", "ab", "aba", "abab", "ababa"真后缀不包含自身的所有后缀"b", "ab", "bab", "abab", "babab"最长相等对同一个字符串既是前缀又是后缀"abab"（长度4）理解next数组的“递推”核心思想：已知next[i-1]，如何求next[i]？模式串: a b a b a b c 索引: 0 1 2 3 4 5 6 已知: next[4] = 3 （"ababa"的最长公共前后缀是"aba"） 求: next[5] = ? （"ababab"的最长公共前后缀）其实这里我还是没理解递推的含义，所以有了下面这个图：S[0..4] = "ababa" 想扩展成 S[0..5] = "ababab" ↑ ↑ 前缀"aba" (长度3) 期望: 前缀"abab" (长度4) 后缀"aba" (长度3) 期望: 后缀"abab" (长度4) 关键观察：如果 S[3] == S[5]，就能直接扩展！ （"aba"的下一个字符） （新字符） 即：如果 S[next[4]] == S[5]，则 next[5] = next[4] + 1 = 4这里我终于懂了，如果当前已知的最大公共前后缀的下一位和最后一位相等，那么就产生了一个新的更长的公共前后缀。失配时的“回退”如果不相等怎么办？S = "abacabad" 求 next[6]，已知 next[5]=3（"abacab"的"abab"? 不对，重新算...） 让我重新来：S="ab…

Nginx allow/deny 不生效？可能是 return 指令的坑
type: item
date: 2026-05-31T04:37:00+08:00
description: 问题现象frunoob.cn 配置了 IP 白名单，但任何 IP 都能访问，限制完全没生效。原因分析Nginx 处理请求分多个阶段：rewrite → access → content → ...allow/deny 在 access 阶段执行return 在 rewrite 阶段执行rewrite 阶段先于 access 阶段！所以 return 200 直接返回响应，allow/deny 根本没机会执行。对比验证ai.frunoob.cn（生效）使用 proxy_pass，无 return，正常走 access 阶段。frunoob.cn（不生效）使用 return 200，rewrite 阶段直接返回，跳过 access。解决方案去掉 return，改用 root + index 或 proxy_pass。关键结论指令执行阶段影响 allow/denyreturnrewrite会跳过rewriterewrite会跳过proxy_passcontent不会跳过root/indexcontent不会跳过使用 return 或 rewrite 时，allow/deny 会失效！

chrome禁用更新
type: item
date: 2026-05-30T20:53:00+08:00
description: [scode type="yellow"]以下方法只能保证不会更新，并不提示更新消息，但不保证点击右上角不闪退[/scode][scode type="red"]我发现闪退往往出现在第二次点击右上角【Customise and control Google Chrome】[/scode]是什么因为最新版本限制第三方抓取cookie，所以必须保持在旧版本，但是就算停止了服务，每次打开右上角都会出现finish update，导致无法进入到设置，一点右上角三个点就自动关闭，就很烦。怎么做步骤 1：禁用更新服务按 Win + R，输入 services.msc，回车找到 Google Update Service (gupdate) 和 Google Update Service (gupdatem)分别双击，将启动类型改为 禁用，然后点击 停止，确定[scode type="yellow"]我直接把所有带Google的都禁用了，计划任务也删了[/scode]步骤2：修改快捷方式启动参数这种方法只关闭右上角的更新提示，不会阻止 Chrome 自动更新，但仅在你通过修改后的快捷方式启动时生效。在桌面找到 Google Chrome 图标，右键 → 属性切换到 快捷方式 标签页在 目标 一栏的末尾（引号外面）加一个空格，然后粘贴：--disable-background-networking点击 确定 保存重启 Chrome 即可[scode type="blue"]注意：如果 Chrome 固定在任务栏，需要先从任务栏取消固定，修改桌面快捷方式后，重新固定到任务栏。[/scode]步骤三 锁死更新文件更新文件在C:\Program Files (x86)\Google下。网上也有说在这两个地方的，但我找不到，最后我是在资源管理器中直接搜到的GoogleUpdate.exe。C:\Program Files (x86)\Google\Update\ C:\Users\你的用户名\AppData\Local\Google\Update\为什么为什么不修改注册表，更彻底，对所有启动方式有效一般情况不动注册表，修改错了容易让系统出错，以前就因为清理系统垃圾的时候过度修改过注册表，结果系统出问题了，重装系统后才好，所以不懂得注册表不要按网上随便改。为啥没有 锁死更新文件夹不知道为啥我的Chrome没有Update文件夹，我没有实操过所以没写。以下是网上得步骤：打开文件夹：C:\Program Files (x86)\Google\Update 删除该文件夹内的所有内容 右键 Update 文件夹 → 属性 → 安全 选项卡 点击 编辑，找到 SYSTEM，将它的权限全部设为 拒绝 点击确定保存

claude code快速上手
type: item
date: 2026-05-28T15:55:00+08:00
description: install国内安装每次更新前，先在终端运行 claude 的情况下退出 Claude Code，再执行 npm update -g @anthropic-ai/claude-code，npm 就能顺利清理临时目录了。npm config set registry https://registry.npmmirror.com npm install -g @anthropic-ai/claude-code # 切换回官方 npm config set registry https://registry.npmjs.org # 先执行一次claude命令，然后会自动生成配置文件，但是让你登陆后才能使用，此时就可以退出了 接下来修改配置文件即可 (linux)~/.claude/settings.json (windows) C:\Users\<User>\.claude\settings.jsondeepseek{ "env": { "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic", "ANTHROPIC_AUTH_TOKEN": "sk-", "ANTHROPIC_MODEL": "deepseek-v4-flash", "ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-pro", "ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-v4-flash", "CLAUDE_CODE_SUBAGENT_MODEL": "deepseek-v4-flash", "CLAUDE_CODE_EFFORT_LEVEL": "max" }, "model": "deepseek-v4-flash", "enabledPlugins": { "code-simplifier@claude-plugins-official": true }, "theme": "dark" }moonshot{ "env": { "ANTHROPIC_BASE_URL": "https://api.moonshot.cn/anthropic", "ANTHROPIC_AUTH_TOKEN": "sk-", "ANTHROPIC_MODEL": "kimi-k2.5", "ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k2.6", "ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-k2.6", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-k2.5", "CLAUDE_CODE_SUBAGENT_MODEL": "kimi-k2.5", "CLAUDE_CODE_EFFORT_LEVEL": "max", "ENABLE_TOOL_SEARCH": "false" }, "model": "haiku", "enabledPlugins": { "code-simplifier@claude-plugins-official": true }, "theme": "dark" }[scode type="green"]建议搞一个claude code router,接入免费模型，目前我每天用免费api都够用了 modelscope最靠谱[/scode]

Android 应用分身|双开|隐私空间 下用户证书不生效
type: item
date: 2026-05-26T21:47:00+08:00
description: 是什么简单来说，无论是应用双开、应用分身还是隐私空间，它们底层的实现原理大多是基于 Android 的 Multiple Users（多用户机制）。主应用运行在宿主用户（User 0）下，而分身应用运行在另一个独立的用户空间（比如 User 10 或 User 999）下。核心问题在于：Android 的“用户证书（User Credentials）”是按用户空间（User）隔离的。 你在手机设置里安装的证书，默认只写入了主用户（User 0），分身用户根本读不到，而且系统默认没有提供为分身用户安装证书的入口。如何做下载ADB[scode type="red"]如果遇到adb找不到设备的情况，请排查：USB调试开启了吗？MTP文件传输开启了吗？仅充电允许ADB开启了吗？ADB/MTP驱动正常吗（打开设备管理器 → 存在未知设备（感叹号））如果是驱动问题，安装华为驱动后可直接修复，点我访问下载主页[/scode]获取分身用户的 IDadb shell pm list users将证书安装到分身用户adb shell am start --user <用户ID> -a android.credentials.INSTALL adb shell pm clear --user <USER_ID> com.android.certinstaller效果： 手机屏幕上会弹出一个熟悉的安全界面，提示你选择并安装证书。此时安装的证书将专门服务于分身应用。为什么为什么不直接把用户证书提升为系统证书？需要root权限。为什么还要操作手机，不直接用命令完成证书安装？因为 Android 系统的凭据存储（KeyStore）不是一个简单的、暴露在用户层面的文件夹，而是一个受到系统底层硬件（如安全芯片/TEE）保护的加密数据库。每个用户空间（User）的加密密钥都是隔离的。为什么不使用Xposed JustTrustMe？JustTrustMe 是一个基于 Xposed 框架的开源模块。它的核心原理非常暴力且有效： 它通过 Hook（拦截）Android 系统中几乎所有主流的网络请求库（如 HttpClient、OkHttp、Volley、WebView 等），强行把里面的证书校验逻辑返回值改成“True”。如果是自己开发的app，在开发测试阶段当然可以使用该模块，但是当前需求是用户证书如何作用于【微信分身】上。写在最后[scode type="red"]实际测试的时候，发现在华为老版本系统上EMUI系统上，副用户安装证书后可用，但在HONOR 10上副用户安装证书但应用识别不到，应该是魔改系统，证书逻辑不一样。[/scode]

JVM 调优实践
type: item
date: 2026-05-25T23:55:00+08:00
description: [scode type="share"]环境：JDK 21 · 2GB内存 · 1-2核 · Spring Boot[/scode]将本地jdk加入到sdkman管理工具为了避免每次执行java都输入绝对路径，我决定将其加入到sdk版本管理中。sdk install java 21-tem-local /projects/java/task_center/jdk-21.0.11+10 # 将该版本设置为默认版本，即当前使用版本 sdk default java 21-tem-local # 检查版本是否已经生效 java -version openjdk version "21.0.11" 2026-04-21 LTS OpenJDK Runtime Environment Temurin-21.0.11+10 (build 21.0.11+10-LTS) OpenJDK 64-Bit Server VM Temurin-21.0.11+10 (build 21.0.11+10-LTS, mixed mode, sharing) Q1：先摸底，看当前 Java 版本和运行状态# 确认 JDK 版本 root@iZ2ze649h4amvjgmwjh43hZ:~# /home/frunoob/my_project/java/distrisched/task_center/jdk-21.0.11+10/bin/java -version openjdk version "21.0.11" 2026-04-21 LTS OpenJDK Runtime Environment Temurin-21.0.11+10 (build 21.0.11+10-LTS) OpenJDK 64-Bit Server VM Temurin-21.0.11+10 (build 21.0.11+10-LTS, mixed mode, sharing) # 查看当前服务进程和 JVM 参数 root@iZ2ze649h4amvjgmwjh43hZ:~# ps aux | grep java USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 688 2.9 20.2 3197048 388852 ? Sl May25 1:00 /home/frunoob/my_project/java/distrisched/task_center/jdk-21.0.11+10/bin/java -jar /home/frunoob/my_project/java/distrisched/task_center/task_center-v0.0.1-exec.jar --spring.config.additional-location=/home/frunoob/my_project/java/distrisched/task_center/application.properties # 查看系统内存分布 root@iZ2ze649h4amvjgmwjh43hZ:~# free -h total used free shared buff/cache available Mem: 1.8Gi 823Mi 120Mi 1.0Mi 933Mi 874Mi Swap: 1.0Gi 940Mi 83Mi # 查看 CPU 信息 root@iZ2ze649h4amvjgmwjh43hZ:~# nproc && lscpu | grep "CPU(s)" 2 CPU(s): 2 On-line CPU(s) list: 0,1 NUMA node0 CPU(s): 0,1Q2：2GB 内存，Heap 该分多少？[scode type="share"]黄金比例：Heap ≤ 总内存 × 50%，剩余留给 OS、Metaspace、线程栈[/scode] #你的机器推荐配置：Heap 700MB，Metaspace 128MB -Xms700m -Xmx700m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m #为什么 Xms=Xmx？避免运行时动态扩容引发 Full GC 抖动Q3：JDK 21 用哪个垃圾回收器？#1核~2核 + 小内存 → 首选 ZGC（JDK21 默认开启分代ZGC） -XX:+UseZGC -XX:+ZGenerational #备选：G1GC（更成熟，兼容性好） -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1HeapRegionSize=4m对比验证哪个更适合你：# 加上 GC 日志再观察 -Xlog…

linux宝塔面板彻底清理
type: item
date: 2026-05-25T10:57:40+08:00
description: 第一步使用官方脚本卸载wget https://download.bt.cn/install/bt-uninstall.sh bash bt-uninstall.sh清理模式选择2清理残留文件rm -rf /www[font color="OrangeRed"]这会将网站，证书，docker-compose配置，数据库等全部清理，请确保备份以免后续有需要的文件被意外删除了[/font]清理残留服务一共要清理两个残留服务site_total.service,spring_executor.servicesystemctl list-units --type=service # 禁用服务并不会清理service源文件，所以这里看看service源文件到底在哪里，在disable之后手动删除它！ systemctl status site\_total.service cat /etc/systemd/system/site\_total.service sudo systemctl stop site_total.service sudo systemctl disable site_total.servicesudo systemctl status spring_executor.service cat /usr/lib/systemd/system/spring_executor.service sudo systemctl stop spring_executor sudo systemctl disable spring_executor sudo systemctl daemon-reload sudo systemctl reset-failed[scode type="red"]随着宝塔面板的更新可能会出现别的服务，所以分享一个清理想法，宝塔面板的服务名称似乎都是用下划线连接单词的[/scode]

docker实操指南
type: item
date: 2026-05-22T16:57:00+08:00
description: 本内容聚焦Docker Compose实操应用，精简基础容器命令，主打多容器编排部署，汇总项目启停、配置编写、服务管理、环境调试等常用操作，适配日常项目快速搭建与运维场景。查看容器查看所有可用字段## 查看所有可用字段 docker ps --format "{{json .}}" { "Command": "\"/entrypoint.sh mysq…\"", "CreatedAt": "2026-02-01 21:47:18 +0800 CST", "HealthStatus": "healthy", "ID": "6591d25f9ae8", "Image": "mysql/mysql-server:5.7", "Labels": "com.docker.compose.config-hash=0d064f827a3e4a9d3bb3b2f01a6e80b32644cae8d996dfdb3d5a67f2c1ce843c,com.docker.compose.container-number=1,com.docker.compose.depends_on=,com.docker.compose.image=sha256:a4ad24fe52cdf82a35c6f2dae7670cfebf39357de983cb909db07c8b56df4dd1,com.docker.compose.oneoff=False,com.docker.compose.project.config_files=/www/server/panel/data/compose/app-compose/docker-compose.yaml,com.docker.compose.project.working_dir=/www/server/panel/data/compose/app-compose,com.docker.compose.project=app-compose,com.docker.compose.replace=mysql57,com.docker.compose.service=mysql57,com.docker.compose.version=2.39.1", "LocalVolumes": "1", "Mounts": "app-compose_my…", "Names": "mysql57", "Networks": "app-compose_default-network", "Platform": null, "Ports": "33060/tcp, 0.0.0.0:3307-\u003e3306/tcp, [::]:3307-\u003e3306/tcp", "RunningFor": "3 months ago", "Size": "88B (virtual 432MB)", "State": "running", "Status": "Up 9 hours (healthy)" }以表格形式（带标题）输出全部容器并指定字段docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}\t{{.Size}}\t{{.State}}" -a 4155921bc878 watchtower 0B (virtual 17.4MB) exited 4cec5eb6ccf4 mysql80 0.0.0.0:3306->3306/tcp, [::]:3306->3306/tcp, 33060-33061/tcp 90B (virtual 496MB) running 6591d25f9ae8 mysql57 33060/tcp, 0.0.0.0:3307->3306/tcp, [::]:3307->3306/tcp 88B (virtual 432MB) running配置默认格式，避免每次都要写# ~/.docker/config.json 中添加 { "psFormat":"table {{.ID}}\t{{.Names}}\t{{.Ports}}\t{{.Size}}\t{{.State}}" }ps的等价命令docker container ls -a CONTAINER ID NAMES PORTS SIZE STATE ff566067f269 interview-redis 0B (virtual 117MB) exited 0496f4af3dc7 interview-postgres 0B (virtual 438MB) exited 45feb05752c2 interview-rustfs 4.59kB (virtual 283MB) exited清理…

Windows 远程桌面连接（RDP）要求的函数不受支持| CredSSP 加密 Oracle 修正
type: item
date: 2026-05-18T21:00:23+08:00
description: 问题描述Windows 11 电脑进行了安全更新，要求强制使用高安全级别的凭据加密；而 Windows Server 2012 服务器可能很久没更新，或者没有启用对应的安全策略。当两边安全级别对不上时，Windows 11 为了保护你的安全，就会直接断开连接。解决措施reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters" /v AllowEncryptionOracle /t REG_DWORD /d 2 /f立即生效无需重启电脑。