From 6a9188990a97b1f2b6db4a412a3261ea595856de Mon Sep 17 00:00:00 2001 From: xking Date: Wed, 3 Dec 2025 13:18:45 +0800 Subject: [PATCH] update --- java/包装模式之java对象.md | 117 ++++++++++++++++++++++++++++++ java/深浅拷贝.md | 4 + kms.md | 20 +++++ 3 files changed, 141 insertions(+) create mode 100644 java/包装模式之java对象.md create mode 100644 java/深浅拷贝.md create mode 100644 kms.md diff --git a/java/包装模式之java对象.md b/java/包装模式之java对象.md new file mode 100644 index 0000000..4a48c85 --- /dev/null +++ b/java/包装模式之java对象.md @@ -0,0 +1,117 @@ +## 当引用第三方包的java对象时重写equals + +使用包装模式 +``` + + private static class VideoInfoWrapper { + + private final VideoInfo videoInfo; + + public VideoInfoWrapper(VideoInfo videoInfo) { + this.videoInfo = videoInfo; + } + + public VideoInfo unwrap() { + return videoInfo; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VideoInfo)) { + return false; + } + VideoInfo vi = (VideoInfo) obj; + return videoInfo.id.equals(vi.id) + && videoInfo.width == vi.width + && videoInfo.height == vi.height; + } + + @Override + public int hashCode() { + int n = 31; + n = n * 31 + videoInfo.id.hashCode(); + n = n * 31 + videoInfo.height; + n = n * 31 + videoInfo.width; + return n; + } + } +``` + + + + + +自定义过滤函数实现去重 + + + +``` +private static Predicate distinctByKey(Function keyExtractor) { + Map map = new ConcurrentHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } +``` + +要理解 `distinctByKey` 方法中 `map` 的生命周期及其被捕获的原因,需要结合 Java 的**变量作用域**、**lambda 表达式的闭包特性**以及**流的执行机制**来分析。 + +### 一、`map` 的生命周期细节 + +`map` 是 `distinctByKey` 方法内部创建的局部变量,其生命周期可分为以下阶段: + +#### 1. **创建阶段:方法调用时初始化** + +当调用 `distinctByKey(keyExtractor)` 方法时,方法体首先执行 `new ConcurrentHashMap<>()`,此时 `map` 被创建并初始化(分配内存、初始化哈希表等)。 + +此时 `map` 的作用域仅限于 `distinctByKey` 方法内部,是一个**局部变量**。 + +#### 2. **存活阶段:被 lambda 捕获,随流处理存活** + +方法返回的 `Predicate` 是一个 lambda 表达式:`t -> map.putIfAbsent(...)`。 + +这个 lambda 表达式**捕获了方法内部的 `map` 变量**(即持有对 `map` 的引用),因此即使 `distinctByKey` 方法执行结束,`map` 也不会被立即销毁 —— 因为它被外部的 `Predicate` 对象引用着。 + +当这个 `Predicate` 被传递给流的 `filter` 操作(如 `stream.filter(distinctByKey(...))`)时: + +- 流的中间操作(如 `filter`)仅会保存 `Predicate` 对象,不会立即执行。 +- 直到流的**终端操作**(如 `collect`、`forEach`)被调用时,流才开始处理元素,此时 `Predicate.test(t)` 会被反复调用,每次调用都会通过捕获的引用操作同一个 `map`(记录已出现的键,实现去重)。 + +因此,`map` 在整个流处理期间(从终端操作开始到结束)一直存活,用于存储去重的状态。 + +#### 3. **销毁阶段:引用链断裂后被回收** + +当流的终端操作执行完毕,流处理结束后: + +- 如果 `Predicate` 对象没有被其他地方引用(通常是一次性使用,用完即弃),则 `Predicate` 会成为垃圾回收(GC)的候选对象。 +- 由于 `map` 仅被这个 `Predicate` 引用,当 `Predicate` 被 GC 回收时,`map` 的引用计数变为 0,也会被 GC 回收。 + +至此,`map` 的生命周期结束。 + +### 二、为什么 `map` 会被 lambda 捕获? + +lambda 表达式(如 `Predicate` 的实现)本质是**闭包(Closure)**—— 它可以访问外部作用域(即 `distinctByKey` 方法)中的变量,这种访问通过 “捕获变量” 实现。具体原因如下: + +#### 1. 功能需求:需要共享状态 + +`distinctByKey` 的核心逻辑是 “通过记录已出现的键实现去重”,因此需要一个共享的容器(`map`)来存储这些键。 + +如果 `map` 不被 lambda 捕获,那么每次调用 `Predicate.test(t)` 时都会创建新的 `map`,无法记录历史状态,去重功能就会失效。 + +#### 2. Java 语法对闭包的支持 + +Java 的 lambda 表达式可以捕获外部作用域的变量,但有严格限制:**只能捕获 “有效最终变量(effectively final)”**(即变量声明后从未被修改,或被 `final` 修饰)。 + +在 `distinctByKey` 中: + +- `map` 被声明为 `Map map = new ConcurrentHashMap<>()`,且后续从未被重新赋值(始终指向同一个 `ConcurrentHashMap` 实例),因此它是 “有效最终变量”。 +- 因此,lambda 表达式可以合法地捕获 `map` 的引用(注意:捕获的是引用,不是变量本身),并在后续操作中通过该引用修改 `map` 内部的内容(如 `putIfAbsent`)。 + +#### 3. 捕获的本质:延长变量生命周期 + +从内存角度看,`map` 原本是 `distinctByKey` 方法的局部变量,方法执行结束后本应被销毁。但由于 lambda 表达式(`Predicate`)持有对 `map` 的引用,`map` 的生命周期被延长,与 `Predicate` 的生命周期绑定 —— 只要 `Predicate` 还存在,`map` 就不会被回收。 + +### 三、关键结论 + +- **`map` 的生命周期**:从 `distinctByKey` 方法调用时创建,到流处理结束且 `Predicate` 被回收时销毁,贯穿整个流的去重过程。 +- **被捕获的原因**:lambda 表达式需要通过共享 `map` 记录去重状态,而 Java 允许 lambda 捕获 “有效最终变量”,从而实现闭包对外部状态的访问。 + +这种设计使得 `distinctByKey` 方法能简洁地实现 “按键去重”,但需注意:同一个 `Predicate` 实例不能重复用于多个流,否则 `map` 会累积多个流的状态,导致逻辑错误。 diff --git a/java/深浅拷贝.md b/java/深浅拷贝.md new file mode 100644 index 0000000..cd0f50c --- /dev/null +++ b/java/深浅拷贝.md @@ -0,0 +1,4 @@ +Apache BeanUtils还是Spring BeanUtils MapStruct m默认都是浅拷贝 + + +java 8大基础数据类型的包装类都是不可变,不受深浅拷贝影响 \ No newline at end of file diff --git a/kms.md b/kms.md new file mode 100644 index 0000000..ea4fe8a --- /dev/null +++ b/kms.md @@ -0,0 +1,20 @@ +slmgr /ipk TVRH6-WHNXV-R9WG3-9XRFY-MY832 + +slmgr /skms kms.03k.org + +slmgr /ato + +slmgr /xpr + +slmgr /dlv + + + + + +2025 标准版 +TVRH6-WHNXV-R9WG3-9XRFY-MY832 + + + +https://learn.microsoft.com/zh-cn/windows-server/get-started/kms-client-activation-keys?tabs=server2025%2Cwindows1110ltsc%2Cversion1803%2Cwindows81#generic-volume-license-keys-gvlk \ No newline at end of file