陈广荣 發表於 2025-9-17 11:00:00

刚刚 Java 25 炸裂发布!让 Java 再次伟大

<p class="md-end-block md-heading"><span class="md-plain">大家好,我是程序员鱼皮。</span></p>
<p class="md-end-block md-p"><span class="md-plain">刚刚,Java 25 正式发布!这是继 Java 21 之后,又一个 LTS 长期支持版本,也是 Java 开发者们最期待的版本之一。其中有个特性可以说是颠覆了我对 Java 的认知,让 Java 再次伟大!</span></p>
<p class="md-end-block md-p"><span class="md-plain">那么 Java 25 都发布了哪些新特性?有没有必要升级?</span></p>
<p class="md-end-block md-p"><span class="md-plain">一篇文章,带你速通 Java 新特性,学会后又能愉快地和面试官吹牛皮了~</span></p>
<blockquote>
<p class="md-end-block md-p"><span class="md-plain">推荐观看视频版:<span class="md-link md-pair-s">https://bilibili.com/video/BV1b5pCzGEPx</span></span></p>
</blockquote>
<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-20250915192012233.png"><img src="https://pic.yupi.icu/1/1756966310189-c3222d19-1a91-4d63-95f7-a8e5a6457653-20250915192012233.png"></span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">⭐️ 正式特性</span></h2>
<p class="md-end-block md-p"><span class="md-plain">这些特性在 Java 25 中正式稳定,可以在生产环境中放心使用。</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">【实用】Scoped Values 作用域值</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">估计你的答案是 ThreadLocal。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是你有没有想过,ThreadLocal 存在什么问题?</span></p>
<p class="md-end-block md-p"><span class="md-plain">举一个典型的 ThreadLocal 使用场景,在同一个请求内获取用户信息:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UserService {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> ThreadLocal&lt;String&gt; USER_ID = <span style="color: rgba(0, 0, 255, 1)">new</span> ThreadLocal&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
   
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> processRequest(String userId) {
       USER_ID.set(userId);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 写入</span>
<span style="color: rgba(0, 0, 0, 1)">       doWork();
       USER_ID.remove();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 问题:必须手动清理,容易忘记</span>
<span style="color: rgba(0, 0, 0, 1)">}
   
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doWork() {
       String userId </span>= USER_ID.get();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 读取</span>
       System.out.println("处理用户: " +<span style="color: rgba(0, 0, 0, 1)"> userId);
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 问题:其他代码可以随意修改</span>
       USER_ID.set("被篡改的值"<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">这段简单的代码其实暗藏玄鸡,可以看出 ThreadLocal 的痛点:</span></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">容易内存泄漏:必须手动调用 <span class="md-pair-s"><code>remove()</code></span></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">可以随意修改数据,可能导致不可预期的结果</span></p>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">此外,如果想让子线程也共享数据,每个子线程都要复制一份数据。如果你使用的是 Java 21 的虚拟线程,1000 个虚拟线程就要复制1000 次,性能很差。</span></p>
<div class="cnblogs_code">
<pre>InheritableThreadLocal&lt;String&gt; threadLocal = <span style="color: rgba(0, 0, 255, 1)">new</span> InheritableThreadLocal&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
threadLocal.set(</span>"用户数据"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 1000; i++<span style="color: rgba(0, 0, 0, 1)">) {
   Thread.ofVirtual().start(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
       String data </span>= threadLocal.get(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每个线程都有自己的副本</span>
<span style="color: rgba(0, 0, 0, 1)">});
}</span></pre>
</div>
<p><span class="md-plain">现在 Java 25 的 Scoped Values 特性转正了,能解决 ThreadLocal 的这些问题。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">什么是 Scoped Values?</span></h4>
<p class="md-end-block md-p"><span class="md-plain">Scoped Values 允许方法 <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">和传统的 ThreadLocal 相比,它不仅更安全,而且在虚拟线程环境下的内存开销要小很多。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Scoped Values 和 ThreadLocal 的写法很像:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> java.lang.ScopedValue.where;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UserService {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> ScopedValue&lt;String&gt; USER_ID =<span style="color: rgba(0, 0, 0, 1)"> ScopedValue.newInstance();
   
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> processRequest(String userId) {
       where(USER_ID, userId)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 写入并绑定作用域</span>
          .run(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">::doWork);
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自动清理,无需 remove()</span>
<span style="color: rgba(0, 0, 0, 1)">}
   
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doWork() {
       String userId </span>= USER_ID.get();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 读取</span>
       System.out.println("处理用户: " +<span style="color: rgba(0, 0, 0, 1)"> userId);
}
}</span></pre>
</div>
<p><span class="md-plain">这段代码中,我们使用 <span class="md-pair-s"><code>where().run()</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">和虚拟线程配合使用时,所有虚拟线程共享同一份数据,内存占用更小:</span></p>
<div class="cnblogs_code">
<pre>ScopedValue&lt;String&gt; scopedValue =<span style="color: rgba(0, 0, 0, 1)"> ScopedValue.newInstance();
where(scopedValue, </span>"用户数据").run(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> {
   </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 1000; i++<span style="color: rgba(0, 0, 0, 1)">) {
       Thread.ofVirtual().start(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         String data </span>= scopedValue.get(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 所有线程共享同一份数据</span>
<span style="color: rgba(0, 0, 0, 1)">      });
}
});</span></pre>
</div>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span style="font-family: &quot;PingFang SC&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 14px">&nbsp;</span></pre>
<h4 class="md-end-block md-heading"><span class="md-plain">使用方法</span></h4>
<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">除了 <span class="md-pair-s"><code>run()</code><span class="md-plain"> 方法,还可以使用 <span class="md-pair-s"><code>call()</code><span class="md-plain"> 方法来处理有返回值的场景:</span></span></span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String processWithResult(String input) {
   </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> where(CONTEXT, input)
      .call(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         String processed </span>=<span style="color: rgba(0, 0, 0, 1)"> doSomeWork();
         </span><span style="color: rgba(0, 0, 255, 1)">return</span> "结果: " +<span style="color: rgba(0, 0, 0, 1)"> processed;
      });
}</span></pre>
</div>
<p>&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">2)嵌套作用域</span></p>
<p class="md-end-block md-p"><span class="md-plain">支持在已有作用域内建立新的嵌套作用域:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> outerMethod() {
   where(X, </span>"hello").run(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> {
       System.out.println(X.get());</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出 "hello"</span>
       where(X, "goodbye").run(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         System.out.println(X.get());</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出 "goodbye"</span>
<span style="color: rgba(0, 0, 0, 1)">      });
       System.out.println(X.get());</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出 "hello"</span>
<span style="color: rgba(0, 0, 0, 1)">});
}</span></pre>
</div>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">3)多值绑定</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以在一个调用中绑定多个 Scoped Values,或者直接用类封装多个值:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">where(USER_ID, userId)
.where(REQUEST_ID, requestId)
.where(TENANT_ID, tenantId)
.run(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
       processRequest();
});</span></pre>
</div>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span style="font-family: &quot;PingFang SC&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 14px">&nbsp;</span></pre>
<p class="md-end-block md-p"><span class="md-plain">4)和结构化并发配合</span></p>
<p class="md-end-block md-p"><span class="md-plain">Scoped Values 和 Java 结构化并发 API 可以打个配合,子线程自动继承父线程的作用域值:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> handleRequest() {
   where(USER_ID, getCurrentUserId())
      .run(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         </span><span style="color: rgba(0, 0, 255, 1)">try</span> (var scope = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.ShutdownOnFailure()) {
               var userTask </span>= scope.fork(() -&gt; loadUser());      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子线程可以访问 USER_ID</span>
               var ordersTask = scope.fork(() -&gt; loadOrders());<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子线程可以访问 USER_ID</span>
<span style="color: rgba(0, 0, 0, 1)">               scope.join().throwIfFailed();
               </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Response(userTask.get(), ordersTask.get());
          }
      });
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">不过结构化并发这哥们也挺惨的,过了这么多个版本还没转正。等它转正了,感觉 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">虽然 Scoped Values 听起来比 ThreadLocal 更高级,但它不能 100% 替代 ThreadLocal。</span></p>
<p class="md-end-block md-p"><span class="md-plain">如果你要在线程中共享不可变数据、尤其是使用了虚拟线程的场景,建议使用 Scoped Values;但如果线程中共享的数据可能需要更新,那么还是使用 ThreadLocal,要根据实际场景选择。</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">模块导入声明特性(Module Import Declarations)虽然是首次亮相,但它的设计理念可以追溯到 Java 9 的模块系统。</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-image md-img-loaded" data-src="https://pic.yupi.icu/1/image-20250915192231092.png"><img src="https://pic.yupi.icu/1/image-20250915192231092.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">以前我们使用多个 Java 标准库的包需要大量的导入语句:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.Map;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.List;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.stream.Collectors;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.stream.Stream;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.util.function.Function;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.nio.file.Path;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.nio.file.Files;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.time.LocalDateTime;
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> java.time.format.DateTimeFormatter;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ... 还有更多</span></pre>
</div>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">现在可以一行导入整个模块:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> module java.base;</pre>
</div>
<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>java.se</code><span class="md-plain">),一次导入可以使用大量包:</span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> module java.se;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 导入 100 多个包</span>
<span style="color: rgba(0, 0, 0, 1)">​
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FullFeatureApp {
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以使用整个 Java SE 平台的所有公开 API</span>
}</pre>
</div>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span style="font-family: &quot;PingFang SC&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 14px">&nbsp;</span></pre>
<p class="md-end-block md-p"><span class="md-plain">不过我感觉这个特性会比较有争议,我记得大厂的 Java 规约中是禁止使用通配符 <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-plain">而模块导入的范围更大,类名冲突可能更严重。如果导入多个模块时遇到了同名类,还要再通过具体导入来解决:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> module java.base;      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 包括 java.util.List 接口</span>
<span style="color: rgba(0, 0, 255, 1)">import</span> module java.desktop;   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 包括 java.awt.List 类</span>
<span style="color: rgba(0, 0, 0, 1)">​
</span><span style="color: rgba(0, 0, 255, 1)">import</span> java.util.List;      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 明确指定使用 util 包的 List</span></pre>
</div>
<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 的刻板印象!</span></p>
<p class="md-end-block md-p"><span class="md-plain">之前不是都说 Java 入门比 Python 难么?一个简单的 Hello World 程序就要包含类、public、static、方法参数等概念。</span></p>
<p class="md-end-block md-p"><span class="md-plain">传统的 Hello World 程序:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> HelloWorld {
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
       System.out.println(</span>"Hello, World!"<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">用 Python 或者 JavaScript 直接写一行代码就完成了:</span></p>
<div class="cnblogs_code">
<pre>print("Hello World")</pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">在 Java 25 中,Hello World 程序可以直接简写为 3 行代码!</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main() {
   IO.println(</span>"Hello, World!"<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<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">但是你知道,这 3 含代码的含金量么?你知道 Java 为了支持简写成这 3 行代码付出了多少努力么?</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1757498995302-10020d06-b915-43ed-bb0d-8c90cd93a684-20250915192319631.png"><img src="https://pic.yupi.icu/1/1757498995302-10020d06-b915-43ed-bb0d-8c90cd93a684-20250915192319631.png"></span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">新的 IO 类</span></h4>
<p class="md-end-block md-p"><span class="md-plain">首先是 Java 25 在 <span class="md-pair-s"><code>java.lang</code><span class="md-plain"> 包中新增了 IO 类,提供更简单的控制台 I/O 操作:</span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main() {
   String name </span>= IO.readln("请输入您的姓名: "<span style="color: rgba(0, 0, 0, 1)">);
   IO.print(</span>"很高兴认识您,"<span style="color: rgba(0, 0, 0, 1)">);
   IO.println(name);
}</span></pre>
</div>
<p class="md-end-block md-p">&nbsp;</p>
<p class="md-end-block md-p"><span class="md-plain">IO 类的主要方法包括:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> print(Object obj);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> println(Object obj);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> println();
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String readln(String prompt);
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> String readln();</pre>
</div>
<p class="md-end-block md-p">&nbsp;</p>
<h4 class="md-end-block md-heading"><span class="md-plain">自动导入 java.base 模块</span></h4>
<p class="md-end-block md-p"><span class="md-plain">在紧凑源文件中,所有 <span class="md-pair-s"><code>java.base</code><span class="md-plain"> 模块导出的包都会自动导入,就像有这样一行代码:</span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> module java.base;</pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">也就是说,你可以直接使用 List、Map、Stream 等常用类:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main() {
   var fruits </span>= List.of("苹果", "香蕉", "橙子"<span style="color: rgba(0, 0, 0, 1)">);
   var lengths </span>=<span style="color: rgba(0, 0, 0, 1)"> fruits.stream()
      .collect(Collectors.toMap(
         fruit </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> fruit,
         String::length
      ));
   IO.println(</span>"水果及其长度: " +<span style="color: rgba(0, 0, 0, 1)"> lengths);
}</span></pre>
</div>
<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 再次伟大的关键,为什么呢?</span></p>
<p class="md-end-block md-p"><span class="md-plain">Java 的核心竞争优势在于它成熟完善的生态系统,但语法不够简洁;现在 Java 只要持续借鉴其他新兴编程语言的优秀设计和语法特性、给开发者提供平滑的技术升级路径,就还是会有很多开发者继续使用 Java,就不会被别的语言取代。</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 自诞生以来就存在的一个限制:构造函数中的 <span class="md-pair-s"><code>super()</code><span class="md-plain"> 或 <span class="md-pair-s"><code>this()</code><span class="md-plain"> 调用必须是第一条语句。</span></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">举个例子:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span> Employee <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Person {
   Employee(String name, </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> age) {
       </span><span style="color: rgba(0, 0, 255, 1)">super</span>(name, age);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 必须是第一条语句</span>
       <span style="color: rgba(0, 0, 255, 1)">if</span> (age &lt; 18 || age &gt; 67<span style="color: rgba(0, 0, 0, 1)">) {
         </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("员工年龄不符要求"<span style="color: rgba(0, 0, 0, 1)">);
      }
}
}</span></pre>
</div>
<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 25 打破了这个限制,引入了新的构造函数执行模型,分为两个阶段:</span></p>
<ol class="ol-list" start="">
<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>
</ol>
<p class="md-end-block md-p"><span class="md-plain">简单来说,允许在构造函数调用之前添加语句!</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span> Employee <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Person {
   String department;
   
   Employee(String name, </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> age, String department) {
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 序言阶段,可以校验参数</span>
       <span style="color: rgba(0, 0, 255, 1)">if</span> (age &lt; 18 || age &gt; 67<span style="color: rgba(0, 0, 0, 1)">) {
         </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("员工年龄必须在 18-67 之间"<span style="color: rgba(0, 0, 0, 1)">);
      }      
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以初始化字段</span>
       <span style="color: rgba(0, 0, 255, 1)">this</span>.department =<span style="color: rgba(0, 0, 0, 1)"> department;
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 验证通过后,再调用父类构造函数</span>
       <span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">(name, age);
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 尾声阶段,可以干点儿别的事</span>
       IO.println("新员工 " + name + " 已加入 " + department + " 部门"<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<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/1757498231220-cbce0e1b-966b-4ae0-84c7-746b5cfc0fed-20250915192412190.png"><img src="https://pic.yupi.icu/1/1757498231220-cbce0e1b-966b-4ae0-84c7-746b5cfc0fed-20250915192412190.png"></span></p>
<p class="md-end-block md-p"><span class="md-plain">此外,这个特性还能防止父类构造函数调用子类未初始化的方法:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Base {
   Base() {
       </span><span style="color: rgba(0, 0, 255, 1)">this</span>.show();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用可能被重写的方法</span>
<span style="color: rgba(0, 0, 0, 1)">}
   </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> show() {
       IO.println(</span>"Base.show()"<span style="color: rgba(0, 0, 0, 1)">);
}
}

</span><span style="color: rgba(0, 0, 255, 1)">class</span> Yupi <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Base {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String message;
   
   Yupi(String message) {
       </span><span style="color: rgba(0, 0, 255, 1)">this</span>.message = message;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 super() 之前初始化</span>
       <span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">();
}
   
   @Override
   </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> show() {
       IO.println(</span>"消息: " + message);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> message 已经正确初始化了</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">现在,当创建 Yupi 对象时,message 字段会在父类构造函数运行之前就被正确初始化,避免了打印 null 值的问题。</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">但是要注意,在序言阶段有一些限制:</span></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">不能使用 this 引用(除了字段赋值)</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>
</ol>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Example {
   </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> value;
   String name </span>= "default";<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 有初始化器</span>
<span style="color: rgba(0, 0, 0, 1)">   
   Example(</span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> val, String nm) {
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ✅ 允许:验证参数</span>
       <span style="color: rgba(0, 0, 255, 1)">if</span> (val &lt; 0) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IllegalArgumentException();
      
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ✅ 允许:初始化未初始化的字段</span>
       <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> val;
      
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ❌ 不允许:字段已有初始化器
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.name = nm;
      
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ❌ 不允许:调用实例方法
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.helper();</span>
      
       <span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">();
      
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ✅ 现在可以正常使用 this 了</span>
       <span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> nm;
       </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.helper();
}
   
   </span><span style="color: rgba(0, 0, 255, 1)">void</span> helper() { <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> }
}</span></pre>
</div>
<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">【了解】密钥派生函数 API</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 也顺应时代,推出了密钥派生函数(KDF),这是一种从初始密钥材料、盐值等输入生成新密钥的加密算法。</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">核心是 <span class="md-pair-s"><code>javax.crypto.KDF</code><span class="md-plain"> 类,提供了两个主要方法:</span></span></span></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">deriveKey() 生成 SecretKey 对象</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">deriveData() 生成字节数组</span></p>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">比如使用 HKDF(HMAC-based Key Derivation Function)算法:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建 HKDF 实例</span>
KDF hkdf = KDF.getInstance("HKDF-SHA256"<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 准备初始密钥材料和盐值</span>
<span style="color: rgba(0, 0, 255, 1)">byte</span>[] initialKeyMaterial = "my-secret-key"<span style="color: rgba(0, 0, 0, 1)">.getBytes();
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] salt = "random-salt"<span style="color: rgba(0, 0, 0, 1)">.getBytes();
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] info = "application-context"<span style="color: rgba(0, 0, 0, 1)">.getBytes();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建 HKDF 参数</span>
AlgorithmParameterSpec params =<span style="color: rgba(0, 0, 0, 1)"> HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加初始密钥材料</span>
.addSalt(salt)               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加盐值</span>
.thenExpand(info, 32);       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 扩展为 32 字节</span>
<span style="color: rgba(0, 0, 0, 1)">​
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 派生 AES 密钥</span>
SecretKey aesKey = hkdf.deriveKey("AES"<span style="color: rgba(0, 0, 0, 1)">, params);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者直接获取字节数据</span>
<span style="color: rgba(0, 0, 255, 1)">byte</span>[] derivedData = hkdf.deriveData(params);</pre>
</div>
<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 对象结构的同学应该知道,Java 对象除了存储数据外,还要通过 <span class="md-pair-s "><strong>对象头</strong><span class="md-plain"> 存储很多额外的信息,比如类型信息、GC 标记、锁状态等元数据。</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/1757496886241-04804619-e100-4841-9e30-9bc695064eec-20250915192441258.png"><img src="https://pic.yupi.icu/1/1757496886241-04804619-e100-4841-9e30-9bc695064eec-20250915192441258.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">比如下面这段代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Point {
   </span><span style="color: rgba(0, 0, 255, 1)">int</span> x, y;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实际数据只有 8 字节</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一堆 Point 对象</span>
List&lt;Point&gt; points = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 10000; i++<span style="color: rgba(0, 0, 0, 1)">) {
   points.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Point(i, i));<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每个对象实际占用 24 字节!</span>
}</pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">用来存储传统的对象头在 64 位系统上占 16 字节,对于只有 8 字节数据的 Point 来说,开销确实有点大。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Java 25 将紧凑对象头特性转正,把对象头从 16 字节压缩到 8 字节,减少了小对象的内存开销。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://pic.yupi.icu/1/1757497079312-00e687e9-ec43-4ba2-9616-a2eebf46b161-20250915192450771.png"><img src="https://pic.yupi.icu/1/1757497079312-00e687e9-ec43-4ba2-9616-a2eebf46b161-20250915192450771.png"></span></p>
<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-plain">使用了 JNI 直接操作对象头的代码</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">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Shenandoah 分代收集</span></h3>
<p class="md-end-block md-p"><span class="md-plain">对于 Java 这种自动垃圾收集的语言,我们经常遇到这种问题:GC 一运行,应用就卡顿。特别是占用大堆内存的应用,传统的垃圾收集器一跑起来,停顿时间可能多达几百毫秒甚至几秒。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Shenandoah 作为 Java 中延迟最低的垃圾收集器,在 Java 25 中 Shenandoah 的分代模式转为正式特性。</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>
<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-20250915192501864.png"><img src="https://pic.yupi.icu/1/1756736338173-cd58e246-f3c5-4e44-96b2-6013b27eb4e4-20250915192501864.png"></span></p>
<p class="md-end-block md-p"><span class="md-plain">经过大量测试,分代 Shenandoah 在保持超低延迟的同时,还能获得更好的吞吐量。对于延迟敏感的应用来说,这个改进还是很实用的。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是 Shenandoah 并不是默认的垃圾收集器,需要手动指定。而且分代模式也不是 Shenandoah 的默认模式,默认情况下 Shenandoah 还是使用单代模式。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">预览特性</span></h2>
<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>
<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 class="md-pair-s"><code>ExecutorService</code><span class="md-plain"> 实现并发,可能会写下面这种代码:</span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 传统写法</span>
Response handle() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> ExecutionException, InterruptedException {
   Future</span>&lt;String&gt; user = executor.submit(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> findUser());
   Future</span>&lt;Integer&gt; order = executor.submit(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> fetchOrder());
   
   String theUser </span>= user.get();   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果这里异常了</span>
   <span style="color: rgba(0, 0, 255, 1)">int</span> theOrder = order.get();    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个任务还在跑,造成线程泄漏!</span>
   
   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Response(theUser, theOrder);
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">上述代码存在一个问题,如果 <span class="md-pair-s"><code>findUser()</code><span class="md-plain"> 方法失败了,<span class="md-pair-s"><code>fetchOrder()</code><span class="md-plain"> 还在后台运行,浪费资源。而且如果当前线程被中断,子任务不会自动取消。</span></span></span></span></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">结构化并发就是来解决并发编程中 <span class="md-pair-s "><strong>线程泄露和资源管理</strong><span class="md-plain"> 问题的。它在 Java 25 中第 5 次预览,API 已经成熟,盲猜下个版本就要转正了。</span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">结构化并发的基本使用方法:</span></p>
<div class="cnblogs_code">
<pre>Response handle() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> InterruptedException {
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打开新作用域,打开作用域的线程是作用域的所有者。</span>
   <span style="color: rgba(0, 0, 255, 1)">try</span> (var scope =<span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.open()) {
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 fork 方法在作用域中开启子任务</span>
       var userTask = scope.fork(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> findUser());
       var orderTask </span>= scope.fork(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> fetchOrder());
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等待所有子任务完成或失败</span>
<span style="color: rgba(0, 0, 0, 1)">       scope.join();
       </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Response(userTask.get(), orderTask.get());
}
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 作用域结束时,所有子任务自动清理,不会泄漏</span>
}</pre>
</div>
<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-plain">自动清理:任一任务失败,其他任务自动取消</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">资源管理:可以配合 try-with-resources 保证资源释放</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">Java 25 提供了多种执行策略:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1. 默认策略:所有任务都要成功</span>
<span style="color: rgba(0, 0, 255, 1)">try</span> (var scope =<span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.open()) {
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 任一失败就全部取消</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2. 竞速策略:任一成功即可</span>
<span style="color: rgba(0, 0, 255, 1)">try</span> (var scope =<span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.open(Joiner.anySuccessfulResultOrThrow())) {
   var task1 </span>= scope.fork(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> callService1());
   var task2 </span>= scope.fork(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> callService2());
   var task3 </span>= scope.fork(() -&gt;<span style="color: rgba(0, 0, 0, 1)"> callService3());
   
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 谁先成功就用谁的结果,其他任务取消</span>
   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> scope.join();
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3. 收集所有结果(忽略失败)</span>
<span style="color: rgba(0, 0, 255, 1)">try</span> (var scope =<span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.open(Joiner.awaitAll())) {
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等待所有任务完成,不管成功失败</span>
}</pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">结构化并发搭配虚拟线程,可以轻松处理大量并发:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">void</span> processLotsOfRequests() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> InterruptedException {
   </span><span style="color: rgba(0, 0, 255, 1)">try</span> (var scope =<span style="color: rgba(0, 0, 0, 1)"> StructuredTaskScope.open()) {
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建上万个虚拟线程都没问题</span>
       <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 10000; i++<span style="color: rgba(0, 0, 0, 1)">) {
         </span><span style="color: rgba(0, 0, 255, 1)">int</span> requestId =<span style="color: rgba(0, 0, 0, 1)"> i;
         scope.fork(() </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> processRequest(requestId));
      }
       scope.join();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等待所有请求处理完成</span>
<span style="color: rgba(0, 0, 0, 1)">}
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自动清理,不用担心线程泄漏</span>
}</pre>
</div>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span style="font-family: &quot;PingFang SC&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 14px">&nbsp;</span></pre>
<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 优化过很多次模式匹配,可以利用 switch 和 instanceof 快速进行类型检查和转换。</span></p>
<p class="md-end-block md-p"><span class="md-plain">比如:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String processMessage(Object message) {
   </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (message) {
       </span><span style="color: rgba(0, 0, 255, 1)">case</span> String text -&gt; "文本消息:" +<span style="color: rgba(0, 0, 0, 1)"> text;
       </span><span style="color: rgba(0, 0, 255, 1)">case</span> Integer number -&gt; "数字消息:" +<span style="color: rgba(0, 0, 0, 1)"> number;
       </span><span style="color: rgba(0, 0, 255, 1)">case</span> List&lt;?&gt; list -&gt; "列表消息,包含 " + list.size() + " 个元素"<span style="color: rgba(0, 0, 0, 1)">;
       </span><span style="color: rgba(0, 0, 255, 1)">case</span>-&gt; "空消息"<span style="color: rgba(0, 0, 0, 1)">;
       </span><span style="color: rgba(0, 0, 255, 1)">default</span> -&gt; "未知消息类型"<span style="color: rgba(0, 0, 0, 1)">;
};
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">但是目前模式匹配只能用于引用类型,如果你想在 switch 中匹配基本类型,只能用常量,不能绑定变量。</span></p>
<p class="md-end-block md-p"><span class="md-plain">支持基本类型模式匹配后,switch 中可以使用基本类型:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (value) {
   </span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(0, 0, 255, 1)">int</span> i when i &gt; 100 -&gt; "大整数: " +<span style="color: rgba(0, 0, 0, 1)"> i;
   </span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(0, 0, 255, 1)">int</span> i -&gt; "小整数: " +<span style="color: rgba(0, 0, 0, 1)"> i;
   </span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(0, 0, 255, 1)">float</span> f -&gt; "浮点数: " +<span style="color: rgba(0, 0, 0, 1)"> f;
   </span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(0, 0, 255, 1)">double</span> d -&gt; "双精度: " +<span style="color: rgba(0, 0, 0, 1)"> d;
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">注意这里的 <span class="md-pair-s"><code>int i</code><span class="md-plain">,变量 <span class="md-pair-s"><code>i</code><span class="md-plain"> 绑定了匹配的值,可以直接使用。</span></span></span></span></span></p>
<p class="md-end-block md-p"><span class="md-plain">而且基本类型模式会检查转换是否安全,这比手动写范围检查方便多了!</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">int</span> largeInt = 1000000<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查能否安全转换为 byte</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (largeInt <span style="color: rgba(0, 0, 255, 1)">instanceof</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)"> b) {
   IO.println(</span>"可以安全转换为 byte: " +<span style="color: rgba(0, 0, 0, 1)"> b);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
   IO.println(</span>"转换为 byte 会丢失精度");<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个会执行</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查 int 转 float 是否会丢失精度</span>
<span style="color: rgba(0, 0, 255, 1)">int</span> preciseInt = 16777217;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2^24 + 1</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (preciseInt <span style="color: rgba(0, 0, 255, 1)">instanceof</span> <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)"> f) {
   IO.println(</span>"转换为 float 不会丢失精度"<span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
   IO.println(</span>"转换为 float 会丢失精度");<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个会执行</span>
}</pre>
</div>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded"><span style="font-family: &quot;PingFang SC&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 14px">&nbsp;</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Stable Values 稳定值</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">不信我先考考大家:final 字段有什么问题?</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>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> OrderController {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger logger = Logger.create(OrderController.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">这段代码中,logger 必须立刻初始化,如果创建 logger 很耗时,所有实例都要等待,可能影响启动性能。</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>
<p class="md-end-block md-p"><span class="md-plain">Stable Values 可以解决上述问题,提供 <span class="md-pair-s "><strong>延迟初始化的不可变性</strong><span class="md-plain">:</span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> OrderController {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> StableValue&lt;Logger&gt; logger =<span style="color: rgba(0, 0, 0, 1)"> StableValue.of();
   
   Logger getLogger() {
       </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 只在首次使用时初始化,之后直接返回同一个实例</span>
       <span style="color: rgba(0, 0, 255, 1)">return</span> logger.orElseSet(() -&gt; Logger.create(OrderController.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">));
}
}</span></pre>
</div>
<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>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> OrderController {
   </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Supplier&lt;Logger&gt; logger =<span style="color: rgba(0, 0, 0, 1)">
       StableValue.supplier(() </span>-&gt; Logger.create(OrderController.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">));
   
   </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> logOrder() {
       logger.get().info(</span>"处理订单");<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自动延迟初始化</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">还支持集合的延迟初始化:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionPool {
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 延迟创建连接池,每个连接按需创建</span>
   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> List&lt;Connection&gt; connections =<span style="color: rgba(0, 0, 0, 1)">
       StableValue.list(POOL_SIZE, index </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> createConnection(index));
   
   </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Connection getConnection(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> index) {
       </span><span style="color: rgba(0, 0, 255, 1)">return</span> connections.get(index);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一次访问才创建这个连接</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p class="md-end-block md-p"><span class="md-plain">重点是,Stable Values 底层使用 JVM 的 <span class="md-pair-s"><code>@Stable</code><span class="md-plain"> 注解,享受和 final 字段一样的优化(比如常量折叠),所以不用担心性能。</span></span></span></p>
<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-plain">创建成本高的对象</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></p>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">其他特性</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】移除 32 位 x86 支持</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Java 25 正式移除了对 32 位 x86 架构的支持。</span></p>
<p class="md-end-block md-p"><span class="md-plain">简单来说就是:32 位系统现在用不了 Java 25 了。</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">【了解】JFR 性能分析增强</span></h3>
<p class="md-end-block md-p"><span class="md-plain">JFR(Java Flight Recorder)飞行记录器是 JDK 内置的低开销性能监控工具,可以记录程序运行时的详细数据。</span></p>
<p class="md-end-block md-p"><span class="md-plain">它在这个版本获得了几个重要增强,包括更精确地测量 CPU 使用情况、通过协作式采样保证了 JVM 安全性、可以更安全地在生产环境开启分析等。</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h3 class="md-end-block md-heading"><span class="md-plain">【了解】Vector API(第 10 次孵化)</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Vector API 继续孵化,主要改进:</span></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">更好的数学函数支持:现在通过 FFM API 调用本地数学库,提高可维护性</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Float16 支持增强:在支持的 CPU 上可以自动向量化 16 位浮点运算</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">VectorShuffle 增强:支持与 MemorySegment 交互</span></p>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">这个 API 对高性能计算、机器学习等领域很重要,尤其是现在 AI 的发展带火了向量运算。但是我想说真的别再孵化了,等你转正了,黄花菜都凉了。</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">以上就是 Java 25 的新特性了,不知道大家有什么想法,可以在评论区留言分享。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我的评价是:虽然 Java 25 在开发体验、性能优化和并发编程方面都有进步,雨露均沾,但是目前对于新项目选择 Java 21 就够了,老项目还是可以继续用 Java 8,真没必要升级到 Java 25。</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是建议大家还是把 Java 8 之后的 Java 新特性都了解一下,拓宽下知识面,面试的时候也能跟面试官多聊几句。我也把免费 Java 教程和 Java 新特性大全都整理到编程导航上了:</span></p>
<blockquote>
<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>
</blockquote>
<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-20250915192748270.png"><img src="https://pic.yupi.icu/1/1756966558937-0919fdd6-e86c-487f-b61e-2b83612d7284-20250915192748270.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-image md-img-loaded" data-src="https://pic.yupi.icu/1/%E6%B1%82%E7%82%B9%E8%B5%9E.webp"><img src="https://pic.yupi.icu/1/%E6%B1%82%E7%82%B9%E8%B5%9E.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/19095054
頁: [1]
查看完整版本: 刚刚 Java 25 炸裂发布!让 Java 再次伟大