文章目录
-
- 前言
- 一、代码质量与重构
-
- [1.1 代码坏味道:代码不会说话,但会给你使眼色](#1.1 代码坏味道:代码不会说话,但会给你使眼色)
- [1.2 复杂度:脑子够不够用,是能算出来的](#1.2 复杂度:脑子够不够用,是能算出来的)
- [1.3 质量度量:数字不会骗人,但你会被数字骗](#1.3 质量度量:数字不会骗人,但你会被数字骗)
- [1.4 原则:老前辈们的六字真言](#1.4 原则:老前辈们的六字真言)
- [1.5 通用编程基础:说人话版](#1.5 通用编程基础:说人话版)
- 二、软件架构与设计模式
-
- [2.1 架构原则:盖楼之前先画图](#2.1 架构原则:盖楼之前先画图)
- [2.2 设计模式:23 种套路,程序员的武功秘籍](#2.2 设计模式:23 种套路,程序员的武功秘籍)
- [2.3 面向对象与模块化:三件套与全家桶](#2.3 面向对象与模块化:三件套与全家桶)
- [2.4 分层对象模型:POJO、DTO、VO 全家桶](#2.4 分层对象模型:POJO、DTO、VO 全家桶)
- [2.5 数据与状态:缓存三兄弟的恩怨情仇](#2.5 数据与状态:缓存三兄弟的恩怨情仇)
- 三、并发与多线程:一个人干八个人的活
- 四、数据结构与算法:面试官的快乐源泉

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/H1727548
前言
跟 AI 聊代码,最崩溃的瞬间不是它不会,是你们俩压根不说同一种话。
你指着代码说"这块味儿不对",它给你改了个变量名;你说"把这段重复的抽出来",它给你复制了三遍,还贴心补了注释。
问题出在哪?出在你们没有共享词表。
术语就是你和 AI 之间的暗号。你说 Code Smell,它脑子里直接调出一整套重构方案;你自造一个"代码馊味指数",它只能一边微笑一边瞎猜,最后交给你一份"看起来都对、用起来哪儿都不对"的东西。
所以我把跟 AI 对线真正用得上的词,按主题码了码。看完你就明白:跟 AI 吵架,吵的不是谁对谁错,是谁的词表大。
一、代码质量与重构
1.1 代码坏味道:代码不会说话,但会给你使眼色
Code Smell(代码坏味道):代码的表层信号,暗示背后藏着更深的设计问题。它不一定错,但闻着不对------就像冰箱里那盒三天前的酸奶,还没过期,但你不想碰。
Duplicated Code(重复代码):Ctrl+C、Ctrl+V 的产物。一时复制一时爽,改需求的时候火葬场------你改了 A 处,漏了 B 处,bug 就像地鼠一样从你漏掉的地方钻出来。这是最常见、重构收益最大的坏味道。
Long Method(长方法):一个方法两百行,干了五个人的活。读它就像读一篇没有标点的小说,你坚持不到结尾。一般超过 20-30 行就该 Extract Method 了。
Large Class(大类):一个类 300 行起步、五六个职责。它什么都管,最后什么都管不好。Extract Class 了解一下。
Feature Envy(特性依恋):一个方法天天往别人家的数据里凑,对自己家的数据不闻不问。就像同事天天在你这儿蹭饭,最后他家厨房长蘑菇了。Move Method 送它回去。
God Object(上帝对象):所有逻辑的垃圾桶。判断方法特别简单------你问它"你是干嘛的",回答里只要必须带"和"字,它就是。"我管订单和用户和支付和库存......"停,够了。
Spaghetti Code(意大利面条代码):goto、标志位、八层嵌套搅在一起。读这种代码就像吃意面,你永远不知道这一根会从哪儿冒出来。
Magic Number(魔数):代码里突然蹦出一个 86400。是啥?秒数。啥的秒数?不知道,写这行的人上个月离职了。具名常量才是正道:
python
# 改之前:86400 是什么?没人知道
if session_timeout > 86400:
pass
# 改之后:一眼看懂
MAX_SESSION_SECONDS = 86400
if session_timeout > MAX_SESSION_SECONDS:
pass
Dead Code(死代码):没人调用的代码,纯属给后人留的阅读理解题。删掉它零风险、收益立竿见影------就像清理微信里三年没联系的人,爽。
Shotgun Surgery(散弹式修改):改一个需求要动十个文件。改完第一个忘了第二个,上线当晚你睡得比代码还香(不是)。
Long Parameter List(长参数列表):方法参数超过三四个,调用方就得对着文档数括号。传参像相亲带了六个媒人,谁跟谁都不熟。参数对象、方法对象安排上。
Primitive Obsession(基本类型偏执):手机号用 String,金额用 double,全拿基本类型硬扛。类型系统是个好东西,你非把它当绳子用。封装成值对象,让编译器替你挡一部分蠢。
Comments(过量注释):注释解释"做了什么"的,基本都会跟代码漂移------代码改了三版,注释还停在两年前那个下午。好代码自解释,注释只写"为什么",别写"是什么"。
1.2 复杂度:脑子够不够用,是能算出来的
Cyclomatic Complexity(圈复杂度):决策点数量 + 1,机器能自动算。团队一般卡在 10 以内,超过这个数,代码就像迷宫------你看得懂,但你不想看懂。
Cognitive Complexity(认知复杂度):圈复杂度是给机器看的,这个才是给人看的------嵌套越深,罚分越重。业界常见双指标:圈复杂度 <10,认知复杂度 <15。你读代码时眉头皱了几次,它都记着。
Big-O Notation(大 O 记号):描述运行时间/内存随输入规模增长的上界,O(1)、O(n)、O(n²)......跟硬件无关的性能语言。面试官的最爱,程序员的噩梦,但确实有用------它让你写之前就知道这玩意儿会不会卡成 PPT。
Coupling(耦合):模块之间互相牵扯的程度。高耦合等于牵一发而动全身,改个 A,B、C、D 全家跟着改。
Cohesion(内聚):模块内部齐心协力干一件事的程度。高内聚 + 低耦合,就是设计界的模范夫妻。
N+1 Query Problem:先查 100 条主记录,再对每条各发一次关联查询,一共 101 次 SQL。数据库看了都想报警。Eager loading 或者 join 一次搞定:
sql
-- N+1:查了 N 条主记录,又发了 N 次关联查询
SELECT * FROM users; -- 1 次
SELECT * FROM orders WHERE user_id = ?; -- N 次
-- 正确姿势:一次 JOIN
SELECT u.*, o.* FROM users u
LEFT JOIN orders o ON o.user_id = u.id;
Nesting Depth(嵌套深度):if 里套 if 里套 if,代码像俄罗斯套娃。早返回、提取方法,还你一个平铺直叙的人生。
Circular Dependency(循环依赖):A 依赖 B,B 又依赖 A。俩模块互相抱着不撒手,重构的时候你才知道什么叫剪不断理还乱。
Dependency Direction(依赖方向):依赖箭头应该指向稳定/抽象的那一侧。方向反了,底层一改上层全线崩溃------就像地基一抖,整栋楼跟着跳广场舞。
Dependency Hygiene(依赖卫生):import 是不是都用上了?有没有循环依赖?版本冲不冲突?冗余依赖拖慢构建、加大变更风险。依赖也是要讲卫生的,不然就是程序员的囤积癖。
1.3 质量度量:数字不会骗人,但你会被数字骗
Code Coverage(代码覆盖率):测试执行期间代码被跑到的比例。注意,它只衡量"被执行了多少",不衡量"测得好不好"。覆盖率 100% 不代表没 bug,只代表你的测试很会表演。
Test Coverage(测试覆盖率):比代码覆盖率更大的筐,需求覆盖、场景覆盖都算。一个管"该测的测了没",一个管"代码跑过没",俩互补。
Technical Debt(技术债):为了上线写的"差不多得了"的代码,就是借钱。不还?利息是每次改需求时多花的两个小时。它不是坏代码的代名词,而是明知不对先这么干的有意取舍。关键是得定期还,别等它利滚利。
Code Review(代码审查):人肉看 diff。静态分析给不了的上下文判断,只有人能给。这是 bug 进主干前最后一道人工防线,也是程序员之间最文明的吵架方式。
Static Analysis(静态分析):不跑程序,光看源码就能查出 SQL 注入、死代码、硬编码密钥。每次提交自动跑,规模化零成本。缺点是有误报,而且它永远读不懂你的业务意图------像安检机,查得出违禁品,查不出你包里那本书好不好看。
Dynamic Analysis(动态分析):跑起来才暴露的毛病------内存泄漏、性能瓶颈,静态分析看不见的它都看得见。俩配合着用。
Maintainability(可维护性):软件生命周期 80% 的成本在维护阶段。写的时候多花十分钟,改的时候省下十个小时,这笔账小学生都会算。
Code Readability(代码可读性):别人能不能快速看懂你的代码。可读的代码才敢改,不可读的代码只能祈祷它永远不用改。
Self-Documenting Code(自解释代码):代码本身清楚到不需要注释解释。注释和代码会漂移,代码本身不会------除非你后来又改了它,那当我没说。
Naming Convention(命名规范):命名是代码可读性的地基。变量叫 temp、data、a、b 的,基本都是在给后人埋雷。
1.4 原则:老前辈们的六字真言
DRY(Don't Repeat Yourself):每份知识只允许有一个权威来源。重复的英文叫 WET(Write Everything Twice),湿了就容易发霉,你懂的。
KISS(Keep It Simple, Stupid):多数系统,越简单越好用。简单不是智商低,是智商高的表现。为复杂而复杂,是缺陷和成本的来源。
YAGNI(You Aren't Gonna Need It):不需要的功能别提前写。超前功能等于白付开发、测试、文档成本,而且大概率永远用不上------就像健身房年卡。
SOLID:五个原则的统称,架构师吵架时的公共语言,五个字母一出,气势先赢一半。
SRP(单一职责):一个类只有一个职责,只有一个改变的理由。问它"你为什么改"答不上来的,多半是职责太多。
OCP(开闭原则):对扩展开放,对修改关闭。不改旧代码就不会引入新 bug------这是世界上最朴素的安全感。
LSP(里氏替换):子类应能无副作用替换父类。经典反例:Square 继承 Rectangle。正方形不是长方形?数学老师都气笑了。但在这个场景里,它俩还真不能这么继承。
ISP(接口隔离):胖接口拆成小接口,别逼实现方实现一堆用不到的方法------就像别逼一个不吃辣的人陪你吃重庆火锅。
DIP(依赖倒置):高层不依赖低层,都依赖抽象。用抽象隔开,核心代码才不会被底层实现绑架。
Law of Demeter(迪米特法则):只和直接的朋友交谈。a.getB().getC().doX() 这种链式穿透,就像让你隔着两栋楼喊话------喊是能喊到,但整栋楼都觉得你疯了。
SoC(关注点分离):不同职责放不同模块,一次只处理一件事。分层架构的根基。把登录和算工资放一起的,都是狠人。
Least Privilege(最小权限):只给完成任务需要的最小权限。权限给多了攻击面就大------就像把家里钥匙配给全小区,方便是方便,但你最好别丢东西。
Fail Fast(快速失败):出错就立刻炸,别带病运行。错误发现得越早修得越便宜。带病上线的后果是:小病拖成大病,大病拖成下线。
Composition Over Inheritance(组合优于继承):优先组合,少用继承。继承是强耦合,组合更灵活更好测------毕竟你亲爹只有一个,但你可以有很多合作伙伴。
1.5 通用编程基础:说人话版
Serialization / Deserialization(序列化 / 反序列化):对象转成字节流/JSON,反序列化是倒过来还原。跨进程、跨网络传数据全靠它。本质就是:把活物装进快递箱,到了再拆出来。
Generics(泛型):参数化类型,让同一个类/方法适配多种类型还不放弃类型安全。编译期就检查,比运行期报错温柔一百倍。泛型就像带分隔的打包盒:装啥都行,标签还提前贴好,总比到站了才发现碎了一地强。
Reflection(反射):运行时查看类信息、调用方法、访问字段。DI、JSON 序列化、注解处理的地基。但性能低、破坏封装------反射一时爽,重构火葬场。
Annotation(注解):给代码贴的元数据标签,编译期或运行期可读。@Override、@Inject 这些,就是给框架看的便利贴。
Lambda Expression(Lambda 表达式):匿名函数,可以当参数传。函数式编程的敲门砖,回调、集合操作瞬间从三行变一行。
Closure(闭包):函数能捕获定义时所在作用域的变量。回调、函数式编程的核心。搞懂它,你就理解了 JS 一半的"为什么"。
Exception(异常):运行时错误事件。受检异常必须捕获,非受检异常随便炸。统一错误处理,避免错误被悄悄吞掉------吞异常等于吞证据。
Callback(回调):把函数当参数传进去,事件发生了再叫你。异步编程的基础,但多层回调嵌套就是"回调地狱",比俄罗斯套娃还套娃。Async/Await 就是来救它的。
Immutable Object(不可变对象):创建后就不能改的对象。天生线程安全、可共享、无副作用。并发编程的最佳实践,也是函数式编程的洁癖来源。
First-Class Function(一等函数):函数能当参数传、能当返回值、能赋值给变量。在有的语言里函数是公民,在另一些语言里函数只是临时工。
Type System(类型系统):静态类型编译期检查,动态类型运行期检查。静态类型提前抓 bug、IDE 体验好;动态类型灵活、开发快。没有绝对好坏,只有适不适合。
GC(垃圾回收):运行时自动回收没人要的内存。解放了手动管理内存的程序员,代价是时不时 STW 卡一下------就像保洁阿姨来打扫,你得先站起来让位。
Memory Leak(内存泄漏):对象不再用了还被引用着,回收不掉。长期运行必 OOM,服务端和移动端的老朋友。
Stack Overflow(栈溢出):调用栈太深,通常是无限递归。写递归记得写终止条件,不然程序会表演原地跳高,直到裁判喊停:
python
# 优雅的递归:有终止条件
def factorial(n):
return 1 if n <= 1 else n * factorial(n - 1)
# 灾难的递归:没有终止条件,栈溢出警告
def infinite():
return infinite()
二、软件架构与设计模式
2.1 架构原则:盖楼之前先画图
Architecture(架构):系统的骨架,组件和它们之间的关系。架构选错是最高成本的技术债,后期重构代价指数级上涨------地基歪了,楼盖得越高越危险。
Layered Architecture(分层架构):表现层、业务层、数据层,一层管一层,依赖单向向下。直观、清晰、最普遍。缺点是容易让业务层依赖数据层,想搞依赖倒置就难了。
Onion Architecture(洋葱架构):领域模型在最里面,外层只能往里依赖,核心不依赖任何外部框架。换数据库、换 UI 框架,核心纹丝不动。
Hexagonal Architecture(六边形架构):核心业务居中,端口定义接口,适配器连接外部(数据库、API、UI)。和洋葱架构一个妈生的------核心零外部依赖,天生适合 TDD。
Clean Architecture(整洁架构):洋葱和六边形的结合体,规则只有一条:依赖永远指向内层抽象。后端架构界的主流思想,开会必提,落地看人。
Module Boundaries(模块边界):模块之间定义清楚的依赖和交互接口。边界画错了,以后拆微服务的成本,够你雇一个团队。
Monolith(单体架构):所有组件打包成一个部署单元。项目前期开发最快,爽到飞起;规模上来后容易变成"大泥球",改一行代码等三分钟编译。
Modular Monolith(模块化单体):还是一个部署单元,但内部严格按业务模块划边界,模块间只通过接口通信。兼顾单体的简单和微服务的清晰------中小团队的最优解,以后想拆也留好了口子。
Microservices(微服务):拆成一组小服务,独立部署、独立伸缩。独立部署、故障隔离、团队自治都好,但分布式复杂度教你做人:网络延迟、数据一致性、运维成本,一个都跑不掉。康威定律诚不欺我------服务边界没跟团队边界对齐,早晚打架。
Event-Driven Architecture(EDA):组件响应事件,发布者不关心谁接收。松耦合、可伸缩、一对多扇出。代价是难调试、引入最终一致性------出了 bug 你要沿着事件流往回找,比考古还难。
Client-Server(客户端-服务器):客户端发请求,服务器响应。最基础的分布式交互模型,简单可预测,REST 就是它的标准姿势。
Progressive Disclosure(渐进式披露):本来是 UX 术语------先展示核心信息,按需加载细节。到了 LLM 工程里,就是按需给 AI 注入知识,别一次性全量倒给它。省 token 还能避免上下文干扰,就像聊天别一上来把家谱背完,问谁再提谁。
Pipeline(流水线):任务拆成串行阶段,每阶段的输出是下阶段的输入。CI/CD、多 Agent 工作流的基础。就像流水线工人,每人只拧一颗螺丝,效率高还责任明确。
CQRS(命令查询职责分离):写操作和读操作用不同模型实现。读写性能可以分别优化,适合读写比悬殊的场景。代价是复杂度上去了------毕竟你要维护两套模型。
Event Sourcing(事件溯源):不存当前状态,存所有变更事件,要状态就重放。完整审计轨迹、可回溯任意时间点。金融、账务系统的最爱,因为"谁改的、什么时候改的、改了几次"全都赖不掉。
Conway's Law(康威定律):系统结构会复制组织的沟通结构。公司沟通混乱,代码就耦合混乱。所以设计架构之前,先设计团队结构。
2.2 设计模式:23 种套路,程序员的武功秘籍
Singleton(单例模式):保证全局只有一个实例。适合配置、连接池这种共享唯一资源。但滥用就成了"全局变量的马甲",还难测试。现代工程一般交给 DI 容器管。
Factory Method(工厂方法):定义创建对象的接口,让子类决定实例化谁。把"创建"和"使用"解耦,符合开闭原则。
Abstract Factory(抽象工厂):创建一系列相关对象的接口,不用指定具体类。跨平台、多产品族场景的标配。
Builder(建造者):把复杂对象的构建和表示分开,分步构建。专治长参数列表------六个参数传进去,谁记得住顺序?Builder 就是填表式创建对象。
Observer(观察者):一对多依赖,被观察对象一变,所有观察者自动收到通知。前端 Hooks、事件总线都是这个思想。本质就是:关注了就别错过。
Strategy(策略模式):定义一族可互换的算法,封装起来互相替换。运行期切换行为不用改调用方。专治 if-else 山:
java
// 改之前:if-else 山,越叠越高
double discount;
if (user.isVip()) { discount = 0.9; }
else if (user.isNew()) { discount = 0.85; }
else { discount = 1.0; }
// 改之后:每种策略一个类,调用方无感知
DiscountStrategy strategy = strategyFactory.get(user);
double discount = strategy.calculate(user);
Adapter(适配器):把 A 的接口转成 B 想要的样子。不修改已有代码就能复用能力。就像插座转换头------美国的电器插中国的孔,中间加个转接头就行。
Decorator(装饰器):动态给对象加职责,不改变结构。避免子类爆炸------与其给每种组合建一个子类,不如一层层往上包。Java IO 流、Python 装饰器都是它。
Facade(外观):给复杂子系统一个简单的门面接口。客户端耦合降了,内部复杂度藏起来了。就像酒店前台------你只需要说"我要退房",不用自己去关水电。
Proxy(代理):给对象套个壳控制访问。延迟加载、权限控制、日志增强都靠它。AOP、RPC 客户端都是代理的亲戚。
Chain of Responsibility(责任链):处理者连成一条链,请求沿链传递直到有人接单。中间件、拦截器都是它。就像报销流程:组长签完财务签,财务签完老板签,谁卡住谁知道。
Template Method(模板方法):父类定好算法骨架,子类只填差异部分。复用公共流程,子类只改自己那一段。就像公司培训:流程都一样,就是讲师不同。
State Pattern(状态模式):把每个状态封装成类,状态一变,行为就变。消除大量状态判断 if-else。比"一个字段加一串 if"优雅得多。
Command(命令模式):把请求封装成对象,可以排队、撤销、记日志。操作撤销、任务队列、宏命令都是它。Ctrl+Z 的底层逻辑。
Repository(仓储模式):领域层和数据层之间的中介,封装数据访问逻辑。隔离数据库细节、集中查询逻辑、方便 mock 测试。
Unit of Work(工作单元):跟踪一个业务事务里所有对象变更,一次性提交。保证事务一致性,避免一次业务操作发 N 次数据库请求。
DI(依赖注入):依赖从外部传进来,而不是对象内部自己 new。松耦合、可测试,现代框架的核心能力。自己 new 依赖,等于自己给自己挖坑。
IoC(控制反转):流程控制权交给框架。"Don't call us, we'll call you"------别找我,我会找你。这不就是甲方吗?框架就是那个甲方,你只管实现它要的接口。DI 是它最常见的落地方式。
2.3 面向对象与模块化:三件套与全家桶
Module(模块):高内聚、对外暴露清晰接口的程序单元。能独立开发、独立测试、独立替换------比团队里某些同事还独立。
Package(包):相关模块的集合 + 命名空间容器。逻辑分组、防命名冲突、控制可见性。包就是装代码的抽屉,抽屉不分类的话,找东西全靠缘分。
Namespace(命名空间):名字的容器,隔离标识符。大型系统里,防止你家的 User 和我家的 User 打架。
Abstraction(抽象):只暴露必要特征,隐藏无关细节。让你聚焦本质------开车不用知道发动机怎么转,踩油门就行。
Encapsulation(封装):数据 + 操作数据的方法绑成类,内部状态藏起来。内部随便改,外部无感------这才是真正的闷声发大财。
Inheritance(继承):子类继承父类,代码复用。OO 三大特性之一,但强耦合,现代工程优先用组合。毕竟你亲爹只有一个。
Polymorphism(多态):不同类型对同一接口呈现不同行为。面向抽象编程,符合开闭原则。加新类型不用改老代码。
Cross-cutting Concerns(横切关注点):鉴权、日志、限流、事务这种横跨所有模块的通用功能。不用中间件/AOP 集中处理,就会散落在业务代码里,重复到怀疑人生。
Value Object(值对象):用属性值定义身份、不可变的对象,比如金额、日期。专治基本类型偏执,还能封装业务校验,天生线程安全。
Aggregate(聚合):DDD 概念,一组相关对象的集合,作为数据修改的单元。聚合内强一致,聚合间最终一致。
2.4 分层对象模型:POJO、DTO、VO 全家桶
POJO(Plain Old Java Object):普通 Java 对象,没继承特殊类、没实现特殊接口。简单、可测试,所有分层对象的基础------全村人的希望。
PO(Persistent Object,持久化对象):和数据库表一一对应,DAO 层用。隔离数据库表结构和业务层。
DTO(Data Transfer Object,数据传输对象):跨进程/跨层传数据的对象,只有字段没有业务逻辑。减少接口调用次数,隔离内部领域模型。
VO(View Object,视图对象):展示层对象,对应前端页面要的数据。封装展示数据,不暴露内部字段------前端要什么你给什么,别把家底都端出去。
BO(Business Object,业务对象):业务层对象,封装业务逻辑,可以组合多个 PO。就像拼积木,业务逻辑往上搭。
DAO(Data Access Object,数据访问对象):数据访问层对象,提供 CRUD 接口。隔离业务层和数据库实现,换库如换衣服。
2.5 数据与状态:缓存三兄弟的恩怨情仇
State Management(状态管理):应用状态的存储、更新、订阅机制。客户端状态(UI 本地态)和服务端状态(API 数据)混着用,是前端最常见的架构错误------就像把钱包和垃圾桶放一个抽屉,找钱的时候总想报警。
Caching(缓存):把昂贵计算/查询结果暂存到高速存储。性能优化头号手段。代价是一致性和失效问题------缓存一时爽,失效火葬场。
TTL(Time-To-Live):存活时间,过期自动失效。实现最终一致性的简单机制,也是缓存们集体下班的闹钟。
Cache Invalidation(缓存失效):数据变更后更新或删除对应缓存。缓存正确性完全取决于失效策略------这是缓存里最难的部分。计算机科学里只有两件难事:缓存失效和命名。
Cache Penetration(缓存穿透):查不存在的数据,请求直接打到数据库。恶意攻击能拖垮数据库。布隆过滤器或空值缓存安排上。
Cache Avalanche(缓存雪崩):大量缓存同时过期,所有请求瞬间打向数据库,压力骤增甚至宕机。解法简单粗暴:过期时间加随机值,错峰下班。
Idempotency(幂等性):同一操作执行 N 次,结果和执行一次一样。网络不可靠,幂等让重试无害。支付、Webhook 必须保证幂等------不然用户点两下支付你扣两次钱,客服电话会被打爆。
Race Condition(竞态条件):并发访问共享资源且无同步,结果取决于执行时序。产生随机 bug 且极难复现------"我这儿没事啊"是它的经典台词。
Deadlock(死锁):两个线程互等对方手里的资源,永久阻塞。必须同时满足互斥、持有并等待、不可剥夺、循环等待四个条件。就像你和室友都在等对方去关灯------你俩能等一晚上。
Dead Letter Queue(死信队列):多次消费失败的消息进专用队列。防止坏消息无限重试阻塞主队列,便于事后排查------坏消息隔离区。
Transaction(事务):多步读写捆绑成原子单元,满足 ACID。保证多步操作的数据一致性。跨服务事务用 Saga,牺牲强一致性换可用性。
Optimistic Locking(乐观锁):假设冲突很少,更新时查版本号,冲突就重试。无锁、性能高,适合读多写少。
Pessimistic Locking(悲观锁):假设冲突很多,操作前先加锁。强一致,适合写多冲突多,但性能低。
三、并发与多线程:一个人干八个人的活
Thread(线程):操作系统调度的最小执行单元,一个进程多个线程。并发执行任务,提升 CPU 利用率。线程就是车间工人,多线程是让一群人同时拧螺丝,前提是别抢同一把扳手。
Process(进程):资源分配的最小单位,独立内存空间,进程间不共享内存。进程隔离保证一个崩了不影响其他------谁都不愿意被同事的 bug 连坐。
Coroutine(协程):用户态轻量级线程,程序自己调度,不依赖内核。创建切换成本极低,轻松上万个。现代异步编程的主流方案------线程是开一间房,协程是在房里加床。
Context Switch(上下文切换):CPU 切线程时保存/恢复寄存器状态。线程太多,时间全花在切换上,反而更慢------这就是为什么"开 10000 个线程"听起来很猛,跑起来很拉。
Mutex / Lock(互斥锁):同一时间只有一个线程访问共享资源。解决竞态,但用不好就是死锁制造机。
Thread Safety(线程安全):多线程环境下代码能正确执行。并发代码的核心质量要求。线程不安全的代码,就像没人看管的公共冰箱------你永远不知道里面剩什么。
Synchronized(同步):保证一段代码同一时间只有一个线程执行。最基础的线程安全手段,但性能一般。就像浴室门口挂"使用中"的牌子,好用,但大家都得排队。
Volatile:保证可见性和有序性,不保证原子性。一个线程写、多个线程读的场景,不用加锁。
Atomic Operation(原子操作):不可中断的操作,要么全完成要么全不完成。无锁实现线程安全,性能高于加锁。
Thread Pool(线程池):预创建一组线程复用执行任务,避免频繁创建销毁。降低创建开销、控制并发数、防止资源耗尽。线程池就是公司工位,线程是员工,复用才是王道。
Future / Promise:异步计算结果的占位符,以后能拿到结果。就像快递单号------货还没到,但你已经有单号了。
Async / Await:用同步的写法写异步代码。避免回调地狱,可读性接近同步代码。读起来像顺叙,跑起来是插叙。
Memory Barrier(内存屏障):保证指令可见性和有序性的机制。理解 Java/Kotlin 内存模型、volatile 原理的基础。相当于给乱跑的指令挂块"请勿打扰"的牌子。
Happens-Before:Java 内存模型的偏序规则,保证一个操作的结果对另一个操作可见。判断并发代码对不对,就靠它了。
四、数据结构与算法:面试官的快乐源泉
Array(数组):连续内存的同类型元素,随机访问 O(1)。最基础的结构,其他结构的地基。随机访问 O(1),就像按门牌号找人,不用挨家挨户敲门。
Linked List(链表):节点靠指针连,内存不连续。插入删除 O(1),访问 O(n)。数组和链表就像高铁和绿皮车:一个直达快但改道难,一个随意停但慢。
Stack(栈):后进先出。函数调用、表达式求值、括号匹配、DFS 的地基。就像一摞盘子,你只能拿最上面那个。
Queue(队列):先进先出。任务调度、消息队列、BFS 的地基。就像食堂排队,先来的先吃。
Hash Table(哈希表):哈希函数把 key 映射到数组位置,平均查找 O(1)。字典、Map 的底层。性能怪兽,但哈希冲突要处理。
Tree(树):分层非线性结构。文件系统、数据库索引、DOM 都是树------连你家族谱也是,可见树是自然界最成功的结构。
Binary Tree(二叉树):每个节点最多两个子节点。二叉搜索树、平衡树的地基。
BST(二叉搜索树):左子树 < 根 < 右子树,平均查找 O(log n)。有序数据的快速查找,平衡 BST 是 TreeMap/TreeSet 的底层。
Balanced Tree(平衡树):AVL、红黑树,自动保持平衡,避免退化成链表。保证最坏情况 O(log n)。没有平衡,你的 BST 就会从二叉树退化成歪脖子树。
Heap(堆):完全二叉树,大顶堆/小顶堆,取最大/最小 O(1)。优先队列、Top K、堆排序的地基。就像自动售货机,最想要的那个永远在最上面。
Graph(图):顶点和边组成的多对多关系。社交网络、路径规划、依赖关系都能建模。程序员的社交关系也是图------而且大概率是稀疏图。
Trie(前缀树):字符串匹配专用树,公共前缀共享节点。搜索提示、敏感词过滤、IP 路由。查前缀快得离谱。
Bloom Filter(布隆过滤器):空间效率极高的概率型结构,判断"一定不存在"或"可能存在"。解决缓存穿透、海量去重。有极低误判率------它说"可能存在",结果可能没有;它说"一定不存在",那就是真没有。程序界的薛定谔。
LRU Cache(最近最少使用):缓存满时淘汰最久没访问的数据。最常用的淘汰策略。就像整理衣柜:最久没穿的先扔。
Sorting Algorithm(排序算法):冒泡、插入、选择、快排、归并、堆排......最基础的算法。工程里直接用语言内置排序,面试里就全靠演技。
Binary Search(二分查找):有序数组每次折半,O(log n)。有序数据查找的最优算法,思想还能用到各种猜数字场景。
Recursion(递归):函数调用自身,大问题拆小问题。树、图、分治的基础。注意栈溢出和重复计算;尾递归可被编译器优化。
DP(动态规划):大问题拆成重叠子问题,存结果避免重复计算。最优解问题的核心思想,背包、最长公共子序列都靠它。递归是重复算,DP 是算一次存起来。
Greedy(贪心):每步选当前最优,期望全局最优。简单高效,但需要证明贪心选择性质------不然就是自助餐吃法:每步拿最贵的,最后不一定最划算。
Divide and Conquer(分治):拆成子问题递归解决再合并。快排、归并、MapReduce 的核心。
BFS(广度优先):按层遍历,用队列。最短路径、层序遍历的基础。BFS 像地毯式搜索,一层层往外推,不放过任何一个角落。
DFS(深度优先):一条路走到底再回溯,用栈/递归。连通性、拓扑排序、回溯的基础。DFS 像一条道走到黑,撞了南墙才回头。
Backtracking(回溯):试探性搜索,走不通就回头。排列组合、N 皇后、解数独的标准解法。程序员的备胎思维:这个不行,换下一个。
Time Complexity(时间复杂度):运行时间随输入规模增长的趋势。评估算法效率的核心指标。
Space Complexity(空间复杂度):额外内存随输入规模增长的趋势。时间换空间、空间换时间,程序员的以物易物。
Skip List(跳表):多层有序链表,平均 O(log n)。Redis 有序集合的底层实现,比平衡树实现简单。
Bitmap(位图):用 bit 表示数据是否存在,极省空间。海量去重、签到、布隆过滤器的基础。一个字节能装 8 个"在不在",比记事本省纸多了。
Sliding Window(滑动窗口):双指针维护一个窗口在数组/字符串上滑动。解决子串、子数组问题的经典思路,O(n)。
Two Pointers(双指针):两个指针相向或同向移动。有序数组两数之和、去重、链表环检测的最优解。左右互搏,程序员的双手剑法。
最后说点实在的。术语这东西,背的时候像背八股,用的时候才知道是保命。你跟 AI 说"用策略模式把这坨 if-else 拆了",它刷刷给你列方案;你说"把那个好多判断的东西弄好看点",它回你一个"好的,已优化"------然后你打开代码一看,它给你加了两个注释。
行话不是用来装的,是用来对齐的。脑子里有什么概念,嘴上就说得出来,AI 才接得住你的活。程序员版的"名不正则言不顺",诚不欺我。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/H1727548