源码 Issue Tracker 中应该用 English 进行沟通,否则可能会被折叠,再不济上个机器翻译。
产品每个月1号均发布一次,避免小改动被搁置更新数月。
安装程序 - Inno Setup 免费安装包制作工具。
github.com Dependabot 可自动拉取已修复安全漏洞的依赖项。
常用符号:
📝 - 编辑、修改、写入。
🛒 - 购物、买东西。
IDE:
终端编辑器 - SSH + tmux + LazyVim
Neovim + LSP(LazyVim/nvim-jdtls):
sudo apt install neovim -y
版本低于 0.11+ 可换用 sudo apt -t testing install ^neovim$
安装后,输入命令启动: nvim 或软链接过来的命令 vim
sudo apt install npm -y
sudo npm install -g vscode-langservers-extracted 安装后多个命令 vscode-html-language-server
neovim 0.11+ 不再通过 nvim-lspconfig 启用 LSP,而直接开箱即用:
说明 - neovim 会感知 ~/.config/nvim/lsp/ 目录内的 *.lua 配置,或全部配置到 init.lua
mkdir -p ~/.config/nvim/
nvim ~/.config/nvim/init.lua
-- 设置通用的 capabilities(开启全局 Snippet 支持)
vim.lsp.config('*', {
capabilities = {
textDocument = {
completion = {
completionItem = {
snippetSupport = true, -- 对 html / css / json 等非常关键
},
},
},
},
})
vim.lsp.config('html', { // 必须指定 cmd 和 filetypes。
cmd = { 'vscode-html-language-server', '--stdio' },
filetypes = { 'html' }, -- script 标签若想补全 DOM API 需加配 typescript-language-server
root_markers = { ".git", "package.json" }, -- 根目录定位
init_options = {
provideFormatter = true, -- 启用格式化
},
}) -- 等同 ~/.config/nvim/lsp/html.lua: return { cmd = ... }
vim.lsp.enable('html') -- 先 config 再 enable
-- 全局开启 Neovim 原生 LSP 补全
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client and client.supports_method('textDocument/completion') then
-- 开启原生 LSP 自动补全
vim.lsp.completion.enable(true, client.id, args.buf, {
autotrigger = true, -- 输入时自动触发弹窗
})
end
end,
})
-- 设置补全弹窗行为:不自动选中第一项、显示预览弹窗
vim.opt.completeopt = { "menu", "menuone", "noselect", "popup" }
vim.lsp.enable("css")
:checkhealth vim.lsp 查看其中日志 cat /home/pi/.local/state/nvim/lsp.log
:lua vim.lsp.buf.format() 手动格式化;或绑定快捷键 :f
手动补全: Ctrl + x 松开 Ctrl + o
:set ft? 输出 filetype=html
安装 LazyVim 前应备份下 neovim 配置目录: mv ~/.config/nvim{,.bak}
命令:
:w x.txt 写入文件;:wq x.txt 写入并退出;:x 保存当前已打开文件。
:Vex 左右分屏,鼠标可切换窗口焦点(或Ctrl+w+h左l右),中键点文件名能在右屏打开,有右键菜单,纯 Linux 物理控制台也支持鼠标。
:Ex 打开目录树;:bd 退出目录树
桌面编辑器 - VS Code
局部不要吃掉异常,还应抛出相应异常,最后通过全局异常捕获来兜底。(JAX-RS用jakarta.ws.rs.ext.ExceptionMapper/安卓用Thread.setDefaultUncaughtExceptionHandler)
日期格式:2025-04-01T00:00:00+08:00 后缀 +08:00 指左侧时间显示的就是该时区本地时间,无需转换了,无后缀则为UTC时区。
存储大小:给大众用户看用MB,给技术用户看用MiB,其中i字母是Binary的缩写;Windows因历史缘故,虽显示MB,但实质数值依然是MiB。
死记:
字段初始化器(field initializer)不能赋值非静态字段,比如 this 等,即使反射 this 实例也只能得到 null 值,应延至构造函数内赋值。
将变量=null或=赋值,等同清除其原先内存地址,但如果有其他变量指向了它旧的地址,在声明范围内(方法内局部变量return前)其旧数据也持续保留;即C#和Java类内或方法内声明局部变量并赋值个引用类型,返回前即使原对象=null,也不会释放该local variable;如果对象就地修改,依然会实时变化。
已赋值对象再次被赋值,则旧对象(String)会在下一次GC回收:var oldObj = new String(); oldObj = new Object();但如果是“追加订阅”(+=)则依然要上弱引用。
对象置null后,字段就不可达了,故下次GC就会回收;即内存泄漏通常出现在长周期对象的字段上。
C# 和 JavaScript 标注了 async 的方法,只有代码运行到了 await 处(若await嵌套的最终await未执行也算同步),它才会转为异步,否则全程同步,故可用 if 包住 await 调用;Java 虚拟线程则全程异步。
Activity 窗口和 Node 场景等重量对象,必须上 WeakReference 弱引用,否则 GC 永不释放;C# 检查对象是否已经 GC(IDE调试器->孤立节点数):
var gch = GCHandle.Alloc(obj, GCHandleType.Weak); var isAlive = gch.Target != null; // 非 null 则析构函数不会触发。
编程术语:
Overload(重载)Override(覆盖)Overwrite(重写)
组合优于继承 - 即前者将功能分散至接口类型的成员变量上实现,且不会像继承那样,残留用不到的方法。
两者并不互斥,可先继承最大公约数,再组合额外需要的能力;普通规模项目两者不分伯仲;如果不在意多一些成员变量,也可以完全由组合拼凑。
判断范围用 if else,判断确定值用 switch case。
编程工具:
C库 - glibc(GNU C Library)、musl 或 Android Bionic,Linux均会内置;带头文件的包通常有后缀,例如 zlib1g-dev 或 glibc-devel;控制台打印就会用到 libc 库常用函数 printf("strings\n");
C/C++等主流语言编译工具 - Clang 中间语言 LLVM IR 比 GCC 的 GIMPLE IR 更易读和标准化。
clang --version 会输出默认编译目标平台 ... Target: x86_64-pc-linux-gnu 若是 AARCH64 则输出 aarch64-unknown-linux-gnu
运行时/编译时:
动态链接 (Dynamic Linking) - 共享库采用 SONAME 命名机制,即程序内部会记录下所依赖库的大版本。
cat /lib/x86_64-linux-gnu/libc.so 就能看到其指定的大版本号库 libc.so.6(2MiB/余生也不会升到7了),后续的小版本则不固定(libz.so、libz.so.1 均会链接至 libz.so.1.3.1)。
头文件 stdio.h 通常位于 ls /usr/include/
静态链接 (Static Linking) - 直接将函数实现嵌入程序内。
延时任务 - 简单业务用无依赖的JDK延迟队列DelayQueue,复杂场景用MQ的DelayMessage、死信(Dead Letter)。
并发是交替用同一份资源,并行则是N个任务同时用N份资源。
文件路径名不允许冒号(:),故应替换为其他符号:
Java - Instant.now().toString().replace(":", "_");
JavaScript - new Date().toJSON().replaceAll(":","_");
说明 - 如果不表现时间,可换用UUID。
map、filter:
说明 - map用来逐项转换,返回类型取决于lambda的返回值;filter则是用来筛选数据项的,通过lambda的boolean返回值决定每项的存留。
JavaScript map例子: [1, 2, 3, 4, 5].map(num => num * 2);
JavaScript filter例子: ["spray", "elite", "exuberant"].filter((word) => word.length > 6);
Java List列表想用 map 和 filter 可直接 list.stream() 即可;但Java数组需要包装下:
Stream.of(new String[]{"a", "b"}); Stream.of(1, 2, 3);
Arrays.stream(stringArray); Intstream.of(new int[]{1,2,3});
for 嵌套计算全局索引号码:
for (int i = 0; i < iCount; i++)
{
for (int j = 0; j < jCount; j++) { var globalNumber = i * jCount + j; }
}