未来的爱已失忆 發表於 2025-9-5 11:18:00

Java 8 终于要被淘汰了!带你速通 Java 8~24 新特性 | 又能跟面试官吹牛皮了

<p class="md-end-block md-heading"><span class="md-plain">Java 8 终于要被淘汰了!</span></p>
<p class="md-end-block md-p"><span class="md-plain">记得我从大一开始学的就是 Java 8,当时还叫做新特性;后来 Java 11 出了,我用 Java 8;Java 17 出了,我用 Java 8;Java 21 出了,我还用 Java 8。</span></p>
<p class="md-end-block md-p"><span class="md-pair-s"><strong>随你怎么更新,我用 Java 8!</strong></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966310189-c3222d19-1a91-4d63-95f7-a8e5a6457653.png"><img src="https://pic.yupi.icu/1/1756966310189-c3222d19-1a91-4d63-95f7-a8e5a6457653.png"></span></p>
<p class="md-end-block md-p"><span class="md-plain">我之前带大家做项目的时候,还是强烈建议大家用 Java 8 的,为什么现在说 Java 8 要被淘汰了呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">在我看来主要是因为业务和生态变了,尤其是这几年 AI 发展,很多老项目都要接入 AI、新项目直接面向 AI 开发,为了追求开发效率,我们要用 AI 开发框架(比如 Spring AI、LangChain4j),而这些框架要求的版本几乎都是 &gt;= 17, 所以我们团队自己的业务也从 Java 8 迁到 Java 21 了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">另外也是因为有些新版本的 Java 特性确实很香,学会之后无论是开发效率还是性能都能提升一大截。</span></p>
<p class="md-end-block md-p"><span class="md-plain">所以我做了本期干货内容,讲通 Java 8 ~ Java 24 的新特性,洋洋洒洒一万多字!建议收藏,看完后你就约等于学完了十几个 Java 版本~</span></p>
<p class="md-end-block md-p"><span class="md-plain">⭐️ 推荐观看视频版,体验更佳:<span class="md-link md-pair-s">https://bilibili.com/video/BV1haamzUE8m</span></span></p>
<p class="md-end-block md-p"><span class="md-plain">📚 免费 Java 教程 + 新特性大全:<span class="md-link md-pair-s">https://codefather.cn/course/java</span></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966558937-0919fdd6-e86c-487f-b61e-2b83612d7284.png"><img src="https://pic.yupi.icu/1/1756966558937-0919fdd6-e86c-487f-b61e-2b83612d7284.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">⭐️ Java 8</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 8 绝对是 Java 历史上最重要的稳定版本,也是这么多年来最受欢迎的 Java 版本,甚至有专门的书籍来讲解 Java 8。这个版本最大的变化就是引入了函数式编程的概念,给 Java 这门传统的面向对象语言增加了新的玩法。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756725723840-20f339f2-4ff4-4992-80d3-4c2816160070.png"><img src="https://pic.yupi.icu/1/1756725723840-20f339f2-4ff4-4992-80d3-4c2816160070.png"></span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Lambda 表达式</span></h3>
<h4 class="md-end-block md-heading"><span class="md-plain">什么是 Lambda 表达式?</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Lambda 表达式可以说是 Java 8 的杀手级特性。在这个特性出现之前,我们要实现一个简单的回调函数,只能通过匿名内部类的方式,代码又臭又长。</span></p>
<p class="md-end-block md-p"><span class="md-plain">举些例子,比如给按钮添加点击事件、或者创建一个新线程执行操作,必须要自己 new 接口并且编写接口的定义和实现代码。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 8 之前的写法,给按钮添加点击事件<br><span><span class="cm-variable">button.<span class="cm-variable">addActionListener(<span class="cm-keyword">new&nbsp;<span class="cm-variable">ActionListener() {<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">actionPerformed(<span class="cm-variable">ActionEvent&nbsp;<span class="cm-variable">e) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"按钮被点击了");<br><span>&nbsp; }<br><span>});<br><span><span>​<br><span><span class="cm-comment">// 使用线程的传统写法<br><span><span class="cm-variable">Thread&nbsp;<span class="cm-variable">thread&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Thread(<span class="cm-keyword">new&nbsp;<span class="cm-variable">Runnable() {<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">run() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"线程正在运行");<br><span>&nbsp; }<br><span>});</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Lambda 表达式的出现,让代码变得简洁优雅,告别匿名内部类!</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 8 Lambda 写法<br><span><span class="cm-variable">button.<span class="cm-variable">addActionListener(<span class="cm-variable">e&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"按钮被点击了"));<br><span><span class="cm-variable">Thread&nbsp;<span class="cm-variable">thread&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Thread(()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"线程正在运行"));</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Lambda 表达式的语法非常灵活,可以根据参数个数和方法代码的复杂度选择不同的写法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 无参数的 Lambda<br><span><span class="cm-variable">Runnable&nbsp;<span class="cm-variable">r&nbsp;<span class="cm-operator">=&nbsp;()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"Hello Lambda!");<br><span><span>​<br><span><span class="cm-comment">// 单个参数(可以省略括号)<br><span><span class="cm-variable">Consumer<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">printer&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">s);<br><span><span>​<br><span><span class="cm-comment">// 多个参数<br><span><span class="cm-variable">BinaryOperator<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">add&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">b;<br><span><span class="cm-variable">Comparator<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">comparator&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a.<span class="cm-variable">compareTo(<span class="cm-variable">b);<br><span><span>​<br><span><span class="cm-comment">// 复杂的方法体(需要大括号和 return)<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">processor&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">input&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">processed&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">input.<span class="cm-variable">trim().<span class="cm-variable">toLowerCase();<br><span>&nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">processed.<span class="cm-variable">isEmpty()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"空字符串";<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"处理后的字符串:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">processed;<br><span>};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">方法引用</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Lambda 表达式还有一个实用特性叫做 <span class="md-pair-s "><strong>方法引用</strong><span class="md-plain">,可以看作是 Lambda 表达式的一种简写形式。当 Lambda 表达式只是调用一个已存在的方法时,使用方法引用代码会更简洁。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">举个例子:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">names&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"鱼皮",&nbsp;<span class="cm-string">"编程导航",&nbsp;<span class="cm-string">"面试鸭");<br><span><span>​<br><span><span class="cm-comment">// 使用 Lambda 表达式<br><span><span class="cm-variable">names.<span class="cm-variable">forEach(<span class="cm-variable">name&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">name));<br><span><span>​<br><span><span class="cm-comment">// 使用方法引用(更简洁)<br><span><span class="cm-variable">names.<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">实际开发中,方法引用经常用于获取某个 Java 对象的属性。比如使用 MyBatis Plus 来构造数据库查询条件时,经常会看到下面这种代码:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">LambdaQueryWrapper<span class="cm-operator">&lt;<span class="cm-variable">User<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">lambdaQueryWrapper&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">LambdaQueryWrapper<span class="cm-operator">&lt;&gt;();<br><span><span class="cm-variable">lambdaQueryWrapper.<span class="cm-variable">eq(<span class="cm-variable">User::<span class="cm-variable">getName,&nbsp;<span class="cm-string">"鱼皮");</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">方法引用有几种不同的形式,包括静态方法引用、实例方法引用、构造器引用,适用于不同的场景。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 静态方法引用<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">strings&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"1",&nbsp;<span class="cm-string">"2",&nbsp;<span class="cm-string">"3");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">numbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">strings.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable-3">Integer::<span class="cm-variable">parseInt) &nbsp;<span class="cm-comment">// 等于 s -&gt; Integer.parseInt(s)<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-comment">// 实例方法引用<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">words&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"hello",&nbsp;<span class="cm-string">"world",&nbsp;<span class="cm-string">"java");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">upperWords&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">words.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable-3">String::<span class="cm-variable">toUpperCase) &nbsp;<span class="cm-comment">// 等于 s -&gt; s.toUpperCase()<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-comment">// 构造器引用<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">nameList&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"鱼皮",&nbsp;<span class="cm-string">"编程导航",&nbsp;<span class="cm-string">"面试鸭");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable">Person<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">persons&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">nameList.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">Person::<span class="cm-keyword">new) &nbsp;<span class="cm-comment">// 等于 name -&gt; new Person(name)<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】函数式接口</span></h3>
<h4 class="md-end-block md-heading"><span class="md-plain">什么是函数式接口?</span></h4>
<p class="md-end-block md-p"><span class="md-plain">函数式接口是 <span class="md-pair-s "><strong>只有一个抽象方法的接口</strong><span class="md-plain">。要玩转 Lambda 表达式,就必须了解函数式接口,因为 <span class="md-pair-s "><strong>Lambda 表达式的本质是函数式接口的匿名实现</strong><span class="md-plain">。</span></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">展开来说,函数式接口定义了 Lambda 表达式的参数和返回值类型,而 Lambda 表达式提供了这个接口的具体实现。两者相辅相成,让 Java 函数式编程伟大!</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">常用的函数式接口</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Java 8 为我们提供了很多内置的函数式接口,让函数式编程变得简单直观。列举一些常用的函数式接口:</span></p>
<p class="md-end-block md-p"><span class="md-plain">1)Predicate 用于条件判断:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Predicate&lt;T&gt; 用于条件判断<br><span><span class="cm-variable">Predicate<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">isEven&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">%&nbsp;<span class="cm-number">2&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-number">0;<br><span><span class="cm-variable">Predicate<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">isEmpty&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable-3">String::<span class="cm-variable">isEmpty;<br><span><span class="cm-variable">Predicate<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">isNotEmpty&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">isEmpty.<span class="cm-variable">negate(); &nbsp;<span class="cm-comment">// 取反<br><span><span>​<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">numbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-number">1,&nbsp;<span class="cm-number">2,&nbsp;<span class="cm-number">3,&nbsp;<span class="cm-number">4,&nbsp;<span class="cm-number">5,&nbsp;<span class="cm-number">6);<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">evenNumbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">numbers.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">isEven)<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">2)Function 接口用于数据转换,支持函数组合,让代码逻辑更清晰:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Function&lt;T, R&gt; 用于转换<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">stringLength&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable-3">String::<span class="cm-variable">length;<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">Integer,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">intToString&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable-3">Object::<span class="cm-variable">toString;<br><span><span>​<br><span><span class="cm-comment">// 函数组合<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">addPrefix&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"前缀-"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s;<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">addSuffix&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">"-后缀";<br><span><span class="cm-variable">Function<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">combined&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">addPrefix.<span class="cm-variable">andThen(<span class="cm-variable">addSuffix);<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">combined.<span class="cm-variable">apply(<span class="cm-string">"鱼皮");&nbsp;<span class="cm-comment">// "前缀-鱼皮-后缀"</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">3)Consumer 和 Supplier 接口分别用于消费和提供数据:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Consumer&lt;T&gt; 用于消费数据(无返回值)<br><span><span class="cm-variable">Consumer<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">printer&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println;<br><span><span class="cm-variable">Consumer<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">logger&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">log.<span class="cm-variable">info(<span class="cm-string">"处理数据:{}",&nbsp;<span class="cm-variable">s);<br><span><span class="cm-comment">// 组合消费<br><span><span class="cm-variable">Consumer<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">combinedConsumer&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">printer.<span class="cm-variable">andThen(<span class="cm-variable">logger);<br><span><span>​<br><span><span class="cm-comment">// Supplier&lt;T&gt; 用于提供数据<br><span><span class="cm-variable">Supplier<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">randomId&nbsp;<span class="cm-operator">=&nbsp;()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">UUID.<span class="cm-variable">randomUUID().<span class="cm-variable">toString();<br><span><span class="cm-variable">Supplier<span class="cm-operator">&lt;<span class="cm-variable">LocalDateTime<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">now&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime::<span class="cm-variable">now;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">4)BinaryOperator 接口用于二元操作,比如数学运算:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// BinaryOperator&lt;T&gt; 用于二元操作<br><span><span class="cm-variable">BinaryOperator<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">max&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable-3">Integer::<span class="cm-variable">max;<br><span><span class="cm-variable">BinaryOperator<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">concat&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">b;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">自定义函数式接口</span></h4>
<p class="md-end-block md-p"><span class="md-plain">虽然实际开发中,我们更多的是使用 Java 内置的函数式接口,但大家还是要了解一下自定义函数式接口的写法,有个印象。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 创建自定义函数式接口<br><span><span class="cm-meta">@FunctionalInterface<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Calculator&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">calculate(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">a,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">b);<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">使用自定义函数式接口,代码会更简洁:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 使用自定义函数式接口<br><span><span class="cm-variable">Calculator&nbsp;<span class="cm-variable">addition&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">b;<br><span><span class="cm-variable">Calculator&nbsp;<span class="cm-variable">subtraction&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">-&nbsp;<span class="cm-variable">b;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">💡 自定义函数式接口时,需要注意:</span></p>
<p class="md-end-block md-p"><span class="md-plain">1)函数式接口必须是接口类型,不能是类、抽象类或枚举。</span></p>
<p class="md-end-block md-p"><span class="md-plain">2)必须且只能包含一个抽象方法。否则 Lambda 表达式可能无法匹配接口。</span></p>
<p class="md-end-block md-p"><span class="md-plain">3)建议使用 <span class="md-pair-s"><code>@FunctionalInterface</code><span class="md-plain">注解。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">虽然这个注解不是强制的,但加上后编译器会帮你检查是否符合函数式接口的规范(是否只有一个抽象方法),如果不符合会报错。</span></p>
<p class="md-end-block md-p"><span class="md-plain">4)可以包含默认方法 <span class="md-pair-s"><code>default</code><span class="md-plain"> 和静态方法 <span class="md-pair-s"><code>static</code></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">函数式接口允许有多个默认方法和静态方法,因为它们不是抽象方法,不影响单一抽象方法的要求。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 创建自定义函数式接口<br><span><span class="cm-meta">@FunctionalInterface<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Calculator&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">calculate(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">a,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">b);<br><span><span>​<br><span>&nbsp; &nbsp;<span class="cm-comment">// 可以有默认方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">add(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">a,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">b) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">b;<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 可以有静态方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">static&nbsp;<span class="cm-variable">Calculator&nbsp;<span class="cm-variable">multiply() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;(<span class="cm-variable">a,&nbsp;<span class="cm-variable">b)&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">a&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">b;<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Stream API</span></h3>
<h4 class="md-end-block md-heading"><span class="md-plain">什么是 Stream API?</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Stream API 是 Java 8 另一个重量级特性,它让集合处理变得既优雅又高效。(学大数据的同学应该对它不陌生)</span></p>
<p class="md-end-block md-p"><span class="md-plain">在 Stream API 出现之前,我们处理集合数据只能通过传统的循环,需要大量的样板代码。</span></p>
<p class="md-end-block md-p"><span class="md-plain">比如过滤列表中的数据、将小写转为大写并排序:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">words&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"apple",&nbsp;<span class="cm-string">"banana",&nbsp;<span class="cm-string">"cherry");<br><span><span>​<br><span><span class="cm-comment">// 传统的处理方式<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">ArrayList<span class="cm-operator">&lt;&gt;();<br><span><span class="cm-keyword">for&nbsp;(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">word&nbsp;:&nbsp;<span class="cm-variable">words) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">word.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">5) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">upperCase&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">word.<span class="cm-variable">toUpperCase();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result.<span class="cm-variable">add(<span class="cm-variable">upperCase);<br><span>&nbsp; }<br><span>}<br><span><span class="cm-variable">Collections.<span class="cm-variable">sort(<span class="cm-variable">result);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">如果使用 Stream API,可以让同样的逻辑变得更简洁直观:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 使用 Stream 的方式<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">words.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">word&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">word.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">5) &nbsp; &nbsp;<span class="cm-comment">// 过滤长度大于 5 的单词<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable-3">String::<span class="cm-variable">toUpperCase) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 转换为大写<br><span>&nbsp; .<span class="cm-variable">sorted() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 排序<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList()); &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 收集结果</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">这就是 Stream 的作用。Stream 不是数据结构,而是 <span class="md-pair-s "><strong>像工厂流水线</strong><span class="md-plain"> 一样处理数据的工具。数据从一端进入,经历过滤、转换、排序等一系列加工步骤后,最终输出我们想要的结果。这种 <span class="md-pair-s "><strong>链式调用</strong><span class="md-plain"> 让代码读起来就像自然语言一样流畅。</span></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966900888-01e72e42-a304-42a5-9671-2c17cb3edab8.png"><img src="https://pic.yupi.icu/1/1756966900888-01e72e42-a304-42a5-9671-2c17cb3edab8.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">Stream 操作类型</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Stream 的操作分为中间操作和终端操作。中间操作是 “懒惰” 的,只有在遇到终端操作时才会真正执行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">filter 过滤和 map 映射都是中间操作,比如下面这段代码,并不会对列表进行过滤和转换:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">numbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-number">1,&nbsp;<span class="cm-number">2,&nbsp;<span class="cm-number">3,&nbsp;<span class="cm-number">4,&nbsp;<span class="cm-number">5,&nbsp;<span class="cm-number">6,&nbsp;<span class="cm-number">7,&nbsp;<span class="cm-number">8,&nbsp;<span class="cm-number">9,&nbsp;<span class="cm-number">10);<br><span><span>​<br><span><span class="cm-variable">numbers.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">3)&nbsp;<span class="cm-comment">// 中间操作:过滤大于3的数字<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">n) &nbsp; &nbsp;<span class="cm-comment">// 中间操作:平方</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">一些常用的中间操作:</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>filter()</code><span class="md-plain"> - 过滤元素</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>map()</code><span class="md-plain"> - 转换元素</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>sorted()</code><span class="md-plain"> - 排序</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>distinct()</code><span class="md-plain"> - 去重</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>limit()</code><span class="md-plain"> - 限制数量</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>skip()</code><span class="md-plain"> - 跳过元素</span></span></p>
</li>
</ul>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">给上面的代码加上一个终端操作 collect 后,才会真正执行:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">numbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-number">1,&nbsp;<span class="cm-number">2,&nbsp;<span class="cm-number">3,&nbsp;<span class="cm-number">4,&nbsp;<span class="cm-number">5,&nbsp;<span class="cm-number">6,&nbsp;<span class="cm-number">7,&nbsp;<span class="cm-number">8,&nbsp;<span class="cm-number">9,&nbsp;<span class="cm-number">10);<br><span><span>​<br><span><span class="cm-comment">// 演示中间操作和终端操作<br><span><span class="cm-variable">numbers.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">3) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 中间操作:过滤大于3的数字<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">n) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 中间操作:平方<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());&nbsp;<span class="cm-comment">// 终端操作:收集结果</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">一些常用的终端操作:</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>collect()</code><span class="md-plain"> - 收集到集合</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>forEach()</code><span class="md-plain"> - 遍历每个元素</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>count()</code><span class="md-plain"> - 统计数量</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>findFirst()</code><span class="md-plain"> - 查找第一个</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>anyMatch()</code><span class="md-plain"> - 是否有匹配的</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>reduce()</code><span class="md-plain"> - 归约操作</span></span></p>
</li>
</ul>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">实际应用</span></h4>
<p class="md-end-block md-p"><span class="md-plain">分享一些 Stream API 在开发中的典型用例。</span></p>
<p class="md-end-block md-p"><span class="md-plain">1)对列表进行分组(List 转为 Map):</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">Boolean,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&gt;&nbsp;<span class="cm-variable">partitioned&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">numbers.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">3)&nbsp;<span class="cm-comment">// 中间操作:过滤大于3的数字<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">n) &nbsp; &nbsp;<span class="cm-comment">// 中间操作:平方<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">partitioningBy(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">n&nbsp;<span class="cm-operator">%&nbsp;<span class="cm-number">2&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-number">0));&nbsp;<span class="cm-comment">// 终端操作:按奇偶分组</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">2)使用 Stream 内置的统计功能,对数据进行统计:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 统计操作<br><span><span class="cm-variable">IntSummaryStatistics&nbsp;<span class="cm-variable">stats&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">numbers.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">mapToInt(<span class="cm-variable-3">Integer::<span class="cm-variable">intValue)<br><span>&nbsp; .<span class="cm-variable">summaryStatistics();<br><span><span>​<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"数量:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">stats.<span class="cm-variable">getCount());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"总和:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">stats.<span class="cm-variable">getSum());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"平均值:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">stats.<span class="cm-variable">getAverage());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"最大值:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">stats.<span class="cm-variable">getMax());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"最小值:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">stats.<span class="cm-variable">getMin());</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">3)按照对象的某个字段进行分组计算:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable">Person<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">people&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<br><span>&nbsp; &nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Person(<span class="cm-string">"张三",&nbsp;<span class="cm-number">25,&nbsp;<span class="cm-string">"北京"),<br><span>&nbsp; &nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Person(<span class="cm-string">"鱼皮",&nbsp;<span class="cm-number">18,&nbsp;<span class="cm-string">"上海"),<br><span>&nbsp; &nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Person(<span class="cm-string">"李四",&nbsp;<span class="cm-number">25,&nbsp;<span class="cm-string">"北京"),<br><span>&nbsp; &nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Person(<span class="cm-string">"老二",&nbsp;<span class="cm-number">35,&nbsp;<span class="cm-string">"上海")<br><span>);<br><span><span>​<br><span><span class="cm-comment">// 按城市分组<br><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable">Person<span class="cm-operator">&gt;&gt;&nbsp;<span class="cm-variable">byCity&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">people.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">groupingBy(<span class="cm-variable">Person::<span class="cm-variable">getCity));<br><span><span>​<br><span><span class="cm-comment">// 按城市分组并统计年龄<br><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">Double<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">avgAgeByCity&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">people.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">groupingBy(<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Person::<span class="cm-variable">getCity,<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Collectors.<span class="cm-variable">averagingInt(<span class="cm-variable">Person::<span class="cm-variable">getAge)<br><span>&nbsp; ));<br><span><span>​<br><span><span class="cm-comment">// 按城市分组并收集姓名<br><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&gt;&nbsp;<span class="cm-variable">namesByCity&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">people.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">groupingBy(<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Person::<span class="cm-variable">getCity,<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Collectors.<span class="cm-variable">mapping(<span class="cm-variable">Person::<span class="cm-variable">getName,&nbsp;<span class="cm-variable">Collectors.<span class="cm-variable">toList())<br><span>&nbsp; ));</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">学过数据库的同学应该对这种操作并不陌生,其实 SQL 语句中的很多操作都可以通过 Stream 实现。这也是 Stream 的典型应用场景 —— 对数据库中查出的数据进行业务层面的运算。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">并行流</span></h4>
<p class="md-end-block md-p"><span class="md-plain">并行流是 Stream API 的另一个强大特性,它可以自动利用多核 CPU 处理器加速数据处理任务的执行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在此之前,我们要实现并行处理集合数据,需要手动管理线程池和任务分割,代码复杂且容易出错。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但有了 Stream API,一行代码就能创建并行流,比如过滤并计算数据的总和:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">largeList&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">IntStream.<span class="cm-variable">rangeClosed(<span class="cm-number">1,&nbsp;<span class="cm-number">1000000)<br><span>&nbsp; .<span class="cm-variable">boxed()<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-comment">// 并行处理,只需要改一个方法调用<br><span><span class="cm-variable-3">long&nbsp;<span class="cm-variable">parallelCount&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">largeList.<span class="cm-variable">parallelStream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">n&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">isPrime(<span class="cm-variable">n))<br><span>&nbsp; .<span class="cm-variable">count();</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">并行流底层使用了 Fork/Join 框架,简单来说就是把大任务拆分成小任务,分配给多个线程同时执行,最后把结果合并起来。这个过程对开发者完全透明,只需要调用 <span class="md-pair-s"><code>parallelStream()</code><span class="md-plain"> 即可。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966890532-22f63b6c-8e66-4ed6-b766-8f9ca114f96d.png"><img src="https://pic.yupi.icu/1/1756966890532-22f63b6c-8e66-4ed6-b766-8f9ca114f96d.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">但也正因如此,实际开发中,要谨慎使用并行流!</span></p>
<p class="md-end-block md-p"><span class="md-plain">因为它使用的是 JVM 全局的 <span class="md-pair-s"><code>ForkJoinPool.commonPool()</code><span class="md-plain">,默认线程数等于 CPU 核心数减 1。<span class="md-pair-s "><strong>如果某个并行流任务阻塞了线程,会影响其他并行流的性能。</strong></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">而且并行流不一定就更快,特别是对于简单操作或小数据集,切换线程的开销可能超过并行带来的收益。</span></p>
<p class="md-end-block md-p"><span class="md-plain">因此,并行流更适合大数据量、CPU 密集型任务(如复杂计算、图像处理),不适合 I/O 密集型任务(如网络请求)。而且只要涉及到并发场景,就要考虑到线程安全问题。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】Optional</span></h3>
<h4 class="md-end-block md-heading"><span class="md-plain">Optional 的作用</span></h4>
<p class="md-end-block md-p"><span class="md-plain">NullPointerException(NPE)一直是 Java 程序员的噩梦,学 Java 的同学应该都被它折磨过。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756697051451-d2e19a2a-9383-41be-8a34-bb3f5ea93de8.png"><img src="https://pic.yupi.icu/1/1756697051451-d2e19a2a-9383-41be-8a34-bb3f5ea93de8.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">之前,我们只能通过大量的 if 语句检查 null 来避免空指针异常,不仅代码又臭又长,而且稍微不注意就漏掉了。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 传统的空值检查<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-def">getDefaultName(<span class="cm-variable">User&nbsp;<span class="cm-variable">user) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">user&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-atom">null) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">name&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">user.<span class="cm-variable">getName();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">name&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-atom">null&nbsp;<span class="cm-operator">&amp;&amp;&nbsp;<span class="cm-operator">!<span class="cm-variable">name.<span class="cm-variable">isEmpty()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">name.<span class="cm-variable">toUpperCase();<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"unknown";<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Optional 类的引入就是为了优雅地处理可能为空的值,可以先把它理解为 “包装器”,把可能为空的对象封装起来。</span></p>
<p class="md-end-block md-p"><span class="md-plain">创建 Optional 对象:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 创建 Optional 对象<br><span><span class="cm-variable">Optional<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">optional1&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Optional.<span class="cm-variable">of(<span class="cm-string">"Hello"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 不能为 null<br><span><span class="cm-variable">Optional<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">optional2&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Optional.<span class="cm-variable">ofNullable(<span class="cm-variable">getName());&nbsp;<span class="cm-comment">// 可能为 null<br><span><span class="cm-variable">Optional<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">optional3&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Optional.<span class="cm-variable">empty(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 空的 Optional</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Optional 提供了多种处理空值的方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 检查是否有值<br><span><span class="cm-keyword">if&nbsp;(<span class="cm-variable">optional.<span class="cm-variable">isPresent()) {<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">optional.<span class="cm-variable">get());<br><span>}<br><span><span>​<br><span><span class="cm-comment">// 更优雅的方式,如果对象存在则输出<br><span><span class="cm-variable">optional.<span class="cm-variable">ifPresent(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还可以设置默认值策略,比如空值时抛出异常:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 提供默认值<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result1&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">optional.<span class="cm-variable">orElse(<span class="cm-string">"默认值");<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result2&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">optional.<span class="cm-variable">orElseGet(()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">generateDefaultValue());<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result3&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">optional.<span class="cm-variable">orElseThrow(()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalStateException(<span class="cm-string">"值不能为空"));</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">除了前面这些基本方法外,Optional 甚至提供了一套完整的 API 来处理空值场景!</span></p>
<p class="md-end-block md-p"><span class="md-plain">跟 Stream API 类似,你可以对 Optional 封装的数据进行过滤、映射等操作:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">optional<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">s.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">5)<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable-3">String::<span class="cm-variable">toUpperCase)<br><span>&nbsp; .<span class="cm-variable">ifPresentOrElse(<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 有值时执行<br><span>&nbsp; &nbsp; &nbsp; ()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"没有值") &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 无值时执行<br><span>&nbsp; );</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">应用场景</span></h4>
<p class="md-end-block md-p"><span class="md-plain">鱼皮经常使用 Optional 来简化空值判断:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">pageNum&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Optional.<span class="cm-variable">ofNullable(<span class="cm-variable">params.<span class="cm-variable">getPageNum())<br><span>&nbsp; .<span class="cm-variable">orElseThrow(()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">RuntimeException(<span class="cm-string">"pageNum不能为空"));</span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">如果不用 Optional,就要写下面这段代码:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">pageNum;<br><span><span class="cm-keyword">if&nbsp;(<span class="cm-variable">params.<span class="cm-variable">getPageNum()&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-atom">null) {<br><span>&nbsp; &nbsp;<span class="cm-variable">pageNum&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">params.<span class="cm-variable">getPageNum();<br><span>}&nbsp;<span class="cm-keyword">else&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">RuntimeException(<span class="cm-string">"pageNum不能为空");<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">此外,Optional 的一个典型应用场景是在集合中进行安全查找:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">names&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"张三",&nbsp;<span class="cm-atom">null,&nbsp;<span class="cm-string">"李四",&nbsp;<span class="cm-string">"",&nbsp;<span class="cm-string">"王五");<br><span><span>​<br><span><span class="cm-comment">// 使用 Optional 进行安全的查找<br><span><span class="cm-variable">Optional<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">foundName&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">names.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">Objects::<span class="cm-variable">nonNull)<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">name&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">name.<span class="cm-variable">startsWith(<span class="cm-string">"张"))<br><span>&nbsp; .<span class="cm-variable">findFirst();<br><span><span>​<br><span><span class="cm-variable">foundName.<span class="cm-variable">ifPresentOrElse(<br><span>&nbsp; &nbsp;<span class="cm-variable">name&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"找到了:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">name),<br><span>&nbsp; ()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"没有找到匹配的名字")<br><span>);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】新的日期时间 API</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 8 引入的新日期时间 API 解决了旧版 Date 和 Calendar 类的很多问题,比如线程安全、可变性、时区处理等等。</span></p>
<p class="md-end-block md-p"><span class="md-plain">传统的日期处理方式:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 旧版本的复杂日期处理<br><span><span class="cm-variable">Calendar&nbsp;<span class="cm-variable">cal&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Calendar.<span class="cm-variable">getInstance();<br><span><span class="cm-variable">cal.<span class="cm-variable">set(<span class="cm-number">2024,&nbsp;<span class="cm-variable">Calendar.<span class="cm-variable">JANUARY,&nbsp;<span class="cm-number">15);&nbsp;<span class="cm-comment">// 注意月份从0开始<br><span><span class="cm-variable">Date&nbsp;<span class="cm-variable">date&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">cal.<span class="cm-variable">getTime();<br><span><span>​<br><span><span class="cm-variable">SimpleDateFormat&nbsp;<span class="cm-variable">sdf&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">SimpleDateFormat(<span class="cm-string">"yyyy-MM-dd");<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">dateStr&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">sdf.<span class="cm-variable">format(<span class="cm-variable">date);&nbsp;<span class="cm-comment">// 线程不安全</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">使用新的日期时间 API,代码会更简洁:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 当前日期时间<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">today&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">now();&nbsp;<span class="cm-comment">// 2025-09-01<br><span><span class="cm-variable">LocalTime&nbsp;<span class="cm-variable">now&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalTime.<span class="cm-variable">now(); &nbsp;&nbsp;<span class="cm-comment">// 14:30:25.123<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">dateTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">now();&nbsp;<span class="cm-comment">// 2025-09-01T14:30:25.123<br><span><span>​<br><span><span class="cm-comment">// 指定的日期时间<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">specificDate&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">of(<span class="cm-number">2025,&nbsp;<span class="cm-number">09,&nbsp;<span class="cm-number">01);<br><span><span class="cm-variable">LocalTime&nbsp;<span class="cm-variable">specificTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalTime.<span class="cm-variable">of(<span class="cm-number">14,&nbsp;<span class="cm-number">30,&nbsp;<span class="cm-number">0);<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">specificDateTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">of(<span class="cm-number">2025,&nbsp;<span class="cm-number">09,&nbsp;<span class="cm-number">01,&nbsp;<span class="cm-number">14,&nbsp;<span class="cm-number">30,&nbsp;<span class="cm-number">0);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">典型的应用场景是从字符串解析日期,一行代码就能搞定:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 从字符串解析<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">parsedDate&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">parse(<span class="cm-string">"2025-09-01");<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">parsedDateTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">parse(<span class="cm-string">"2025-09-01T14:30:25");<br><span><span>​<br><span><span class="cm-comment">// 自定义格式解析<br><span><span class="cm-variable">DateTimeFormatter&nbsp;<span class="cm-variable">formatter&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">DateTimeFormatter.<span class="cm-variable">ofPattern(<span class="cm-string">"yyyy/MM/dd HH:mm:ss");<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">customParsed&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">parse(<span class="cm-string">"2025/09/01 14:30:25",&nbsp;<span class="cm-variable">formatter);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还有日期和时间的计算,也变得更直观、见名知意:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">today&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">now();<br><span><span>​<br><span><span class="cm-comment">// 基本的日期计算<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">nextWeek&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">today.<span class="cm-variable">plusWeeks(<span class="cm-number">1);<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">lastMonth&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">today.<span class="cm-variable">minusMonths(<span class="cm-number">1);<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">nextYear&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">today.<span class="cm-variable">plusYears(<span class="cm-number">1);<br><span><span>​<br><span><span class="cm-comment">// 时间段计算<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">startDate&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">of(<span class="cm-number">2024,&nbsp;<span class="cm-number">1,&nbsp;<span class="cm-number">28);<br><span><span class="cm-variable">LocalDate&nbsp;<span class="cm-variable">endDate&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDate.<span class="cm-variable">of(<span class="cm-number">2025,&nbsp;<span class="cm-number">9,&nbsp;<span class="cm-number">1);<br><span><span class="cm-variable">Period&nbsp;<span class="cm-variable">period&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Period.<span class="cm-variable">between(<span class="cm-variable">startDate,&nbsp;<span class="cm-variable">endDate);<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"相差 "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">period.<span class="cm-variable">getMonths()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" 个月 "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">period.<span class="cm-variable">getDays()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" 天");<br><span><span>​<br><span><span class="cm-comment">// 精确时间差计算<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">start&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">now();<br><span><span class="cm-variable">LocalDateTime&nbsp;<span class="cm-variable">end&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">LocalDateTime.<span class="cm-variable">of(<span class="cm-number">2025,&nbsp;<span class="cm-number">09,&nbsp;<span class="cm-number">01,&nbsp;<span class="cm-number">14,&nbsp;<span class="cm-number">30,&nbsp;<span class="cm-number">0);<br><span><span class="cm-variable">Duration&nbsp;<span class="cm-variable">duration&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Duration.<span class="cm-variable">between(<span class="cm-variable">start,&nbsp;<span class="cm-variable">end);<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"执行时间:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">duration.<span class="cm-variable">toMillis()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" 毫秒");</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还支持时区处理和时间戳处理,不过这段代码就没必要记了,现在有了 AI,直接让它生成时间日期操作就好。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 带时区的日期时间<br><span><span class="cm-variable">ZonedDateTime&nbsp;<span class="cm-variable">beijingTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">ZonedDateTime.<span class="cm-variable">now(<span class="cm-variable">ZoneId.<span class="cm-variable">of(<span class="cm-string">"Asia/Shanghai"));<br><span><span class="cm-variable">ZonedDateTime&nbsp;<span class="cm-variable">newYorkTime&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">ZonedDateTime.<span class="cm-variable">now(<span class="cm-variable">ZoneId.<span class="cm-variable">of(<span class="cm-string">"America/New_York"));<br><span><span>​<br><span><span class="cm-comment">// 时区转换<br><span><span class="cm-variable">ZonedDateTime&nbsp;<span class="cm-variable">beijingToNewYork&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">beijingTime.<span class="cm-variable">withZoneSameInstant(<span class="cm-variable">ZoneId.<span class="cm-variable">of(<span class="cm-string">"America/New_York"));<br><span><span>​<br><span><span class="cm-comment">// 获取所有可用时区<br><span><span class="cm-variable">ZoneId.<span class="cm-variable">getAvailableZoneIds().<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">zoneId&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">zoneId.<span class="cm-variable">contains(<span class="cm-string">"Shanghai"))<br><span>&nbsp; .<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);<br><span><span>​<br><span><span class="cm-comment">// 时间戳处理<br><span><span class="cm-variable">Instant&nbsp;<span class="cm-variable">instant&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Instant.<span class="cm-variable">now();<br><span><span class="cm-variable-3">long&nbsp;<span class="cm-variable">epochSecond&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">instant.<span class="cm-variable">getEpochSecond();<br><span><span class="cm-variable">ZonedDateTime&nbsp;<span class="cm-variable">fromInstant&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">instant.<span class="cm-variable">atZone(<span class="cm-variable">ZoneId.<span class="cm-variable">systemDefault());</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">总之,有了这套 API,我们不需要使用第三方的时间日期处理库,也能解决大多数问题。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】接口默认方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 8 引入的接口默认方法解决了接口演化的问题。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在默认方法出现之前,如果你想给一个被广泛使用的接口添加新方法,就会影响所有已有的实现类。想象一下,如果要给 Collection 接口添加一个新方法,ArrayList、LinkedList 等所有的实现类都需要修改,成本很大。</span></p>
<p class="md-end-block md-p"><span class="md-plain">默认方法让接口可以在 <span class="md-pair-s "><strong>不破坏现有代码的情况下添加新功能</strong><span class="md-plain">。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">举个例子,如果想要给接口增加一个 <span class="md-pair-s"><code>drawWithBorder</code><span class="md-plain"> 方法:</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Drawable&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 已有抽象方法<br><span>&nbsp; &nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">draw();<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 默认方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">drawWithBorder() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"绘制边框");<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">draw();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"边框绘制完成");<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">使用默认方法后,实现类可以选择重写默认方法,也可以直接使用:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 实现类可以选择重写默认方法<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Circle&nbsp;<span class="cm-keyword">implements&nbsp;<span class="cm-variable">Drawable&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">draw() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"绘制圆形");<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 可以重写默认方法<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">drawWithBorder() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"绘制圆形边框");<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">draw();<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Java 8 为 Collection 接口添加了 stream、removeIf 等方法,都是默认方法:</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756699759296-a4b9d174-c79b-4e56-82f7-fbb1877086c2.png"><img src="https://pic.yupi.icu/1/1756699759296-a4b9d174-c79b-4e56-82f7-fbb1877086c2.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">需要注意的是,如果一个类实现多个接口,并且这些接口有相同的默认方法时,需要显式解决冲突:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">interface&nbsp;<span class="cm-def">A&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">hello() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"Hello from A");<br><span>&nbsp; }<br><span>}<br><span><span>​<br><span><span class="cm-keyword">interface&nbsp;<span class="cm-def">B&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">hello() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"Hello from B");<br><span>&nbsp; }<br><span>}<br><span><span>​<br><span><span class="cm-comment">// 实现类必须重写冲突的方法<br><span><span class="cm-keyword">class&nbsp;<span class="cm-def">C&nbsp;<span class="cm-keyword">implements&nbsp;<span class="cm-variable">A,&nbsp;<span class="cm-variable">B&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">hello() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 可以调用特定接口的默认方法<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">A.<span class="cm-keyword">super.<span class="cm-variable">hello();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">B.<span class="cm-keyword">super.<span class="cm-variable">hello();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 或者提供自己的实现<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"Hello from C");<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">类似的,Java 8 还支持接口的静态方法,前面讲函数式接口的时候有提到。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 9</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】模块系统</span></h3>
<p class="md-end-block md-p"><span class="md-plain">在模块系统出现之前,传统 Java 应用只能依赖 classpath 来管理依赖,所有的类都在同一个类路径下,任何类都可以访问任何其他类,这种 “全局可见性” 在大型项目中会导致代码耦合严重、依赖关系混乱、运行时才发现 ClassNotFoundException 等问题。</span></p>
<p class="md-end-block md-p"><span class="md-plain">模块系统允许我们将代码组织成模块,每个模块都有明确的依赖关系和导出接口,让大型应用的架构变得更加清晰和可维护。</span></p>
<p class="md-end-block md-p"><span class="md-plain">模块系统通过 <span class="md-pair-s"><code>module-info.java</code><span class="md-plain"> 文件来定义模块的边界,明确声明哪些包对外开放,哪些依赖是必需的,这样就形成了强封装的架构。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">比如一个用户管理模块只暴露用户服务接口,而内部的数据访问层对其他模块完全不可见,这种设计让系统的层次结构更加清晰,也避免了意外的跨层调用。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">module&nbsp;<span class="cm-variable">user.<span class="cm-variable">management&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 只导出 service 包,dao 包对外不可见<br><span>&nbsp; &nbsp;<span class="cm-variable">exports&nbsp;<span class="cm-variable">com.<span class="cm-variable">company.<span class="cm-variable">user.<span class="cm-variable">service;<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 依赖其他模块<br><span>&nbsp; &nbsp;<span class="cm-variable">requires&nbsp;<span class="cm-variable">java.<span class="cm-variable">base;<br><span>&nbsp; &nbsp;<span class="cm-variable">requires&nbsp;<span class="cm-variable">database.<span class="cm-variable">connection;<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">此外,模块系统还带来了更好的性能优化,JVM 可以在启动时只加载必需的模块,减少内存占用和启动时间(适合云原生应用)。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是,模块系统在企业中用的比较少,目前大多数企业还是使用传统的 Maven/Gradle + JAR 包的方式管理依赖,改造项目的成本 &gt; 模块系统带来的实际收益,所以仅作了解就好。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】JShell 交互工具</span></h3>
<p class="md-end-block md-p"><span class="md-plain">JShell 是 Java 9 引入的一个交互式工具,在这个工具出现之前,我们要测试一小段 Java 代码,必须创建完整的类和 main 方法,编译后才能运行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">有了 JShell,我们可以像使用 Python 解释器一样使用 Java,对于学习调试有点儿用(但不多)。</span></p>
<p class="md-end-block md-p"><span class="md-plain">直接在命令行输入 <span class="md-pair-s"><code>jshell</code><span class="md-plain"> 就能使用了:</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756708051759-0bc8e1e2-7bb4-46db-989e-04f3ffeac585.png"><img src="https://pic.yupi.icu/1/1756708051759-0bc8e1e2-7bb4-46db-989e-04f3ffeac585.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】集合工厂方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 9 为集合类添加了便捷的工厂方法,能够轻松创建不可变集合。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在这之前,创建不可变集合还是比较麻烦的,很多开发者会选择依赖第三方库(比如 Google Guava)。</span></p>
<p class="md-end-block md-p"><span class="md-plain">传统的不可变集合创建方式:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 9 之前创建不可变集合的方式<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">oldList&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">ArrayList<span class="cm-operator">&lt;&gt;();<br><span><span class="cm-variable">oldList.<span class="cm-variable">add(<span class="cm-string">"苹果");<br><span><span class="cm-variable">oldList.<span class="cm-variable">add(<span class="cm-string">"香蕉");<br><span><span class="cm-variable">oldList.<span class="cm-variable">add(<span class="cm-string">"鱼皮");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">immutableList&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Collections.<span class="cm-variable">unmodifiableList(<span class="cm-variable">oldList);<br><span><span>​<br><span><span class="cm-comment">// 或者使用 Google Guava 等第三方库<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">guavaList&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">ImmutableList.<span class="cm-variable">of(<span class="cm-string">"苹果",&nbsp;<span class="cm-string">"香蕉",&nbsp;<span class="cm-string">"鱼皮");</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">有了 Java 9 的工厂方法,创建不可变集合简直不要太简单!</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 9 的简洁写法<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">fruits&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">List.<span class="cm-variable">of(<span class="cm-string">"苹果",&nbsp;<span class="cm-string">"香蕉",&nbsp;<span class="cm-string">"鱼皮");<br><span><span class="cm-variable">Set<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">numbers&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Set.<span class="cm-variable">of(<span class="cm-number">1,&nbsp;<span class="cm-number">2,&nbsp;<span class="cm-number">3,&nbsp;<span class="cm-number">4,&nbsp;<span class="cm-number">5);<br><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">scores&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Map.<span class="cm-variable">of(<br><span>&nbsp; &nbsp;<span class="cm-string">"张三",&nbsp;<span class="cm-number">85,&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-string">"鱼皮",&nbsp;<span class="cm-number">92,&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-string">"狗剩",&nbsp;<span class="cm-number">78<br><span>);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">这些集合是真正不可变的,任何修改操作都会抛出 <span class="md-pair-s"><code>UnsupportedOperationException</code><span class="md-plain"> 异常。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756708656069-63008461-59ef-43bb-bc5e-f1865b8b80aa-20250904142210675.png"><img src="https://pic.yupi.icu/1/1756708656069-63008461-59ef-43bb-bc5e-f1865b8b80aa-20250904142210675.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">如果想创建包含大量元素的不可变 Map,可以使用 ofEntries 方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">largeMap&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Map.<span class="cm-variable">ofEntries(<br><span>&nbsp; &nbsp;<span class="cm-variable">Map.<span class="cm-variable">entry(<span class="cm-string">"key1",&nbsp;<span class="cm-string">"value1"),<br><span>&nbsp; &nbsp;<span class="cm-variable">Map.<span class="cm-variable">entry(<span class="cm-string">"key2",&nbsp;<span class="cm-string">"value2"),<br><span>&nbsp; &nbsp;<span class="cm-variable">Map.<span class="cm-variable">entry(<span class="cm-string">"key3",&nbsp;<span class="cm-string">"value3")<br><span>&nbsp; &nbsp;<span class="cm-comment">// ... 可以有任意多个<br><span>);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】接口私有方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">思考一个问题,如果某个接口中的默认方法需要复用代码,你会怎么做呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">比如让你来优化下面这段代码:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Calculator&nbsp;{<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">calculateRectangleArea(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">width,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">height) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 重复的验证逻辑<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">width&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">height&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"宽度和高度必须为正数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">width&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">height;<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">calculateTriangleArea(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">base,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">height) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 重复的验证逻辑<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">base&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">height&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"底边和高度必须为正数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">base&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">height&nbsp;<span class="cm-operator">/&nbsp;<span class="cm-number">2;<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">你会把重复的验证逻辑写在哪里呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">答案很简单,写在一个外部工具类里,或者在接口内再写一个通用的验证方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Calculator&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 通用的验证方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">validate(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">x,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">y) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">x&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">y&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"必须为正数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">但这种方式存在一个问题,validate 作为 default 方法,它会成为接口的公共 API,所有实现类都能访问到!其实这个方法只需要在接口内可以使用就够了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Java 9 解决了这个问题,允许在接口中定义私有方法(以及私有静态方法)。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">interface&nbsp;<span class="cm-def">Calculator&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 私有方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">private&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">validate(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">x,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">y) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">x&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">y&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"必须为正数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span><span>​<br><span>&nbsp; &nbsp;<span class="cm-comment">// 私有静态方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">private&nbsp;<span class="cm-keyword">static&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">validatePositive(<span class="cm-variable-3">double&nbsp;<span class="cm-variable">x,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">y) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">x&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">y&nbsp;<span class="cm-operator">&lt;=&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"必须为正数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">这样一来,接口内部可以优雅地复用代码,同时保持接口对外的简洁性。</span></p>
<p class="md-end-block md-p"><span class="md-plain">💡 这里也能看出 Java 的演进很谨慎,先允许 default 方法(Java 8),再允许 private 方法(Java 9),每一步都有明确的设计考量。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】改进的 try-with-resources</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 9 改进了 try-with-resources 语句,在这之前,我们不能在 try 子句中使用外部定义的变量,必须在 try 括号内重新声明,会让代码变得冗余。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 9 之前<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">readFile(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">filename)&nbsp;<span class="cm-keyword">throws&nbsp;<span class="cm-variable">IOException&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable">BufferedReader&nbsp;<span class="cm-variable">reader&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">newBufferedReader(<span class="cm-variable">Paths.<span class="cm-variable">get(<span class="cm-variable">filename));<br><span>&nbsp; &nbsp;<span class="cm-keyword">try&nbsp;(<span class="cm-variable">BufferedReader&nbsp;<span class="cm-variable">br&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">reader) { &nbsp;<span class="cm-comment">// 需要重新赋值<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">br.<span class="cm-variable">lines().<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Java 9 的改进让代码更加简洁:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 9<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">readFile(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">filename)&nbsp;<span class="cm-keyword">throws&nbsp;<span class="cm-variable">IOException&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable">BufferedReader&nbsp;<span class="cm-variable">reader&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">newBufferedReader(<span class="cm-variable">Paths.<span class="cm-variable">get(<span class="cm-variable">filename));<br><span>&nbsp; &nbsp;<span class="cm-keyword">try&nbsp;(<span class="cm-variable">reader) { &nbsp;<span class="cm-comment">// 直接使用 effectively final 变量<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">reader.<span class="cm-variable">lines().<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">而且还可以同时使用多个变量:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">processFiles(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">file1,&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">file2)&nbsp;<span class="cm-keyword">throws&nbsp;<span class="cm-variable">IOException&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">reader1&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">newBufferedReader(<span class="cm-variable">Paths.<span class="cm-variable">get(<span class="cm-variable">file1));<br><span>&nbsp; &nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">reader2&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">newBufferedReader(<span class="cm-variable">Paths.<span class="cm-variable">get(<span class="cm-variable">file2));<br><span>&nbsp; &nbsp;<span class="cm-keyword">try&nbsp;(<span class="cm-variable">reader1;&nbsp;<span class="cm-variable">reader2) { &nbsp;<span class="cm-comment">// 可以使用多个变量<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">line1&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">reader1.<span class="cm-variable">readLine();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">line2&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">reader2.<span class="cm-variable">readLine();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">while&nbsp;(<span class="cm-variable">line1&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-atom">null&nbsp;<span class="cm-operator">&amp;&amp;&nbsp;<span class="cm-variable">line2&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-atom">null) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">line1&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" | "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">line2);<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">line1&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">reader1.<span class="cm-variable">readLine();<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">line2&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">reader2.<span class="cm-variable">readLine();<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 10</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】var 关键字</span></h3>
<p class="md-end-block md-p"><span class="md-plain">用过弱类型编程语言的朋友应该知道,不用自己声明变量的类型有多爽。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是对于 Java 这种强类型语言,我们经常要写下面这种代码,一个变量类型写老长(特别是在泛型场景下):</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">Map<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&gt;&nbsp;<span class="cm-variable">complexMap&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">HashMap<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&gt;();<br><span><span class="cm-variable">ArrayList<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">list&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">ArrayList<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;();<br><span><span class="cm-variable">Iterator<span class="cm-operator">&lt;<span class="cm-variable">Map.<span class="cm-variable">Entry<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&gt;&gt;&nbsp;<span class="cm-variable">iterator&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">complexMap.<span class="cm-variable">entrySet().<span class="cm-variable">iterator();</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">好在 Java 10 引入了 <span class="md-pair-s"><code>var</code><span class="md-plain"> 关键字,支持局部变量的类型推断,编译器会根据初始化表达式自动推断变量的类型,让代码可以变得更简洁。</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">var&nbsp;<span class="cm-variable">complexMap&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">HashMap<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Integer<span class="cm-operator">&gt;&gt;();<br><span><span class="cm-variable">var&nbsp;<span class="cm-variable">list&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">ArrayList<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;();<br><span><span class="cm-variable">var&nbsp;<span class="cm-variable">iterator&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">complexMap.<span class="cm-variable">entrySet().<span class="cm-variable">iterator();<br><span><span>​<br><span><span class="cm-comment">// 使用 var 的 for-each 循环<br><span><span class="cm-keyword">for&nbsp;(<span class="cm-variable">var&nbsp;<span class="cm-variable">entry&nbsp;:&nbsp;<span class="cm-variable">complexMap.<span class="cm-variable">entrySet()) {<br><span>&nbsp; &nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">key&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">entry.<span class="cm-variable">getKey();<br><span>&nbsp; &nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">value&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">entry.<span class="cm-variable">getValue();<br><span>&nbsp; &nbsp;<span class="cm-comment">// 处理逻辑<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">但是,var 关键字是一把双刃剑,不是所有程序员都喜欢它。毕竟代码中都是 <span class="md-pair-s"><code>var</code><span class="md-plain">,丢失了一定的可读性,尤其是下面这种代码,你不能直观地了解变量的类型:</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">var&nbsp;<span class="cm-variable">data&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">getData();</span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">而且使用 <span class="md-pair-s"><code>var</code><span class="md-plain"> 时,还要确保编译器能正确推断类型,下面这几种写法都是错误的:</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756710654321-793c9c49-95ad-4070-9cf4-fa1d97c793e0.png"><img src="https://pic.yupi.icu/1/1756710654321-793c9c49-95ad-4070-9cf4-fa1d97c793e0.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">所以我个人其实是没那么喜欢用这个关键字的,纯个人偏好。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】应用程序类数据共享</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 10 扩展了类数据共享功能,允许应用程序类也参与共享(Application Class-Data Sharing)。在此之前,只有 JDK 核心类可以进行类数据共享,应用程序类每次启动都需要重新加载和解析。</span></p>
<p class="md-end-block md-p"><span class="md-plain">类数据共享的核心思路是:将 JDK 核心类和应用程序类的元数据都打包到共享归档文件中,多个 JVM 实例同时映射同一个归档文件,通过 <span class="md-pair-s "><strong>共享读取</strong><span class="md-plain"> 优化应用启动时间和减少内存占用。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966850130-c8c215e9-00b3-4d7e-b62a-61c68a0da1b8.png"><img src="https://pic.yupi.icu/1/1756966850130-c8c215e9-00b3-4d7e-b62a-61c68a0da1b8.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">⭐️ Java 11</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 11 是继 Java 8 之后的第二个 LTS 版本,这个版本的重点是提供更好的开发体验和更强大的标准库功能,特别是在字符串处理、文件操作和 HTTP 客户端方面,增加了不少新方法。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】HTTP 客户端 API</span></h3>
<p class="md-end-block md-p"><span class="md-plain">HTTP 请求是后端开发常用的能力,之前我们只能基于内置的 HttpURLConnection 自己封装,或者使用 Apache HttpClient、OkHttp 第三方库。</span></p>
<p class="md-end-block md-p"><span class="md-plain">还记得我第一次去公司实习的时候,就看到代码仓库内有很多老员工自己封装的 HTTP 请求代码,写法各异。。。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 传统的 HttpURLConnection 使用方式<br><span><span class="cm-variable">URL&nbsp;<span class="cm-variable">url&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">URL(<span class="cm-string">"https://codefather.cn");<br><span><span class="cm-variable">HttpURLConnection&nbsp;<span class="cm-variable">connection&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">HttpURLConnection)&nbsp;<span class="cm-variable">url.<span class="cm-variable">openConnection();<br><span><span class="cm-variable">connection.<span class="cm-variable">setRequestMethod(<span class="cm-string">"GET");<br><span><span class="cm-variable">connection.<span class="cm-variable">setRequestProperty(<span class="cm-string">"Accept",&nbsp;<span class="cm-string">"application/json");<br><span><span>​<br><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">responseCode&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">connection.<span class="cm-variable">getResponseCode();<br><span><span class="cm-variable">BufferedReader&nbsp;<span class="cm-variable">reader&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">BufferedReader(<span class="cm-keyword">new&nbsp;<span class="cm-variable">InputStreamReader(<span class="cm-variable">connection.<span class="cm-variable">getInputStream()));<br><span><span class="cm-comment">// 更多繁琐的代码...</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Java 11 将 HTTP 客户端 API 正式化,新的 HTTP 客户端提供了现代化的、支持 HTTP/2 和 WebSocket 的客户端实现,让网络编程变得简单。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 创建 HTTP 客户端<br><span><span class="cm-variable">HttpClient&nbsp;<span class="cm-variable">client&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">HttpClient.<span class="cm-variable">newBuilder()<br><span>&nbsp; .<span class="cm-variable">connectTimeout(<span class="cm-variable">Duration.<span class="cm-variable">ofSeconds(<span class="cm-number">10))<br><span>&nbsp; .<span class="cm-variable">followRedirects(<span class="cm-variable">HttpClient.<span class="cm-variable">Redirect.<span class="cm-variable">NORMAL)<br><span>&nbsp; .<span class="cm-variable">build();<br><span><span>​<br><span><span class="cm-comment">// 构建 GET 请求<br><span><span class="cm-variable">HttpRequest&nbsp;<span class="cm-variable">getRequest&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">HttpRequest.<span class="cm-variable">newBuilder()<br><span>&nbsp; .<span class="cm-variable">uri(<span class="cm-variable">URI.<span class="cm-variable">create(<span class="cm-string">"https://codefather.cn"))<br><span>&nbsp; .<span class="cm-variable">header(<span class="cm-string">"Accept",&nbsp;<span class="cm-string">"application/json")<br><span>&nbsp; .<span class="cm-variable">header(<span class="cm-string">"User-Agent",&nbsp;<span class="cm-string">"Java-HttpClient")<br><span>&nbsp; .<span class="cm-variable">timeout(<span class="cm-variable">Duration.<span class="cm-variable">ofSeconds(<span class="cm-number">30))<br><span>&nbsp; .<span class="cm-variable">GET()<br><span>&nbsp; .<span class="cm-variable">build();<br><span><span>​<br><span><span class="cm-comment">// POST 请求<br><span><span class="cm-variable">HttpRequest&nbsp;<span class="cm-variable">postRequest&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">HttpRequest.<span class="cm-variable">newBuilder()<br><span>&nbsp; .<span class="cm-variable">uri(<span class="cm-variable">URI.<span class="cm-variable">create(<span class="cm-string">"https://api.example.com/users"))<br><span>&nbsp; .<span class="cm-variable">header(<span class="cm-string">"Content-Type",&nbsp;<span class="cm-string">"application/json")<br><span>&nbsp; .<span class="cm-variable">POST(<span class="cm-variable">HttpRequest.<span class="cm-variable">BodyPublishers.<span class="cm-variable">ofString(<span class="cm-variable">jsonData))<br><span>&nbsp; .<span class="cm-variable">build();</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">支持发送同步和异步请求,能够轻松获取响应结果:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 同步发送请求<br><span><span class="cm-variable">HttpResponse<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">response&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">client.<span class="cm-variable">send(<span class="cm-variable">getRequest,&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-variable">HttpResponse.<span class="cm-variable">BodyHandlers.<span class="cm-variable">ofString());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"状态码: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">response.<span class="cm-variable">statusCode());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"响应头: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">response.<span class="cm-variable">headers().<span class="cm-variable">map());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"响应体: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">response.<span class="cm-variable">body());<br><span><span>​<br><span><span class="cm-comment">// 异步发送请求<br><span><span class="cm-variable">client.<span class="cm-variable">sendAsync(<span class="cm-variable">getRequest,&nbsp;<span class="cm-variable">HttpResponse.<span class="cm-variable">BodyHandlers.<span class="cm-variable">ofString())<br><span>&nbsp; .<span class="cm-variable">thenApply(<span class="cm-variable">HttpResponse::<span class="cm-variable">body)<br><span>&nbsp; .<span class="cm-variable">thenAccept(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还支持自定义响应处理和 WebSocket 请求:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 自定义响应处理<br><span><span class="cm-variable">HttpResponse<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">customResponse&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">client.<span class="cm-variable">send(<span class="cm-variable">getRequest,&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-variable">responseInfo&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">responseInfo.<span class="cm-variable">statusCode()&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-number">200) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">HttpResponse.<span class="cm-variable">BodySubscribers.<span class="cm-variable">ofString(<span class="cm-variable">StandardCharsets.<span class="cm-variable">UTF_8);<br><span>&nbsp; &nbsp; &nbsp; }&nbsp;<span class="cm-keyword">else&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">HttpResponse.<span class="cm-variable">BodySubscribers.<span class="cm-variable">discarding();<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; });<br><span><span>​<br><span><span class="cm-comment">// WebSocket 支持<br><span><span class="cm-variable">WebSocket&nbsp;<span class="cm-variable">webSocket&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">HttpClient.<span class="cm-variable">newHttpClient()<br><span>&nbsp; .<span class="cm-variable">newWebSocketBuilder()<br><span>&nbsp; .<span class="cm-variable">buildAsync(<span class="cm-variable">URI.<span class="cm-variable">create(<span class="cm-string">"ws://localhost:8080/websocket"),&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">WebSocket.<span class="cm-variable">Listener() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-variable">onOpen(<span class="cm-variable">WebSocket&nbsp;<span class="cm-variable">webSocket) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"WebSocket 连接已打开");<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">webSocket.<span class="cm-variable">sendText(<span class="cm-string">"Hello WebSocket!",&nbsp;<span class="cm-atom">true);<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable">CompletionStage<span class="cm-operator">&lt;?&gt;&nbsp;<span class="cm-variable">onText(<span class="cm-variable">WebSocket&nbsp;<span class="cm-variable">webSocket,&nbsp;<span class="cm-variable">CharSequence&nbsp;<span class="cm-variable">data,&nbsp;<span class="cm-variable-3">boolean&nbsp;<span class="cm-variable">last) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"收到消息: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">data);<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-atom">null;<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; })<br><span>&nbsp; .<span class="cm-variable">join();</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">上面这些代码都不用记,现在直接把接口文档甩给 AI,让它来帮你生成请求代码就好。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】String 类的新方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 11 为 String 类添加了许多实用的方法,让字符串处理变得更加方便。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我估计很多现在学 Java 的同学都已经区分不出来哪些是新增的方法、哪些是老方法了,反正能用就行~</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">1)基本的字符串检查和处理:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">text&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">" Hello World \n\n";<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">emptyText&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">" &nbsp; ";<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">multiLine&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">"第一行\n第二行\n第三行";<br><span><span>​<br><span><span class="cm-comment">// isBlank() 检查字符串是否为空或只包含空白字符<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">emptyText.<span class="cm-variable">isBlank()); &nbsp; &nbsp;&nbsp;<span class="cm-comment">// true<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"hello".<span class="cm-variable">isBlank()); &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// false<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"".<span class="cm-variable">isBlank()); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// true</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">2)strip() 系列方法</span></p>
<p class="md-end-block md-p"><span class="md-plain">相比传统的 trim() 更加强大,能够处理 Unicode 空白字符:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// strip() 系列方法,去除空白字符<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"'"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">text.<span class="cm-variable">strip()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">"'"); &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 'Hello World'<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"'"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">text.<span class="cm-variable">stripLeading()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">"'"); &nbsp;<span class="cm-comment">// 'Hello World \n\n'<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"'"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">text.<span class="cm-variable">stripTrailing()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">"'");&nbsp;<span class="cm-comment">// ' Hello World'</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">3)lines() 方法,让多行字符串处理更简单:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 将字符串按行分割成 Stream<br><span><span class="cm-variable">multiLine.<span class="cm-variable">lines()<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">line&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"处理: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">line)<br><span>&nbsp; .<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);<br><span><span>​<br><span><span class="cm-variable-3">long&nbsp;<span class="cm-variable">lineCount&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">multiLine.<span class="cm-variable">lines().<span class="cm-variable">count();<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"总行数: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">lineCount);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">4)repeat() 方法,可以重复字符串:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"Java ".<span class="cm-variable">repeat(<span class="cm-number">3));&nbsp;<span class="cm-comment">// "Java Java Java "<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"=".<span class="cm-variable">repeat(<span class="cm-number">50)); &nbsp; &nbsp;<span class="cm-comment">// 50个等号<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"*".<span class="cm-variable">repeat(<span class="cm-number">0)); &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 空字符串</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">即便如此,我还是更喜欢使用 Hutool 或者 Apache Commons 提供的字符串工具类。</span></p>
<p class="md-end-block md-p"><span class="md-plain">💡 提到字符串处理,鱼皮建议大家安装 StringManipulation 插件,便于我们开发时对字符串进行各种转换(比如小写转为驼峰):</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756711493348-96eb9b3b-04ed-4d80-b76e-654a2d0c75a7.png"><img src="https://pic.yupi.icu/1/1756711493348-96eb9b3b-04ed-4d80-b76e-654a2d0c75a7.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】Files 类的新方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 11 为文件操作新增了更便捷的方法,不需要使用 FileReader / FileWriter 这种复杂的操作了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">基本的文件读写操作,一个方法搞定:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 写入文件<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">content&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">"这是一个测试文件\n包含多行内容\n中文支持测试";<br><span><span class="cm-variable">Path&nbsp;<span class="cm-variable">tempFile&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">writeString(<br><span>&nbsp; &nbsp;<span class="cm-variable">Paths.<span class="cm-variable">get(<span class="cm-string">"temp.txt"),&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-variable">content,<br><span>&nbsp; &nbsp;<span class="cm-variable">StandardCharsets.<span class="cm-variable">UTF_8<br><span>);<br><span><span>​<br><span><span class="cm-comment">// 读取文件<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">readContent&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">readString(<span class="cm-variable">tempFile,&nbsp;<span class="cm-variable">StandardCharsets.<span class="cm-variable">UTF_8);<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"读取的内容:\n"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">readContent);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">支持流式读取文件,适合文件较大的场景:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">try&nbsp;(<span class="cm-variable">Stream<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">lines&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Files.<span class="cm-variable">lines(<span class="cm-variable">tempFile)) {<br><span>&nbsp; &nbsp;<span class="cm-variable">lines.<span class="cm-variable">filter(<span class="cm-variable">line&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-operator">!<span class="cm-variable">line.<span class="cm-variable">isBlank())<br><span>&nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">map(<span class="cm-variable-3">String::<span class="cm-variable">trim)<br><span>&nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">forEach(<span class="cm-variable">System.<span class="cm-variable">out::<span class="cm-variable">println);<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Optional 的新方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 11 为 Optional 类添加了 <span class="md-pair-s"><code>isEmpty()</code><span class="md-plain"> 方法,和之前的 <span class="md-pair-s"><code>isPresent</code><span class="md-plain"> 正好相反,让空值检查更直观。</span></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756712087838-69a43b04-15b2-4af4-b680-8d4dc0e48485.png"><img src="https://pic.yupi.icu/1/1756712087838-69a43b04-15b2-4af4-b680-8d4dc0e48485.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 12 ~ 13</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 12 和 13 主要引入了一些预览特性,其中最重要的是 Switch 表达式和文本块,这些特性在后续版本中得到了完善和正式化。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 14</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 14 将 Switch 表达式正式化,并引入了 Records、instanceof 模式匹配作为预览特性。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Switch 表达式</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 14 将 Switch 表达式转正了,让条件判断变得更简洁和安全。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在这之前,传统的 switch 语句存在不少问题,比如需要手动添加 break 防止穿透、赋值不方便等:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">dayType;<br><span><span class="cm-keyword">switch&nbsp;(<span class="cm-variable">day) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">MONDAY:<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">TUESDAY:<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">WEDNESDAY:<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">THURSDAY:<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">FRIDAY:<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">dayType&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">"工作日";<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">SATURDAY:<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">SUNDAY:<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">dayType&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">"周末";<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break;<br><span>&nbsp; &nbsp;<span class="cm-keyword">default:<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">dayType&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-string">"未知";<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break;<br><span>}<br><span><span>​<br><span><span class="cm-comment">// 赋值不方便<br><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">score;<br><span><span class="cm-keyword">switch&nbsp;(<span class="cm-variable">grade) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-string">'A':<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"优秀!");<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">score&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-number">90;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-string">'B':<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">score&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-number">80;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break;<br><span>&nbsp; &nbsp;<span class="cm-keyword">default:<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">score&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-number">0;<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">在 Java 14 之后,可以直接这么写:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 14 的简洁写法<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">dayType&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">day) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">MONDAY,&nbsp;<span class="cm-variable">TUESDAY,&nbsp;<span class="cm-variable">WEDNESDAY,&nbsp;<span class="cm-variable">THURSDAY,&nbsp;<span class="cm-variable">FRIDAY&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"工作日";<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">SATURDAY,&nbsp;<span class="cm-variable">SUNDAY&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"周末";<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"未知";<br><span>};<br><span><span>​<br><span><span class="cm-comment">// 支持复杂逻辑的 yield 关键字<br><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">score&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">grade) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-string">'A'&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"优秀!");<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">yield&nbsp;<span class="cm-number">90; &nbsp;<span class="cm-comment">// 使用 yield 返回值<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-string">'B'&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-number">80;<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-number">0;<br><span>};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">上述代码中,我们使用了 Switch 表达式增强的几个特性:</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">箭头语法:使用 <span class="md-pair-s"><code>-&gt;</code><span class="md-plain"> 替代冒号,自动防止 fall-through(不用写 break 了)</span></span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">多标签支持:<span class="md-pair-s"><code>case A, B, C -&gt;</code><span class="md-plain"> 一行处理多个条件</span></span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">表达式求值:可以直接使用 yield 关键字返回值并赋给变量</span></p>
</li>
</ul>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">这样一来,多条件判断变得更优雅了!还能避免忘记 break 导致的逻辑错误。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 实际应用示例:根据月份判断季节<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">season&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">month) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-number">12,&nbsp;<span class="cm-number">1,&nbsp;<span class="cm-number">2&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"冬季";<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-number">3,&nbsp;<span class="cm-number">4,&nbsp;<span class="cm-number">5&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"春季";&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-number">6,&nbsp;<span class="cm-number">7,&nbsp;<span class="cm-number">8&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"夏季";<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-number">9,&nbsp;<span class="cm-number">10,&nbsp;<span class="cm-number">11&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"秋季";<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"无效月份: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">month);<br><span>};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】有用的空指针异常</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 14 改进了 NullPointerException 的错误信息。JVM 会提供更详细的堆栈跟踪信息,指出导致异常的具体位置和原因,让调试变得更加容易。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756713046482-7a31910b-3d3e-4903-a718-a7347572cb7a.png"><img src="https://pic.yupi.icu/1/1756713046482-7a31910b-3d3e-4903-a718-a7347572cb7a.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 15</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 15 将文本块正式化,新增了 Hidden 隐藏类,并引入了 Sealed 类作为预览特性。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】文本块</span></h3>
<p class="md-end-block md-p"><span class="md-plain">这可能是我最喜欢的特性之一了,因为之前每次复制多行文本到代码中,都会给我转成这么一坨:</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756717281759-789a9b03-134a-4d5f-a7d9-d64c0d07943f.png"><img src="https://pic.yupi.icu/1/1756717281759-789a9b03-134a-4d5f-a7d9-d64c0d07943f.png"></span></p>
<p class="md-end-block md-p"><span class="md-plain">需要大量的字符串拼接、转义字符,对于 HTML、SQL 和 JSON 格式来说简直是噩梦了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">有了 Java 15 的文本块特性,多行字符串简直不要太爽!直接用三个引号 <span class="md-pair-s"><code>"""</code><span class="md-plain"> 括起来,就能以字符串本来的格式展示。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756717262864-a9d662c9-a6ac-4e12-bd0e-b1eda1da44fe.png"><img src="https://pic.yupi.icu/1/1756717262864-a9d662c9-a6ac-4e12-bd0e-b1eda1da44fe.png"></span></p>
<p class="md-end-block md-p"><span class="md-plain">文本块会保持代码的缩进、而且内部的引号不需要转义。</span></p>
<p class="md-end-block md-p"><span class="md-plain">配合 String 的格式化方法,就能轻松传入参数生成复杂的字符串模板:</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756717301051-5a656231-bdbe-4b2e-94a9-1a4ad9848907.png"><img src="https://pic.yupi.icu/1/1756717301051-5a656231-bdbe-4b2e-94a9-1a4ad9848907.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Hidden 隐藏类</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 15 引入了 Hidden 隐藏类特性,这是一个 <span class="md-pair-s "><strong>专为框架和运行时环境设计</strong><span class="md-plain"> 的底层机制,主要是为了优化 <span class="md-pair-s "><strong>动态生成短期类</strong><span class="md-plain">(比如 Lambda 表达式、动态代理)的性能问题,普通开发者无需关心。</span></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">在 Lambda 表达式、AOP 动态代理、ORM 映射等场景中,框架会动态生成代码载体(比如方法句柄、临时代理类),这些载体需要关联类的元数据才能运行。如果生成频繁,传统类的元数据会被类加载器追踪,需要等待类加载器卸载才能回收,导致元空间堆积和 GC 压力。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Hidden 类的特点是对其定义类加载器之外的所有代码都不可见,由于不可发现且链接微弱,JVM 垃圾回收器能够更高效地卸载隐藏类及其元数据,从而防止短期类堆积对元空间造成压力,优化了需要动态生成大量类的性能。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 16</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 16 正式发布了 Records 和 instanceof 模式匹配这 2 大特性,让代码更简洁易读。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Records</span></h3>
<p class="md-end-block md-p"><span class="md-plain">以前,我们如果想创建一个 POJO 对象来存一些数据,需要编写大量的样板代码,包括构造函数、getter 方法、equals、hashCode 等等,比较麻烦。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// Java 16 之前创建数据类的方式<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Person&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-keyword">private&nbsp;<span class="cm-keyword">final&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">name;<br><span>&nbsp; &nbsp;<span class="cm-keyword">private&nbsp;<span class="cm-keyword">final&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">age;<br><span>&nbsp; &nbsp;<span class="cm-keyword">private&nbsp;<span class="cm-keyword">final&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">email;<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable">Person(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">age,&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">email) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">this.<span class="cm-variable">name&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">name;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">this.<span class="cm-variable">age&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">age;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">this.<span class="cm-variable">email&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">email;<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">getName() {&nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">name; }<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">getAge() {&nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">age; }<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">getEmail() {&nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">email; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">boolean&nbsp;<span class="cm-variable">equals(<span class="cm-variable-3">Object&nbsp;<span class="cm-variable">obj) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-keyword">this&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-variable">obj)&nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-atom">true;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">obj&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-atom">null&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">getClass()&nbsp;<span class="cm-operator">!=&nbsp;<span class="cm-variable">obj.<span class="cm-variable">getClass())&nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-atom">false;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Person&nbsp;<span class="cm-variable">person&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">Person)&nbsp;<span class="cm-variable">obj;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">age&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-variable">person.<span class="cm-variable">age&nbsp;<span class="cm-operator">&amp;&amp;&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-variable">Objects.<span class="cm-variable">equals(<span class="cm-variable">name,&nbsp;<span class="cm-variable">person.<span class="cm-variable">name)&nbsp;<span class="cm-operator">&amp;&amp;&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-variable">Objects.<span class="cm-variable">equals(<span class="cm-variable">email,&nbsp;<span class="cm-variable">person.<span class="cm-variable">email);<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-meta">@Override<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">hashCode() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">Objects.<span class="cm-variable">hash(<span class="cm-variable">name,&nbsp;<span class="cm-variable">age,&nbsp;<span class="cm-variable">email);<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">即使通过 Lombok 插件简化了代码,估计也要十几行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">有了 Java 16 的 Records,创建数据包装类简直不要太简单,一行代码搞定:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">record&nbsp;<span class="cm-def">Person(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">age,&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">email) {}</span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">Records 自动提供了所有必需的方法,使用方式完全一样!</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">Person&nbsp;<span class="cm-variable">person&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Person(<span class="cm-string">"鱼皮",&nbsp;<span class="cm-number">25,&nbsp;<span class="cm-string">"yupi@yuyuanweb.com");<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">person.<span class="cm-variable">name()); &nbsp; &nbsp;&nbsp;<span class="cm-comment">// 自动生成的访问器<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">person.<span class="cm-variable">age());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">person.<span class="cm-variable">email());<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">person.<span class="cm-variable">toString());&nbsp;<span class="cm-comment">// 自动生成的 toString</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">此外,Records 还支持自定义方法和验证逻辑,只不过个人建议这种情况下不如老老实实用 “类” 了。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">record&nbsp;<span class="cm-def">BankAccount(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">accountNumber,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">balance) {<br><span>&nbsp; &nbsp;<span class="cm-comment">// 构造函数中添加验证<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable">BankAccount&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">balance&nbsp;<span class="cm-operator">&lt;&nbsp;<span class="cm-number">0) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"余额不能为负数");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">accountNumber&nbsp;<span class="cm-operator">==&nbsp;<span class="cm-atom">null&nbsp;<span class="cm-operator">||&nbsp;<span class="cm-variable">accountNumber.<span class="cm-variable">isBlank()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">IllegalArgumentException(<span class="cm-string">"账号不能为空");<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 自定义方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-variable-3">boolean&nbsp;<span class="cm-variable">isVIP() {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">balance&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">100000;<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-comment">// 静态工厂方法<br><span>&nbsp; &nbsp;<span class="cm-keyword">public&nbsp;<span class="cm-keyword">static&nbsp;<span class="cm-variable">BankAccount&nbsp;<span class="cm-variable">createSavingsAccount(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">accountNumber) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">BankAccount(<span class="cm-variable">accountNumber,&nbsp;<span class="cm-number">0.0);<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】instanceof 模式匹配</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 16 正式推出了 instanceof 的模式匹配,让类型检查和转换变得更优雅。</span></p>
<p class="md-end-block md-p"><span class="md-plain">传统的 instanceof 使用方式,需要显示转换对象类型:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable-3">Object&nbsp;<span class="cm-variable">obj&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">xxx;<br><span><span class="cm-keyword">if&nbsp;(<span class="cm-variable">obj&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable-3">String) {<br><span>&nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">str&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable-3">String)&nbsp;<span class="cm-variable">obj; &nbsp;<span class="cm-comment">// 需要显式转换<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"字符串长度: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">str.<span class="cm-variable">length();<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">有了 instanceof 模式匹配,可以直接在匹配类型时声明变量:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">if&nbsp;(<span class="cm-variable">obj&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">str) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"字符串长度: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">str.<span class="cm-variable">length();<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">但是要注意,str 变量的作用域被限定在 if 条件为 true 的代码块中,符合最小作用域原则。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Stream 新增方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 16 为 Stream API 添加了 <span class="md-pair-s"><code>toList()</code><span class="md-plain"> 方法,可以用更简洁的代码将流转换为不可变列表。</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 传统写法<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">stream<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">s.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">3)<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-comment">// Java 16 简化写法<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">stream<br><span>&nbsp; .<span class="cm-variable">filter(<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">s.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">3)<br><span>&nbsp; .<span class="cm-variable">toList(); &nbsp;<span class="cm-comment">// 返回不可变 List</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还提供了 <span class="md-pair-s"><code>mapMulti()</code><span class="md-plain"> 方法,跟 flatMap 的作用一样,将一个元素映射为 0 个或多个元素,但是某些场景下比 flatMap 更灵活高效。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">当需要从一个元素生成多个元素时,flatMap 需要先创建一个中间 Stream,而 <span class="md-pair-s"><code>mapMulti()</code><span class="md-plain"> 可以通过传入的 Consumer 直接 “推送” 多个元素,避免了中间集合或 Stream 的创建开销。</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// flatMap 传统方式<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">words&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">List.<span class="cm-variable">of(<span class="cm-string">"hello",&nbsp;<span class="cm-string">"world",&nbsp;<span class="cm-string">"java");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Character<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">chars&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">words.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">flatMap(<span class="cm-variable">word&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">word.<span class="cm-variable">chars()<br><span>&nbsp; &nbsp; &nbsp; .<span class="cm-variable">mapToObj(<span class="cm-variable">c&nbsp;<span class="cm-operator">-&gt;&nbsp;(<span class="cm-variable-3">char)&nbsp;<span class="cm-variable">c))<br><span>&nbsp; .<span class="cm-variable">toList();<br><span><span>​<br><span><span class="cm-comment">// Java 16 的 mapMulti 方式<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Character<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">chars&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">words.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-operator">&lt;<span class="cm-variable-3">Character<span class="cm-operator">&gt;<span class="cm-def">mapMulti((<span class="cm-variable">word,&nbsp;<span class="cm-variable">consumer)&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">for&nbsp;(<span class="cm-variable-3">char&nbsp;<span class="cm-variable">c&nbsp;:&nbsp;<span class="cm-variable">word.<span class="cm-variable">toCharArray()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">consumer.<span class="cm-variable">accept(<span class="cm-variable">c); &nbsp;<span class="cm-comment">// 直接向下游推送元素<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; })<br><span>&nbsp; .<span class="cm-variable">toList();</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">⭐️ Java 17</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 17 是目前 Java 最主流的 LTS 版本,比例已经超越了 Java 8!现在很多新的 Java 开发框架和类库支持的最低 JDK 版本就是 17(比如 AI 开发框架 LangChain4j)。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756718763673-e8168847-271e-42fe-ab6b-a21e0d627268.png"><img src="https://pic.yupi.icu/1/1756718763673-e8168847-271e-42fe-ab6b-a21e0d627268.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】Sealed 密封类</span></h3>
<p class="md-end-block md-p"><span class="md-plain">在很多 Java 开发者的印象中,一个类要么完全开放继承(任何类都能继承),要么完全禁止继承(final 类)。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 选择1:完全开放继承<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Shape&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 问题:不知道会有哪些子类,难以进行穷举<br><span>}<br><span><span>​<br><span><span class="cm-comment">// 选择2:完全禁止继承<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">final&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Circle&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 问题:即使在同一个模块内也无法继承<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">其实这样是没办法精确控制继承关系的,在设计 API 或领域模型时可能会遇到问题。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Java 17 将 Sealed 密封类转正,让类的继承关系变得更可控和安全。</span></p>
<p class="md-end-block md-p"><span class="md-plain">比如我可以只允许某几个类继承:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">sealed&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Shape&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-def">permits&nbsp;<span class="cm-def">Circle,&nbsp;<span class="cm-variable">Rectangle,&nbsp;<span class="cm-variable">Triangle&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-comment">// 只允许这三个类继承<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">但是,被允许继承的子类必须选择一种继承策略:</span></p>
<p class="md-end-block md-p"><span class="md-plain">1)final:到我为止,不能再继承了</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">final&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Circle&nbsp;<span class="cm-keyword">extends&nbsp;<span class="cm-variable">Shape&nbsp;{<br><span>}</span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">2)sealed:我也要控制谁能继承我</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">sealed&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Triangle&nbsp;<span class="cm-keyword">extends&nbsp;<span class="cm-variable">Shape&nbsp;<br><span>&nbsp; &nbsp;<span class="cm-variable">permits&nbsp;<span class="cm-variable">RightTriangle&nbsp;{<br><span>}</span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">3)non-sealed:我开放继承,任何人都可以继承我</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">non<span class="cm-operator">-<span class="cm-variable">sealed&nbsp;<span class="cm-keyword">class&nbsp;<span class="cm-def">Rectangle&nbsp;<span class="cm-keyword">extends&nbsp;<span class="cm-variable">Shape&nbsp;{<br><span>}</span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">强制声明继承策略是为了 <span class="md-pair-s "><strong>确保设计控制权的完整传递</strong><span class="md-plain">。如果不强制声明,sealed 类精确控制继承的价值就会被破坏,任何人都可以通过继承子类来绕过原始设计的限制。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">注意,虽然看起来 non-sealed 打破了这个设计,但这也是设计者的主动选择。如果不需要强制声明,设计者可能会无意中失去控制权。</span></p>
<p class="md-end-block md-p"><span class="md-plain">有了 Sealed 类后,某个接口可能的实现类型就尽在掌握了,可以让 switch 模式匹配变得更加安全:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 编译器知道所有可能的子类型,可以进行完整性检查<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-def">calculateArea(<span class="cm-variable">Shape&nbsp;<span class="cm-variable">shape) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">shape) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">Circle&nbsp;<span class="cm-variable">c&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">Math.<span class="cm-variable">PI&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">c.<span class="cm-variable">getRadius()&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">c.<span class="cm-variable">getRadius();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">Rectangle&nbsp;<span class="cm-variable">r&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-variable">r.<span class="cm-variable">getWidth()&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">r.<span class="cm-variable">getHeight();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">Triangle&nbsp;<span class="cm-variable">t&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-number">0.5&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">t.<span class="cm-variable">getBase()&nbsp;<span class="cm-operator">*&nbsp;<span class="cm-variable">t.<span class="cm-variable">getHeight();<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 编译器确保我们处理了所有情况,无需 default 分支<br><span>&nbsp; };<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】新的随机数生成器</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 17 引入了全新的随机数生成器 API,提供了更优的性能和更多的算法选择:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 传统的随机数<br><span><span class="cm-variable">Random&nbsp;<span class="cm-variable">oldRandom&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Random();<br><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">oldValue&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">oldRandom.<span class="cm-variable">nextInt(<span class="cm-number">100);<br><span><span>​<br><span><span class="cm-comment">// 新的随机数生成器<br><span><span class="cm-variable">RandomGenerator&nbsp;<span class="cm-variable">generator&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">RandomGenerator.<span class="cm-variable">of(<span class="cm-string">"L32X64MixRandom");<br><span><span class="cm-variable-3">int&nbsp;<span class="cm-variable">newValue&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">generator.<span class="cm-variable">nextInt(<span class="cm-number">100);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】强封装 JDK 内部 API</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 17 进一步强化了对 JDK 内部 API 的封装,一些之前可以通过反射访问的内部类现在完全不可访问,比如:</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>sun.misc.Unsafe</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>com.sun.*</code><span class="md-plain"> 包下的类</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s"><code>jdk.internal.*</code><span class="md-plain"> 包下的类</span></span></p>
</li>
</ul>
<p class="md-end-block md-p"><span class="md-plain">虽然这提高了 JDK 的安全性和稳定性,但可能需要迁移一些依赖内部 API 的老代码。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 18</span></h2>
<p class="md-end-block md-p"><span class="md-plain">个人感觉 Java 18 提供的功能都没什么用,简单了解一下就好。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】简单 Web 服务器</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 18 引入了一个简单的 Web 服务器,主要用于开发和测试。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment"># 启动简单的文件服务器,服务当前目录<br><span>jwebserver<br><span><span>​<br><span><span class="cm-comment"># 指定端口和目录<br><span>jwebserver&nbsp;<span class="cm-attribute">-p&nbsp;<span class="cm-number">8080&nbsp;<span class="cm-attribute">-d&nbsp;/path/to/your/files<br><span><span>​<br><span><span class="cm-comment"># 绑定到特定地址<br><span>jwebserver&nbsp;<span class="cm-attribute">-b&nbsp;<span class="cm-number">127.0.0.1&nbsp;<span class="cm-attribute">-p&nbsp;<span class="cm-number">9000</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">Nginx 不香么,我要用这个东西?</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】UTF-8 作为默认字符集</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 18 将 UTF-8 设为默认字符集,解决了很多字符编码相关的问题,Java 程序在不同平台上的行为会更加一致。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 这些操作现在默认使用 UTF-8 编码<br><span><span class="cm-variable">FileReader&nbsp;<span class="cm-variable">reader&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">FileReader(<span class="cm-string">"file.txt");<br><span><span class="cm-variable">FileWriter&nbsp;<span class="cm-variable">writer&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">FileWriter(<span class="cm-string">"file.txt");<br><span><span class="cm-variable">PrintStream&nbsp;<span class="cm-variable">out&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">PrintStream(<span class="cm-string">"output.txt");</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">在这之前,Java 使用的是 <span class="md-pair-s "><strong>系统默认字符集</strong><span class="md-plain">,会导致同一段代码在不同操作系统上可能产生完全不同的结果。</span></span></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】JavaDoc 代码片段</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 18 引入了 <span class="md-pair-s"><code>@snippet</code><span class="md-plain"> 标签,可以让 JavaDoc 生成的代码示例更美观,而且支持从外部文件引入代码片段。</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">/**<br><span><span class="cm-comment">* 计算两个数的最大公约数<br><span><span class="cm-comment">*&nbsp;<br><span><span class="cm-comment">* {@snippet :<br><span><span class="cm-comment">* int a = 48;<br><span><span class="cm-comment">* int b = 18;<br><span><span class="cm-comment">* int gcd = MathUtils.gcd(a, b);<br><span><span class="cm-comment">* System.out.println("GCD: " + gcd); // @highlight substring="GCD"<br><span><span class="cm-comment">* }<br><span><span class="cm-comment">*&nbsp;<br><span><span class="cm-comment">* @param a 第一个数<br><span><span class="cm-comment">* @param b 第二个数<br><span><span class="cm-comment">* @return 最大公约数<br><span><span class="cm-comment">*/<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">static&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-def">gcd(<span class="cm-variable-3">int&nbsp;<span class="cm-variable">a,&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">b) {<br><span>&nbsp; &nbsp;<span class="cm-comment">// 实现代码...<br><span>}<br><span><span>​<br><span><span class="cm-comment">/**<br><span><span class="cm-comment">* 从外部文件引入代码片段<br><span><span class="cm-comment">*&nbsp;<br><span><span class="cm-comment">* {@snippet file="examples/QuickSort.java" region="main-algorithm"}<br><span><span class="cm-comment">*&nbsp;<br><span><span class="cm-comment">* @param arr 要排序的数组<br><span><span class="cm-comment">*/<br><span><span class="cm-keyword">public&nbsp;<span class="cm-keyword">static&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">quickSort(<span class="cm-variable-3">int[]&nbsp;<span class="cm-variable">arr) {<br><span>&nbsp; &nbsp;<span class="cm-comment">// 实现代码...<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">不过这年头还有开发者阅读 JavaDoc 么?</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756722276726-4dd31862-9211-4d5f-8ee2-a6891dbd1219.png"><img src="https://pic.yupi.icu/1/1756722276726-4dd31862-9211-4d5f-8ee2-a6891dbd1219.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 19 ~ 20</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 19 和 20 主要是为一些重大特性做准备,包括虚拟线程、Record 模式、Switch 模式匹配等。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 21</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Java 21 是鱼皮做新项目时使用的首选 LTS 版本。这个版本发布了很多重要特性,其中最重要的是 Virtual Threads 虚拟线程。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Virtual Threads 虚拟线程</span></h3>
<p class="md-end-block md-p"><span class="md-plain">这是 Java 并发编程的革命性突破,也是很多 Java 开发者选择 21 的理由。</span></p>
<p class="md-end-block md-p"><span class="md-plain">什么是虚拟线程呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">想象一下,你是一家餐厅的老板。传统的线程就像是餐厅的服务员,假设每个服务员同时只能服务一桌客人。如果有 1000 桌客人,你就需要 1000 个服务员,但这显然不现实。餐厅地方不够,也负担不起那么多员工的工钱。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在传统的 Java 线程模型中也是如此。如果每个线程都对应操作系统的一个真实线程,创建成本很高、内存占用也大。当需要处理大量并发请求时,系统可能很快就会被拖垮。</span></p>
<p class="md-end-block md-p"><span class="md-plain">举个例子,假设开 1000 个线程同时处理网络请求:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">handleRequests() {<br><span>&nbsp; &nbsp;<span class="cm-keyword">for&nbsp;(<span class="cm-variable-3">int&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-number">0;&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">&lt;&nbsp;<span class="cm-number">1000;&nbsp;<span class="cm-variable">i<span class="cm-operator">++) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">Thread(()&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 发送网络请求,需要等待响应<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">httpClient.<span class="cm-variable">get(<span class="cm-string">"https://codefather.cn");<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"收到响应: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">result);<br><span>&nbsp; &nbsp; &nbsp; }).<span class="cm-variable">start();<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">创建 1000 个线程会消耗大量系统资源(因为对应 1000 个操作系统线程),而且大部分时间线程都在等待网络响应,很浪费。</span></p>
<p class="md-end-block md-p"><span class="md-plain">而虚拟线程就像是给餐厅引入了一个智能调度系统。服务员不再需要傻傻地等在客人桌边等菜上桌,而是可以在等待的时候去服务其他客人。当某桌的菜准备好了,系统会自动安排一个空闲的服务员去处理。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以开一个虚拟线程执行器执行同样的一批任务,这里我用的执行器会为每个任务生成一个虚拟线程来处理:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">handleRequestsWithVirtualThreads() {<br><span>&nbsp; &nbsp;<span class="cm-keyword">try&nbsp;(<span class="cm-variable">var&nbsp;<span class="cm-variable">executor&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Executors.<span class="cm-variable">newVirtualThreadPerTaskExecutor()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">for&nbsp;(<span class="cm-variable-3">int&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-number">0;&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">&lt;&nbsp;<span class="cm-number">1000;&nbsp;<span class="cm-variable">i<span class="cm-operator">++) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">executor.<span class="cm-variable">submit(()&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 同样的网络请求代码<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">httpClient.<span class="cm-variable">get(<span class="cm-string">"https://codefather.cn");<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"收到响应: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">result);<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br><span>&nbsp; &nbsp; &nbsp; }<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">同样是 1000 个,但是 1000 个虚拟线程只需要很少的系统资源(比如映射到 8 个操作系统线程上);而且当虚拟线程等待网络响应时,会让出底层的操作系统线程,操作系统线程就会自动切换去执行其他虚拟线程和任务。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966778797-5624b1c8-3bf9-449e-bc02-e5887bfb56bc.png"><img src="https://pic.yupi.icu/1/1756966778797-5624b1c8-3bf9-449e-bc02-e5887bfb56bc.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">总结一下 Virtual Threads 的核心优势。首先是 <span class="md-pair-s "><strong>超级轻量</strong><span class="md-plain">。一个传统线程可能需要几 MB 的内存,而一个虚拟线程只需要几 KB。你可以轻松创建百万级别的虚拟线程而不用担心系统资源。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">其次是 <span class="md-pair-s "><strong>编程简单</strong><span class="md-plain">。你不需要学习复杂的异步编程模式,跟创建一个普通线程的代码类似,一行代码就能提交异步任务。当遇到阻塞的 I/O 操作时,虚拟线程会自动让出底层的操作系统线程。</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 直接创建虚拟线程<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">handleSingleUser(<span class="cm-variable-3">Long&nbsp;<span class="cm-variable">userId) {<br><span>&nbsp; &nbsp;<span class="cm-variable">Thread.<span class="cm-variable">ofVirtual().<span class="cm-variable">start(()&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 要异步执行的任务<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">User&nbsp;<span class="cm-variable">user&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">userService.<span class="cm-variable">findById(<span class="cm-variable">userId);<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">processUser(<span class="cm-variable">user);<br><span>&nbsp; });<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">相关面试题:<span class="md-meta-i-cmd-link"><span class="md-plain">什么是协程?Java 支持协程吗?</span></span></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【必备】Switch 模式匹配</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 14 版本推出了 Switch 表达式,能够一行处理多个条件;Java 21 版本进一步优化了 Switch 的能力,新增了模式匹配特性,能够更轻松地根据对象的类型做不同的处理。</span></p>
<p class="md-end-block md-p"><span class="md-plain">没有 Switch 模式匹配时,我们需要利用 instanceof 匹配类型:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-def">processMessage(<span class="cm-variable-3">Object&nbsp;<span class="cm-variable">message) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">message&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable-3">String) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">textMessage&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable-3">String)&nbsp;<span class="cm-variable">message;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"文本消息:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">textMessage;<br><span>&nbsp; }&nbsp;<span class="cm-keyword">else&nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">message&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable-3">Integer) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">Integer&nbsp;<span class="cm-variable">numberMessage&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable-3">Integer)&nbsp;<span class="cm-variable">message;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"数字消息:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">numberMessage;<br><span>&nbsp; }&nbsp;<span class="cm-keyword">else&nbsp;<span class="cm-keyword">if&nbsp;(<span class="cm-variable">message&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable">List) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;?&gt;&nbsp;<span class="cm-variable">listMessage&nbsp;<span class="cm-operator">=&nbsp;(<span class="cm-variable">List<span class="cm-operator">&lt;?&gt;)&nbsp;<span class="cm-variable">message;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"列表消息,包含 "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">listMessage.<span class="cm-variable">size()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" 个元素";<br><span>&nbsp; }&nbsp;<span class="cm-keyword">else&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-string">"未知消息类型";<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">有了模式匹配,这段代码可以变得很优雅,直接在匹配对象类型的同时声明了变量(跟 instanceof 模式匹配有点像):</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-def">processMessage(<span class="cm-variable-3">Object&nbsp;<span class="cm-variable">message) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">message) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">text&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"文本消息:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">text;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">Integer&nbsp;<span class="cm-variable">number&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"数字消息:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">number;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">List<span class="cm-operator">&lt;?&gt;&nbsp;<span class="cm-variable">list&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"列表消息,包含 "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">list.<span class="cm-variable">size()&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-string">" 个元素";<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-atom">null&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"空消息";<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"未知消息类型";<br><span>&nbsp; };<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">此外,模式匹配还支持 <span class="md-pair-s "><strong>条件判断</strong><span class="md-plain">,让处理逻辑更加精细,相当于在 <span class="md-pair-s"><code>case ... when ...</code><span class="md-plain"> 中写 if 条件表达式(感觉有点像 SQL 的语法)。</span></span></span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 根据字符串长度采用不同处理策略<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-def">processText(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">text) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">text) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-variable">when&nbsp;<span class="cm-variable">s.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&lt;&nbsp;<span class="cm-number">10&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"短文本:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-variable">when&nbsp;<span class="cm-variable">s.<span class="cm-variable">length()&nbsp;<span class="cm-operator">&lt;&nbsp;<span class="cm-number">100&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"中等文本:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s.<span class="cm-variable">substring(<span class="cm-number">0,&nbsp;<span class="cm-number">5);<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"长文本:"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s.<span class="cm-variable">substring(<span class="cm-number">0,&nbsp;<span class="cm-number">10);<br><span>&nbsp; };<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】Record 模式</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Record 模式让数据的解构变得更简单直观,可以一次性取出 record 中所有需要的信息。</span></p>
<p class="md-end-block md-p"><span class="md-plain">举个例子,先定义一些简单的 Record:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">record&nbsp;<span class="cm-def">Person(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable-3">int&nbsp;<span class="cm-variable">age) {}<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">record&nbsp;<span class="cm-def">Address(<span class="cm-variable-3">String&nbsp;<span class="cm-variable">city,&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">street) {}<br><span><span class="cm-keyword">public&nbsp;<span class="cm-variable">record&nbsp;<span class="cm-def">Employee(<span class="cm-variable">Person&nbsp;<span class="cm-variable">person,&nbsp;<span class="cm-variable">Address&nbsp;<span class="cm-variable">address,&nbsp;<span class="cm-variable-3">double&nbsp;<span class="cm-variable">salary) {}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">使用 Record 模式可以直接解构这些数据,不用一层一层取了:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-def">analyzeEmployee(<span class="cm-variable">Employee&nbsp;<span class="cm-variable">emp) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">emp) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 一次性提取所有需要的信息<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">Employee(<span class="cm-variable">Person(<span class="cm-variable">var&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">age),&nbsp;<span class="cm-variable">Address(<span class="cm-variable">var&nbsp;<span class="cm-variable">city,&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">street),&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">salary)&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">when&nbsp;<span class="cm-variable">salary&nbsp;<span class="cm-operator">&gt;&nbsp;<span class="cm-number">50000&nbsp;<span class="cm-operator">-&gt;&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String.<span class="cm-variable">format(<span class="cm-string">"%s(%d岁)是高薪员工,住在%s%s,月薪%.0f",&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable">age,&nbsp;<span class="cm-variable">city,&nbsp;<span class="cm-variable">street,&nbsp;<span class="cm-variable">salary);<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable">Employee(<span class="cm-variable">Person(<span class="cm-variable">var&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">age),&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">address,&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">salary)&nbsp;<span class="cm-operator">-&gt;&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String.<span class="cm-variable">format(<span class="cm-string">"%s(%d岁)月薪%.0f,住在%s",&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="cm-variable">name,&nbsp;<span class="cm-variable">age,&nbsp;<span class="cm-variable">salary,&nbsp;<span class="cm-variable">address.<span class="cm-variable">city());<br><span>&nbsp; };<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">这种写法适合追求极致简洁代码的程序员,可以在一行代码中同时完成 <span class="md-pair-s "><strong>类型检查</strong><span class="md-plain">、<span class="md-pair-s "><strong>数据提取</strong><span class="md-plain"> 和 <span class="md-pair-s "><strong>条件判断</strong><span class="md-plain">。</span></span></span></span></span></span></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】有序集合</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 21 的有序集合为我们提供了更直观的方式来操作集合的头尾元素,说白了就是补了几个方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">tasks&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">new&nbsp;<span class="cm-variable">ArrayList<span class="cm-operator">&lt;&gt;();<br><span><span class="cm-variable">tasks.<span class="cm-variable">addFirst(<span class="cm-string">"鱼皮的任务"); &nbsp; &nbsp;<span class="cm-comment">// 添加到开头<br><span><span class="cm-variable">tasks.<span class="cm-variable">addLast(<span class="cm-string">"小阿巴的任务"); &nbsp;&nbsp;<span class="cm-comment">// 添加到结尾<br><span><span>​<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">firstStr&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">tasks.<span class="cm-variable">getFirst(); &nbsp;<span class="cm-comment">// 获取第一个<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">lastStr&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">tasks.<span class="cm-variable">getLast(); &nbsp;&nbsp;<span class="cm-comment">// 获取最后一个<br><span><span>​<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">removedFirst&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">tasks.<span class="cm-variable">removeFirst(); &nbsp;<span class="cm-comment">// 删除并返回第一个<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">removedLast&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">tasks.<span class="cm-variable">removeLast(); &nbsp; &nbsp;<span class="cm-comment">// 删除并返回最后一个<br><span><span>​<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">reversed&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">tasks.<span class="cm-variable">reversed(); &nbsp;&nbsp;<span class="cm-comment">// 反转列表</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">除了 List 之外,SequencedMap 接口(比如 LinkedHashMap)和 SequencedSet 接口(比如 LinkedHashSet)也新增了类似的方法。本质上都是实现了有序集合接口:</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756723729171-7d7dd87d-a602-4820-9278-8c7bb96211a8.png"><img src="https://pic.yupi.icu/1/1756723729171-7d7dd87d-a602-4820-9278-8c7bb96211a8.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】分代 ZGC</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 21 中的分代 ZGC 可以说是垃圾收集器领域的一个重大突破。ZGC 从 Java 11 开始就以其超低延迟而闻名,但是它并没有采用分代的设计思路。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在这之前,ZGC 对所有对象一视同仁,无论是刚创建的新对象还是存活了很久的老对象,都使用同样的收集策略。这虽然保证了一致的低延迟,但在内存分配密集的应用中,效率并不是最优的。</span></p>
<p class="md-end-block md-p"><span class="md-plain">分代 ZGC 的核心思想是基于一个现象 —— 大部分对象都是 “朝生夕死” 的。它将堆内存划分为年轻代和老年代两个区域,年轻代的垃圾收集可以更加频繁和高效,因为大部分年轻对象很快就会死亡,收集器可以快速清理掉这些垃圾;而老年代的收集频率相对较低,减少了对长期存活对象的不必要扫描。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756736338173-cd58e246-f3c5-4e44-96b2-6013b27eb4e4.png"><img src="https://pic.yupi.icu/1/1756736338173-cd58e246-f3c5-4e44-96b2-6013b27eb4e4.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 22</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】外部函数和内存 API</span></h3>
<p class="md-end-block md-p"><span class="md-plain">长期以来,Java 程序员想要调用 C/C++ 编写的本地库,只能依赖 JNI(Java Native Interface)。但说实话,JNI 的使用体验并不好,需要手写胶水代码、维护头文件和构建脚本、处理 JNIEnv 和复杂类型转换,一旦接口频繁变更,维护成本较高。</span></p>
<p class="md-end-block md-p"><span class="md-plain">外部函数与内存 API(FFM API)提供了标准化、类型安全的方式来从 Java 直接调用本地代码。FFM API 现在支持几乎所有主流平台,性能相比 JNI 可能有一定提升,特别是在频繁调用本地函数的场景下。</span></p>
<p class="md-end-block md-p"><span class="md-plain">大家不用记忆具体是怎么使用的,只要知道有这个特性就足够了。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】未命名变量和模式</span></h3>
<p class="md-end-block md-p"><span class="md-plain">在开发中,我们可能会遇到这样的情况:有些变量我们必须声明,但实际上并不会使用到它们的值。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在这之前,我们只能给这些不使用的变量起一个名字,代码会显得有些多余。举些例子:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">try&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable">processData();<br><span>}&nbsp;<span class="cm-keyword">catch&nbsp;(<span class="cm-variable">IOException&nbsp;<span class="cm-variable">ignored) { &nbsp;<span class="cm-comment">// 只关心异常发生,不关心异常对象<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"处理数据时出错了");<br><span>}<br><span><span>​<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">obj) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">Integer&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是整数: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">i;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是字符串: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">Double&nbsp;<span class="cm-variable">unused&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是浮点数"; &nbsp;<span class="cm-comment">// 不需要使用具体的值<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"未知类型";<br><span>};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">有了未命名变量特性,可以使用下划线 <span class="md-pair-s"><code>_</code><span class="md-plain"> 表示不使用的变量代码,意图更清晰:</span></span></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">try&nbsp;{<br><span>&nbsp; &nbsp;<span class="cm-variable">processData();<br><span>}&nbsp;<span class="cm-keyword">catch&nbsp;(<span class="cm-variable">IOException&nbsp;<span class="cm-variable">_) {&nbsp;<span class="cm-comment">// 不关心异常对象<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"处理数据时出错了");<br><span>}<br><span><span>​<br><span><span class="cm-variable-3">String&nbsp;<span class="cm-variable">result&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-keyword">switch&nbsp;(<span class="cm-variable">obj) {<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">Integer&nbsp;<span class="cm-variable">i&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是整数: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">i;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">String&nbsp;<span class="cm-variable">s&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是字符串: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">s;<br><span>&nbsp; &nbsp;<span class="cm-keyword">case&nbsp;<span class="cm-variable-3">Double&nbsp;<span class="cm-variable">_&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"这是浮点数"; &nbsp;<span class="cm-comment">// 只关心类型,不关心值<br><span>&nbsp; &nbsp;<span class="cm-keyword">default&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-string">"未知类型";<br><span>};<br><span><span>​<br><span><span class="cm-comment">// 在解构中也很有用<br><span><span class="cm-keyword">if&nbsp;(<span class="cm-variable">point&nbsp;<span class="cm-keyword">instanceof&nbsp;<span class="cm-variable">Point(<span class="cm-variable">var&nbsp;<span class="cm-variable">x,&nbsp;<span class="cm-variable">var&nbsp;<span class="cm-variable">_)) { &nbsp;<span class="cm-comment">// 只关心 x 坐标<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"x 坐标是: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">x);<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 23</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】ZGC 默认分代模式</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 22 引入了分代 ZGC,但当时你需要通过特殊的 JVM 参数来启用它:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span>java&nbsp;<span class="cm-attribute">-XX:<span class="cm-operator">+UseZGC&nbsp;<span class="cm-attribute">-XX:<span class="cm-operator">+UnlockExperimentalVMOptions&nbsp;<span class="cm-attribute">-XX:<span class="cm-operator">+UseGenerationalZGC MyApp</span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">而在 Java 23 中,分代模式成为了 ZGC 的默认行为。</span></p>
<p class="md-end-block md-p"><span class="md-plain">虽然听起来只是个小改动,但这个改变的背后是大量的性能测试和实际应用验证的结果。Oracle 的工程师们发现,分代 ZGC 在绝大多数应用场景中都能带来显著的性能改善,特别是在内存分配密集的应用中,性能提升可能达到数倍之多。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">Java 24</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】类文件 API</span></h3>
<p class="md-end-block md-p"><span class="md-plain">类文件 API 是一个专为框架和工具开发者设计的强大特性。长期以来,如果你想要在运行时动态生成、分析或修改 Java 字节码,就必须依赖像 ASM、Javassist 或者 CGLIB 这样的第三方库。</span></p>
<p class="md-end-block md-p"><span class="md-plain">而且操作字节码需要深入了解底层细节,学习难度很大,我只能借助 AI 来搞定。</span></p>
<p class="md-end-block md-p"><span class="md-plain">有了类文件 API,操作字节码变得简单了一些:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">byte[]&nbsp;<span class="cm-def">generateClass() {<br><span>&nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">ClassFile.<span class="cm-variable">of().<span class="cm-variable">build(<span class="cm-variable">ClassDesc.<span class="cm-variable">of(<span class="cm-string">"com.example.GeneratedClass"),&nbsp;<span class="cm-variable">cb&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 添加默认构造函数<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cb.<span class="cm-variable">withMethod(<span class="cm-string">"&lt;init&gt;",&nbsp;<span class="cm-variable">MethodTypeDesc.<span class="cm-variable">of(<span class="cm-variable">ConstantDescs.<span class="cm-variable">CD_void),&nbsp;<span class="cm-variable">ACC_PUBLIC,&nbsp;<span class="cm-variable">mb&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">mb.<span class="cm-variable">withCode(<span class="cm-variable">codeb&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">codeb.<span class="cm-variable">aload(<span class="cm-number">0)<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">invokespecial(<span class="cm-variable">ConstantDescs.<span class="cm-variable">CD_Object,&nbsp;<span class="cm-string">"&lt;init&gt;",&nbsp;<span class="cm-variable">MethodTypeDesc.<span class="cm-variable">of(<span class="cm-variable">ConstantDescs.<span class="cm-variable">CD_void))<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">return_();<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br><span>&nbsp; &nbsp; &nbsp; });<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 添加 sayHello 方法<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">cb.<span class="cm-variable">withMethod(<span class="cm-string">"sayHello",&nbsp;<span class="cm-variable">MethodTypeDesc.<span class="cm-variable">of(<span class="cm-variable">ConstantDescs.<span class="cm-variable">CD_String),&nbsp;<span class="cm-variable">ACC_PUBLIC,&nbsp;<span class="cm-variable">mb&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">mb.<span class="cm-variable">withCode(<span class="cm-variable">codeb&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">codeb.<span class="cm-variable">ldc(<span class="cm-string">"Hello from generated class!")<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">areturn();<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br><span>&nbsp; &nbsp; &nbsp; });<br><span>&nbsp; });<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">读取和分析现有的类文件也很简单:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-keyword">public&nbsp;<span class="cm-variable-3">void&nbsp;<span class="cm-def">analyzeClass(<span class="cm-variable-3">byte[]&nbsp;<span class="cm-variable">classBytes) {<br><span>&nbsp; &nbsp;<span class="cm-variable">ClassModel&nbsp;<span class="cm-variable">cm&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">ClassFile.<span class="cm-variable">of().<span class="cm-variable">parse(<span class="cm-variable">classBytes);<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"类名: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">cm.<span class="cm-variable">thisClass().<span class="cm-variable">asInternalName());<br><span>&nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"方法列表:");<br><span>&nbsp; &nbsp;<br><span>&nbsp; &nbsp;<span class="cm-keyword">for&nbsp;(<span class="cm-variable">MethodModel&nbsp;<span class="cm-variable">method&nbsp;:&nbsp;<span class="cm-variable">cm.<span class="cm-variable">methods()) {<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">" - "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">method.<span class="cm-variable">methodName().<span class="cm-variable">stringValue()&nbsp;<span class="cm-operator">+&nbsp;<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">method.<span class="cm-variable">methodType().<span class="cm-variable">stringValue());<br><span>&nbsp; }<br><span>}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">第三方字节码库可能需要一段时间才能跟上新特性的变化,而官方的类文件 API 则能够与语言特性同步发布,确保开发者能够使用最新的字节码功能。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Stream Gatherers 流收集器</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Stream API 自 Java 8 引入以来,极大地改变了我们处理集合数据的方式,但是在一些特定的场景中,传统的 Stream 操作就显得力不从心了。Stream Gatherers 正是对 Stream API 的一个重要扩展,它解决了现有 Stream API 在某些复杂数据处理场景中的局限性,补齐了 Stream API 的短板。</span></p>
<p class="md-end-block md-p"><span class="md-plain">如果你想实现一些复杂的数据聚合操作,比如滑动窗口或固定窗口分析,可以直接使用 Java 24 内置的 Gatherers。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-comment">// 1. 滑动窗口 - windowSliding(size)<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Double<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">prices&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-number">100.0,&nbsp;<span class="cm-number">102.0,&nbsp;<span class="cm-number">98.0,&nbsp;<span class="cm-number">105.0,&nbsp;<span class="cm-number">110.0);<br><span><span>​<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">Double<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">movingAverages&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">prices.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">gather(<span class="cm-variable">Gatherers.<span class="cm-variable">windowSliding(<span class="cm-number">3)) &nbsp;<span class="cm-comment">// 创建大小为 3 的滑动窗口<br><span>&nbsp; .<span class="cm-variable">map(<span class="cm-variable">window&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// window 是 List&lt;Double&gt; 类型,包含 3 个连续元素<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-variable">window.<span class="cm-variable">stream()<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">mapToDouble(<span class="cm-variable-3">Double::<span class="cm-variable">doubleValue)<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">average()<br><span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .<span class="cm-variable">orElse(<span class="cm-number">0.0);<br><span>&nbsp; })<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-string">"移动平均值: "&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">movingAverages);<br><span><span class="cm-comment">// 移动平均值: </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">还有更多方法,感兴趣的同学可以自己尝试:</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756737375561-78c967c0-db67-4261-b47f-21118e1ad504.png"><img src="https://pic.yupi.icu/1/1756737375561-78c967c0-db67-4261-b47f-21118e1ad504.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">除了内置的 Gatherers 外,还可以自定义 Gatherer,举一个最简单的例子 —— 给每个元素添加前缀。先自定义一个 Gatherer:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">Gatherer<span class="cm-operator">&lt;<span class="cm-variable-3">String,&nbsp;<span class="cm-operator">?,&nbsp;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">addPrefix&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Gatherer.<span class="cm-variable">ofSequential(<br><span>&nbsp; ()&nbsp;<span class="cm-operator">-&gt;&nbsp;<span class="cm-atom">null, &nbsp;<span class="cm-comment">// 不需要状态,所以初始化为 null<br><span>&nbsp; (<span class="cm-variable">state,&nbsp;<span class="cm-variable">element,&nbsp;<span class="cm-variable">downstream)&nbsp;<span class="cm-operator">-&gt;&nbsp;{<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 给每个元素添加 "前缀-" 并推送到下游<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">downstream.<span class="cm-variable">push(<span class="cm-string">"前缀-"&nbsp;<span class="cm-operator">+&nbsp;<span class="cm-variable">element);<br><span>&nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return&nbsp;<span class="cm-atom">true; &nbsp;<span class="cm-comment">// 继续处理下一个元素<br><span>&nbsp; }<br><span>&nbsp; &nbsp;<span class="cm-comment">// 不需要 finisher,省略第三个参数<br><span>);</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-pair-s"><code>Gatherer.ofSequential</code><span class="md-plain"> 方法会返回 Gatherer 接口的实现类:</span></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756737668628-5a4dbcf2-e8e0-42c6-8265-15c15a79fad2.png"><img src="https://pic.yupi.icu/1/1756737668628-5a4dbcf2-e8e0-42c6-8265-15c15a79fad2.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">然后就可以愉快地使用了:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">names&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">Arrays.<span class="cm-variable">asList(<span class="cm-string">"鱼皮",&nbsp;<span class="cm-string">"编程",&nbsp;<span class="cm-string">"导航");<br><span><span class="cm-variable">List<span class="cm-operator">&lt;<span class="cm-variable-3">String<span class="cm-operator">&gt;&nbsp;<span class="cm-variable">prefixedNames&nbsp;<span class="cm-operator">=&nbsp;<span class="cm-variable">names.<span class="cm-variable">stream()<br><span>&nbsp; .<span class="cm-variable">gather(<span class="cm-variable">addPrefix)<br><span>&nbsp; .<span class="cm-variable">collect(<span class="cm-variable">Collectors.<span class="cm-variable">toList());<br><span><span>​<br><span><span class="cm-variable">System.<span class="cm-variable">out.<span class="cm-variable">println(<span class="cm-variable">prefixedNames);<br><span><span class="cm-comment">// 输出: [前缀-鱼皮, 前缀-编程, 前缀-导航]</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">这个例子展示了 Gatherer 的最基本形态:</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">不需要状态:第一个参数返回 null,因为我们不需要维护任何状态</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">简单转换:第二个参数接收每个元素,做简单处理后推送到下游</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">无需收尾:省略第三个参数,因为不需要最终处理</span></p>
</li>
</ul>
<p class="md-end-block md-p"><span class="md-plain">虽然这个例子用 <span class="md-pair-s"><code>map()</code><span class="md-plain"> 也能实现,但它帮助我们理解了 Gatherer 的基本工作机制。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">这就是 Stream Gatherers 强大之处,它能够维护复杂的内部状态,并根据业务逻辑灵活地向下游推送结果,让原本需要手动循环的复杂逻辑变得简洁优雅。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Stream Gatherers 的另一个优势是它和现有的 Stream API 完全兼容。你可以在 Stream 管道中的任何位置插入 Gatherer 操作,就像使用 map、filter 或 collect 一样自然,让复杂的数据处理变得既强大又优雅。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<div class="md-hr md-end-block"><hr></div>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">OK 以上就是本期内容,学会的话记得点赞三连支持,我们下期见。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1756966748940-765231d5-5024-4164-b09f-76a9a3b13a1d.webp"><img src="https://pic.yupi.icu/1/1756966748940-765231d5-5024-4164-b09f-76a9a3b13a1d.webp"></span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">更多编程学习资源</span></h2>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">Java前端程序员必做项目实战教程+毕设网站</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">程序员免费编程学习交流社区(自学必备)</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">程序员保姆级求职写简历指南(找工作必备)</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">程序员免费面试刷题网站工具(找工作必备)</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新Java零基础入门学习路线 + Java教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新Python零基础入门学习路线 + Python教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新前端零基础入门学习路线 + 前端教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新数据结构和算法零基础入门学习路线 + 算法教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新C++零基础入门学习路线、C++教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新数据库零基础入门学习路线 + 数据库教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新Redis零基础入门学习路线 + Redis教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新计算机基础入门学习路线 + 计算机基础教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新小程序入门学习路线 + 小程序开发教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新SQL零基础入门学习路线 + SQL教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新Linux零基础入门学习路线 + Linux教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新Git/GitHub零基础入门学习路线 + Git教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新操作系统零基础入门学习路线 + 操作系统教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新计算机网络零基础入门学习路线 + 计算机网络教程</span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-meta-i-cmd-link"><span class="md-plain">最新设计模式零基础入门学习路线 + 设计模式教程</span></span></p>
</li>
<li class="md-list-item md-focus-container">
<p class="md-end-block md-p md-focus"><span class="md-meta-i-c md-link md-expand"><span class="md-plain">最新软件工程零基础入门学习路线 + 软件工程教程</span></span></p>
</li>
</ul><br><br>
来源:https://www.cnblogs.com/yupi/p/19075074
頁: [1]
查看完整版本: Java 8 终于要被淘汰了!带你速通 Java 8~24 新特性 | 又能跟面试官吹牛皮了