本文默认读者已掌握 Java 基础语法与集合框架,JDK 以 8 为主,同时标注 JDK 15 之后偏向锁被废弃带来的变化。文中代码均可直接运行,建议边读边跑,尤其是第三节 i++ 复现实验与第七节死锁实验——只有亲眼看到”结果不等于 20000”和”jstack 打印出 deadlock”,抽象的内存模型概念才会落地。
一、为什么要并发:从摩尔定律失效说起过去二十年程序员享受过一段”免费的午餐”:CPU 主频从几百 MHz 涨到几 GHz,同样的代码不改一行,明年就自动变快。但 2005 年前后这条曲线断了——受制于功耗墙与散热极限,单核主频停滞在 4GHz 附近,厂商转而把晶体管堆成多核。于是”免费午餐”结束:想变快,就得自己写并发。
并发(Concurrency)与并行(Parallelism)经常被混用,但层次不同。并发指”逻辑上同时处理多件事”,强调结构与调度,单核上靠时间片轮转也能实现;并行指”物理上真正同时执行多条指令”,必须依赖多核。可以说并行是并发在硬件上的一种执行形态,是并发的子集。
维度
并发 Concurrency
并行 Parallelism
...
Vue 用 Proxy 把”数据变了就更新视图”做成响应式,React 则相反:它几乎不追踪依赖,而是靠 虚拟 DOM + Diff + 可中断调度 来决定”这次该更新哪”。本文把 React 的任督二脉——协调(Reconciliation)与 Fiber——彻底拆开,再看 Hooks 怎么把状态与副作用组织得既灵活又容易踩坑。
一、为什么是 React:声明式与组件化传统 jQuery 时代,我们手写”查到 DOM → 改样式 → 绑事件”的命令式代码,状态一多就变成难以维护的”面条”。React 的核心承诺是:
声明式:你描述”UI 应该长什么样”(基于当前 state),而不是”一步步怎么改”。
组件化:把界面拆成可复用、自包含的函数/类,数据自顶向下流动。
1234function Counter() { const [count, setCount] = React.useState(0); return <button onClick={() => setCount(count + 1)}>点了 {cou ...
本篇属于「知其然更知其所以然」的一章。反射、注解、动态代理这三样东西,日常写业务代码几乎碰不到,但只要你点开 Spring 的 AutowiredAnnotationBeanPostProcessor、MyBatis 的 MapperProxy、Jackson 的 BeanDeserializer,就会发现它们无处不在。理解这三块基石,你读框架源码时就不会再迷路,也能在真正需要”通用能力”的场景里写出靠谱的底层工具,而不是把反射当成万能胶乱抹一气。阅读建议:跟着代码敲一遍,尤其是第六节的 DI 容器和第七节的代理切面,跑通之后很多面试题会自然解开。
写在前面:为什么这三个东西要一起讲很多教材把反射、注解、动态代理拆成三章,结果读者学完只记住了三套 API,串不起来。实际上它们是一条流水线上的三道工序:注解负责”打标记”(声明元数据),反射负责”读标记 + 执行动作”(运行期解析与调用),动态代理负责”把动作织入调用过程”(无侵入增强)。
Spring 里 @Transactional 就是一个典型闭环:你在方法上打一个注解(注解),Spring 容器启动时用反射扫描到它并判读事务属性 ...
很多人的 Java IO 学习止步于”复制粘贴一段 try-catch 模板”:能读文件、能写文件,但说不清 new BufferedReader(new InputStreamReader(new FileInputStream(f), UTF_8)) 为什么要套三层;遇到中文乱码只会全局替换成 UTF-8;听说 NIO 快却不知道快在哪;背过 Buffer 的 flip 却总在读写切换时踩坑。根本原因在于:IO 从来不是纯 Java 的语法问题,它是应用程序、JVM、操作系统内核、磁盘与网卡四者协同的结果。不把内核态/用户态、系统调用、数据拷贝这些底层环节打通,上层 API 就永远是玄学。本篇的目标就是把这条链路从硬件一路讲到 API。
一、IO 基础概念:流、阻塞模型与内核/用户态的数据旅程1.1 什么是”流”流(Stream)是对有序、连续、有方向的数据序列的抽象。它有三个关键特征:
有序:先写入的字节先被读出,不存在”跳到中间读第 100 个字节”这种随机访问语义(RandomAccessFile 与 Channel 是例外)。
连续:数据像水流一样从一端流向另一端,读写 ...
上一篇我们把 List 与 Set 的底层结构、扩容机制、fail-fast 迭代器讲透了。本篇进入 Map 家族——这是 Java 面试中出现频率最高、源码最值得逐行精读的一块。全文代码均在 JDK 8 与 JDK 17 上实测通过,涉及版本差异的地方会明确标注。建议把文中的源码片段对照你本地 rt.jar / src.zip 里的 java.util.HashMap 一起读,尤其是 putVal、resize、treeifyBin 这三段,读完你会明白”为什么容量必须是 2 的幂””为什么树化阈值是 8”这些结论根本不需要死记硬背。另外文中会出现大量位运算,看不懂的地方先把二进制写出来。
一、Map 家族全景1.1 八种 Map 实现的语义与适用场景Map 不是 Collection 的子接口,它是独立的顶层接口,存储的是”键到值”的映射(key-value mapping)。JDK 提供了八种常用实现,它们的核心差异体现在四个方面:是否有序、是否允许 null、底层数据结构、是否线程安全。
实现类
底层结构
是否有序
key/value 可否为 null
线程安全
时间 ...
Hive 不是数据库,而是 “把 SQL 翻译成分布式作业”的引擎。它的存储在 HDFS、元数据在 Metastore、计算可跑在 MapReduce/Tez/Spark 上。理解这一点,才能用对它——它擅长的是 离线、大批量、schema-on-read 的 ETL 与报表,而不是低延迟的点查(那是 HBase、ClickHouse 的活)。
一、为什么需要 Hive在 Hive 出现之前,要统计 HDFS 上一天的日志 PV,你得手写一个 MapReduce:Mapper 切字段、Reducer 做聚合,几十行样板代码只为一条 SELECT count(*)。更严重的是——业务逻辑全锁死在 Java 代码里,改个口径要重新编译上线,数据团队没法自助分析。
Hive 的解法是:给你一套 类 SQL 语言(HQL),你写 SQL,它帮你翻译成 MapReduce/Tez/Spark 作业去 HDFS 上跑。工程师从”写代码”退化回”写查询”,效率数量级提升。
一句话定位:Hive = SQL 接口 + 元数据管理 + 将 HQL 编译为分布式作业的执行层。它是 Hadoop 生态里” ...
几乎每个 Java 程序员每天都在用 ArrayList 和 HashSet,但下面这些问题能答到源码层面的人并不多:为什么 foreach 循环里调用 list.remove() 会抛 ConcurrentModificationException,而 iterator.remove() 却不会?ArrayList 扩容到底是 1.5 倍还是 2 倍?Arrays.asList() 返回的 List 为什么不能 add?HashSet 里到底存的是什么?PriorityQueue 凭什么能 O(n log k) 求出 TopK?本文把 List、Set、Queue 三大族从接口契约一路拆到 JDK 源码,再把迭代器的 fail-fast 机制彻底讲透。
一、集合框架全景:两大体系与接口契约1.1 Collection 与 Map:两条并行的线java.util 包下的集合框架(Collections Framework)诞生于 JDK 1.2,用来取代 Vector、Hashtable、Enumeration 这些早期遗留类。它的第一层切分非常关键:Collection 是单列数据 ...
大部分人写泛型只停留在 List<String> 这一层,一旦 IDE 报出 capture of ?、name clash: ... have the same erasure、unchecked cast 就只能靠试。本文的目标是把这层窗户纸彻底捅破:擦除到底发生在哪一步、擦完之后字节码里还剩下什么、桥接方法是谁塞进去的、为什么 List<? extends T> 读得写不得。读完你应该能徒手画出泛型在编译期与运行期之间的那道分界线。
一、为什么需要泛型:Object 时代的三大问题1.1 JDK 5 之前:一切皆 Object2004 年 JDK 5 发布之前,Java 的集合框架只有一条路可走:容器内部统一用 Object 存储,取出来由调用方自己强转。下面这段代码在 JDK 1.4 时代是标准写法:
12345678// JDK 1.4 写法:原始类型(raw type),编译器只知道里面装的是 ObjectList names = new ArrayList();names.add("张三");names.add("李 ...
前端早已不只是”切图”。用户最先接触的页面就是攻击面:一处 innerHTML 用错、一个 Cookie 少写了 HttpOnly,就可能把会话凭据、甚至整个账号交到攻击者手里。本文从同源策略出发,把 XSS、CSRF、点击劫持 三大高频漏洞的成因、利用链与防御一次讲透,并附上可直接落地的 CSP 配置与速查清单。
一、先看懂浏览器同源策略同源 = 协议 + 域名 + 端口,三者完全一致。这是浏览器安全的基石,它的核心作用是:限制一个源的脚本读取另一个源的资源。
操作
是否受同源策略限制
说明
发送跨域请求(表单 / img / script)
否
可以”发出”,只是默认读不到响应
读取跨域 AJAX 响应
是
需目标站 CORS 显式放行
读取跨域 iframe 的 DOM
是
需同源或 postMessage
设置/读取跨域 Cookie
受限
受 SameSite、Domain、Secure 约束
关键认知:同源策略拦截”读取”,不拦截”发送”。正因为如此,<img src="https://bank.com/transfer ...
几乎每个 Java 程序员都写过 try-catch,但能把下面几个问题答清楚的人并不多:finally 里写 return 会发生什么?try-with-resources 到底编译成了什么?为什么高并发场景下有人要重写 fillInStackTrace?catch 住 OutOfMemoryError 之后程序还能活吗?本文从语言规范一路挖到字节码,把这一整套机制讲透。
一、异常体系全景:从 Throwable 说起1.1 家族树与两条语义分界线Java 的异常世界只有一个根:java.lang.Throwable。它往下分出两支——Error 与 Exception,Exception 再往下分出一支特殊的 RuntimeException。这棵树的形状非常简单,但它背后暗含了两条完全不同的语义分界线。
1234567891011121314Throwable ├── Error (虚拟机级故障,不可恢复) │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── No ...













