静态内部类单例与枚举单例在反射和序列化攻击下的差异对比
单例(Singleton)是面试里被问烂的模式,但真正能区分候选人水平的,不是「你怎么写单例」,而是「你的单例在反射和序列化面前还剩几条命」。很多人在白板上写出 private static class Holder 版本,觉得既延迟加载又线程安全,已经是满分答案;面试官接着追问一句「我用反射把它构造两次,你怎么办」,回答就开始含糊了。这篇文章把两种常见的高
FWDD.NET · 技术文档站
聚焦后端工程实践:语言教程、功能实现、面试题与运维脚本。每篇都从真实踩坑场景出发,给出可直接复用的代码与取舍依据。
单例(Singleton)是面试里被问烂的模式,但真正能区分候选人水平的,不是「你怎么写单例」,而是「你的单例在反射和序列化面前还剩几条命」。很多人在白板上写出 private static class Holder 版本,觉得既延迟加载又线程安全,已经是满分答案;面试官接着追问一句「我用反射把它构造两次,你怎么办」,回答就开始含糊了。这篇文章把两种常见的高
线上常遇到一类故障:某个服务因为配置写错、依赖模块缺失或端口被占用而一启动就退出,而它的 unit 文件里写着 Restart=always。systemd 会立刻重新拉起它,进程又秒退,于是形成每秒数次、甚至每秒十几次的重启风暴。日志被刷满、监控被打爆、CPU 被 fork 和 exec 吃掉,真正的根因被淹没在 Started ... 与 Stopped
在同步 Web 应用里,把 request_id、当前登录用户、trace_id 这类信息塞进 threading.local 是很常见的做法。一旦迁到 asyncio 或 FastAPI/Starlette 这类异步栈上,这套写法立刻失效:协程在同一个线程里被事件循环交替调度,threading.local 里存的数据会互相串台,A 请求的 user_id
在 TypeScript 里处理对象类型转换时,keyof、索引访问 T[K] 和 as 子句几乎总是一起出现。看起来只是三行泛型,实际写进项目里却经常出现两种极端:要么推导结果是 never,要么 as 把键名重映射成了 string 这种宽类型,导致下游类型守卫全部失效。本文围绕这三个语法点的交互边界展开,用可运行的代码片段说明什么时候能推导、什么时候会
在 Go 面试里,只要聊到并发,select 几乎是必问项。而一旦候选人能说出 select 的语法,面试官往往会追一句:「那 select 里加 default 会发生什么?会不会空转?」再往下就是经典陷阱:「time.After 放在 for + select 里会不会漏定时器?」 这两问看着是语法细节,实际考的是三件事:对 select 调度语义的理解
表单校验最常见的写法是「一长串 if + return」,字段一多就变成几十行 if 嵌套,错误信息散落在各处,单元测试只能整块跑。本文用 PHP 的 array_reduce 把一组校验规则折叠成一个链式校验器:规则顺序注册、遇到第一个失败立即短路,并返回统一结构的错误对象,调用方拿到的永远是「要么 null,要么一个含 code / field / me
Ansible 默认的执行模型是「遇错即停」:playbook 里任何一个 task 在目标机上返回非零退出码,host 就会被标记为 failed,后续所有任务直接跳过。这对一次性脚本够用,对滚动部署、数据库迁移、服务重启这类「失败后需要原地收拾残局」的场景就非常别扭——你真正想要的是「这一步可能失败,失败了先回滚/降级/重试,收拾干净再决定整体成败」。b
用 record 承载请求参数、配置项、值对象时,最容易踩的坑不是「怎么少写 getter」,而是「构造时该不该校验、怎么校验、规范化后的值怎么写回去」。很多人第一次写紧凑构造器(compact constructor)时写出了 this.name = name.trim();,编译器直接报 error: cannot assign a value to f
用 Redis 做限流时,很多人第一版都会写成「先 ZREMRANGEBYSCORE 清过期、再 ZCARD 判断、最后 ZADD 写入」的三段式逻辑。单机压测没问题,一上并发就出现超卖:明明配置 100 次/分钟,实际放过了 130 次。根因不是 Redis 性能不够,而是这三步之间存在竞态窗口——两个请求可能同时读到 99,然后各自 +1。把这三步塞进同