您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

JDK11 升级 JDK17 最全实践干货来了

时间:2023-11-15 14:54:10  来源:京东云开发者  作者:

1、前言

 

上篇文章给大家带来了 JDK8 升级 JDK11 的最全实践,相信大家阅读后已经对 JDK11 有了比较深入的了解。2021 年 9 月 14 日,Oracle 发布了可以长期支持的 JDK17 版本,那么从 JDK11 到 JDK17,到底带来了哪些特性呢?亚毫秒级的 ZGC 效果到底怎么样呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的 JDK11 升级 JDK17 最全实践。

2、为什么升级 JDK17

1)长期支持版本

JDK17 是 Oracle 官方在 2021 年 9 月 14 日发布的一个长期支持(LTS)版本,意味着它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性。

2)性能提升

更好的垃圾回收器。综合评估,从 JAVA 8 升级到 Java 11,**G1GC 平均速度提升 16.1%,ParallelGC 为 4.5%****,** 从 Java 11 升级到 Java 17,G1GC 平均速度提升 8.66%,ParallelGC 为 6.54%(基于 OptaPlanner 的用例基准测试表明)

最大的亮点是带来了稳定版的 ZGC 垃圾回收器,达到亚毫秒级停顿。

3)新语法和特性

Switch 表达式简化、Text Blocks 文本块、instanceof 的模式匹配升级和 NullPointerException 提示信息改进等

4)支持最新的技术和框架

Spring framework6 和 Spring Boot3 都默认使用 Java 17 作为最低版本

3、升级后压测效果

先给出结论:
1、JDK17 相对于 JDK8 和 JDK11, 所有垃圾回收器的性能都有很明显的提升,特别是稳定版的 ZGC 垃圾回收器
2、 不论任何机器配置下,都推荐使用 ZGC,ZGC 的停顿时间达到亚毫秒级,吞吐量也比较高

我在 JDOS 平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用 JDK8、JDK11 和 JDK17 进行部署和压测。

整个压测过程限时 60 分钟,用 180 个虚拟用户并发请求一个接口,每次接口请求都创建 512Kb 的数据。最终产出不同 GC 回收器的各项指标数据,来分析 GC 的性能提升效果。

以下是压测的性能情况:

JDK11 升级 JDK17 最全实践干货来了

4、OracleJDK 和 OpenJDK 的选择

2021 年 9 月,Oracle 宣布 JDK17 可以免费商用,直到下一个 LTS 版本之后继续提供整整一年,同时 Oracle 将继续按照自 Java 9 以来的相同版本和时间表提供 GPL 下的 Oracle OpenJDK 版本。

2023 年 9 月,OracleJDK 发布了新的 LTS 版本 JDK21,这就意味着从 2024 年 9 月开始,在生产环境使用 OracleJDK17 将需要付费。

JDK11 升级 JDK17 最全实践干货来了

参考: https://www.oracle.com/hk/java/technologies/downloads/#java17

OracleJDK 和 OpenJDK 这两个之间没有真正的技术差别,因为针对 Oracle JDK 构建过程是基于 OpenJDK 的。自从 JDK11 开始,OracleJDK 和 OpenJDK 在功能上基本相同,所以推荐使用 OpenJDK17 或其他开源的 JDK 版本,这些开源版本都是基于 OpenJDK 构建并提供长期支持的,比如:AdoptOpenJDK、RedHatOpenJDK。

JDK11 升级 JDK17 最全实践干货来了

官方参考: https://blogs.oracle.com/java/post/oracle-jdk-releases-for-java-11-and-later

5、JDK11 到 JDK17 带来了哪些新特性

5.1、JVM 改进

1、ZGC 垃圾回收器从实验性功能更改为正式产品功能,从 JDK11 引入以来,经过持续的迭代升级,目前已经足够稳定。需要手动开启,开启方式:-XX:+UseZGC

2、G1 垃圾回收器仍然作为默认垃圾回收器,进行改进升级,主要包括可中止的混合收集集合、NUMA 可识别内存分配等

3、JDK14 开始删除 CMS 垃圾回收器

4、JDK14 开始弃用 ParallelScavenge 和 SerialOld GC 的组合使用

5、JDK15 禁用偏向锁,默认禁用:-XX:+UseBiasedLocking

6、NullPointerException 提示信息改进

JDK14 以前的出现 NullPointerException 时,只能定位到所在异常行,无法定位具体是哪个变量。改进后的 NullPointerException,可以清晰描述具体变量,提升了空指针异常的可读性。

JDK11 升级 JDK17 最全实践干货来了

5.2、新语法特性

5.2.1、Switch 表达式简化

switch 表达式带来了简化式的编码方式,提供了新的分支切换方式,即 -> 符号,右则表达式方法体在执行完分支方法之后,自动结束 switch 分支,同时 -> 右则方法块中可以是表达式、代码块或者是手动抛出的异常

参考: https://openjdk.org/jeps/361

传统写法

JDK11 升级 JDK17 最全实践干货来了

新写法

JDK11 升级 JDK17 最全实践干货来了

5.2.2、Text Blocks 文本块

参考: https://openjdk.org/jeps/378

通过编写 """,来减少转义字符和换行符,达到简化代码和提高代码可读性的目的

JDK11 升级 JDK17 最全实践干货来了

5.2.3、Record 类型

参考: https://openjdk.org/jeps/395

record 是 JDK 14 引入的关键字,用于声明不可变的数据类。它适用于存储纯粹的值类型数据,如接口传输数据、坐标点和只读的日志记录。与 lombok 相比,record 简化了定义纯粹数据类型的过程。由于 record 类是不可变的,成员变量只能设置一次且无法更改,无需提供显式的 setter () 方法。

1、定义 Point 类,使用关键字 record,未定义 get/set

JDK11 升级 JDK17 最全实践干货来了

2、查看编译后的字节码文件

JDK11 升级 JDK17 最全实践干货来了

JDK11 升级 JDK17 最全实践干货来了

3、使用 Point 类

JDK11 升级 JDK17 最全实践干货来了

5.2.4、instanceof 的模式匹配升级

  • instanceof 类型判断再也不需要强制转换

参考: https://openjdk.org/jeps/394

JDK11 升级 JDK17 最全实践干货来了

5.2.5、密封的类和接口

参考: https://openjdk.org/jeps/409

JDK15 开始,引入了 sealed 普通类或接口类,这些类只允许被指定的类或者 interface 进行扩展和实现。

使用修饰符 sealed,您可以将一个类声明为密封类。密封的类使用关键字 permits 列出可以直接扩展它的类。子类可以是最终的,非密封的或密封的

比较实用的一个特性,可以用来限制类的层次结构

JDK11 升级 JDK17 最全实践干货来了

5.2.6、其他优化和升级

感兴趣的同学,推荐阅读 OpenJDK 官方文档说明,从 JDK11 到 JDK17 的改动: https://openjdk.org/projects/jdk/17/jeps-since-jdk-11

6、升级步骤

6.1、JDK 选择

OpenJDK17 下载:https://jdk.java.NET/archive/

行云镜像:jdt-base-Tomcat/java-jdt-centos7.4-openjdk-17.0.2-tomcat8.0.53

6.2、pom 编译配置升级

maven 编译所需 JDK 升级至 17

<properties>

<maven.compiler.source>17</maven.compiler.source>

<maven.compiler.target>17</maven.compiler.target>

</properties>

6.3、SpringBoot 升级

SpringBoot 版本升级到 2.7.15,Spring 版本升级为 5.3.29

为什么不升级到 SpringBoot3?

Spring Boot 3.0 最低要求 Java 17,SpringBoot3.0 带来了很多变化,和 SpringBoot2 差异较大。 考虑到公司很多中间件都是基于 SpringBoot2 构建的,所以此处推荐升级到 SpringBoot2 的最高版本 2.7.15。

POM 升级

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.7.15</version>

</parent>

也可以通过设置 dependencyManagement 的方式:

<properties>

<!-- 框架版本配置-->

<springboot-version>2.7.15</springboot-version>

<springframework.version>5.3.29</springframework.version>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>${springboot-version}</version>

<scope>import</scope>

<type>pom</type>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-framework-bom</artifactId>

<version>${springframework.version}</version>

<scope>import</scope>

<type>pom</type>

</dependency>

</dependencies>

</dependencyManagement>

参考:

spring 升级指南: https://Github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions

springboot 版本官网: https://spring.io/projects/spring-boot#learn

循环依赖问题

SpringBoot 升级到 2.7.15 后,如果应用中存在循环依赖的问题,启动时会报如下错误:

JDK11 升级 JDK17 最全实践干货来了

原因:官方文档不鼓励循环依赖引用,默认情况下是禁止的

解决方案:

第一种:推荐更新应用中 bean 的依赖关系来解决

第二种:配置文件中加入以下配置,为了和旧版本保持一致,此配置推荐添加

#放开循环依赖

spring.mAIn.allow-circular-references=true

6.4、常用中间件升级

6.4.1、Lombok 版本升级到 1.18.20 以上

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<version>1.18.20</version>

</dependency>

如果不升级,编译时会报错如下:

JDK11 升级 JDK17 最全实践干货来了

6.4.2、swgger 问题,springfox3.0.0 和 springboot2.7 版本不兼容

异常:

Failed to start bean 'documentationPluginsBootstrApper'; nested exception is java.lang.NullPointerException:

Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null

解决方案:

/**

* 增加如下配置可解决Spring Boot 2.7.15 与Swagger 3.0.0 不兼容问题

**/

@Bean

public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {

return new BeanPostProcessor() {

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {

customizeSpringfoxHandlerMappings(getHandlerMappings(bean));

}

return bean;

}

private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {

List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());

mappings.clear();

mappings.addAll(copy);

}

@SuppressWarnings("unchecked")

private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {

try {

Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");

field.setAccessible(true);

return (List<RequestMappingInfoHandlerMapping>) field.get(bean);

} catch (IllegalArgumentException | IllegalAccessException e) {

throw new IllegalStateException(e);

}

}

};

}

参考:https://developer.aliyun.com/article/950787

6.4.3、AKS 升级(针对直接从 JDK8 升级的情况)

异常:Causedby: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

原因:Java11 删除了 Java EE modules,其中就包括 java.xml.bind (JAXB)。

解决方案:

手动引入如下包即可

<!-- API, java.xml.bind module -->

<dependency>

<groupId>jakarta.xml.bind</groupId>

<artifactId>jakarta.xml.bind-api</artifactId>

<version>2.3.2</version>

</dependency>

<!-- Runtime, com.sun.xml.bind module -->

<dependency>

<groupId>org.glassfish.jaxb</groupId>

<artifactId>jaxb-runtime</artifactId>

<version>2.3.2</version>

</dependency>

6.4.4、Concrete 配置中心阻塞升级

使用 Concrete 时,启动时异常:

Unable to make field private static final java.lang.reflect.Method jdk.proxy2.$Proxy97.m0 accessible:

module jdk.proxy2 does not "opens jdk.proxy2" to unnamed module @61d47554

原因:

分析下 Concrete 报错的原因,如下图,包内 com.wangyin.concrete.spring.ConcreteConfigProcessor#postProcessAfterInitialization(212 行)的实现逻辑

JDK11 升级 JDK17 最全实践干货来了

JDK11 升级 JDK17 最全实践干货来了

解决方案:

1、在 JVM 启动参数中设置 --add-opens jdk.proxy2 来开启私有字段的访问,但因为动态代理生成的包名是随机不明确的,所以这种方案不可行。JDK 官方文档也明确表示不支持访问动态代理内部的随机字段。官方说明:https://cr.openjdk.org/~mr/jigsaw/spec/api/java/lang/reflect/Proxy.html

2、代码修改,只需把 f.setAccessible (true) 移到 Modifier.isStatic (f.getModifiers ()) 的判断下方即可。原因是方法 Modifier.isStatic (f.getModifiers ()) 本来就要跳过静态字段,这样修改直接避免了访问。推动 concrete 团队修复问题或更换使用 Ducc 配置中心

6.5、JVM 启动参数配置

6.5.1、开启 ZGC

启动参数中配置:-XX:+UseZGC

移除 - XX:ConcGCThreads,行云部署下 JVM 参数配置需要清除

JDK11 升级 JDK17 最全实践干货来了

6.5.2、不同中间件所需启动参数

升级 JDK17 后,项目启动时可能会遇到如下两种类型的异常:

1、cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @0x2611f533

2、Unable to make field final int java.math.BigInteger.signum accessible: module java.base does not "opens java.math" to unnamed module @525f1e4e

异常原因:

自从 JDK9 中引入了模块化功能后,再到 JDK17,对于包扫描和反射的权限控制更加的严格。常见的库比如(Spring)大量用到包扫描和反射,所以常出现此错误。

解决方案:

一个粗暴的解决办法是将没开放的 module 强制对外开放,即保持和 Java9 之前的版本一致。

  • --add-exports 导出包,意味着其中的所有公共类型和成员都可以在编译和运行时访问。
  • --add-opens 打开包,意味着其中的所有类型和成员(不仅是公共类型)都可以在运行时访问。

主要区别在于 --add-opens 允许 “深度反射”,即非公共成员的访问,才可以调用 setAccessible(true)

参考: https://stackoverflow.com/questions/44056405/whats-the-difference-between-add-exports-and-add-opens-in-java-9

SGM 需要加入:

--add-opens java.management/java.lang.management=ALL-UNNAMED

--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED

--add-opens java.management/sun.management=ALL-UNNAMED

R2M 需要加入:

--add-opens java.base/java.time=ALL-UNNAMED

Ducc 需要加入:

--add-opens java.base/java.util.concurrent=ALL-UNNAMED

--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED

--add-opens java.base/java.security=ALL-UNNAMED

--add-opens java.base/jdk.internal.loader=ALL-UNNAMED

--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED

--add-opens java.base/java.net=ALL-UNNAMED

--add-opens java.base/sun.nio.ch=ALL-UNNAMED

AKS 需要加入:

--add-exports java.base/sun.security.action=ALL-UNNAMED

--add-opens java.base/java.lang=ALL-UNNAMED

--add-opens java.base/java.math=ALL-UNNAMED

--add-opens java.base/java.util=ALL-UNNAMED

--add-opens java.base/sun.util.calendar=ALL-UNNAMED

6.6、启动后的验证

1. 推荐先升级 JDK11,再到 JDK17,一边升级一边进行验证观察

2. 观察日志是否有异常,特别是上面说到的启动时异常

3. 观察监控类软件,比如 SGM、UMP 等监控是否正常

4. 推荐逐步有序切量,并做好常态化压测,防止影响核心业务

5. 升级完成后,最好能做个全流程的功能测试,防止功能异常

7、总结

1、升级后,除了可以使用新的语法特性,最大的亮点是可以使用亚毫秒级停顿的 GC 性能(至少百倍的 GC 性能提升),所以 强烈建议升级到 JDK17
2、整个升级过程并不复杂,主要涉及到中间件版本的升级和启动参数的配置

如果还停留在 JDK8,推荐先升级 JDK11,再到 JDK17,具体升级步骤先参考我的上篇文章 “JDK8 升级 JDK11 最全实践干货来了”,再参考本章中的升级步骤。

希望以上分享可以给大家带来实际的帮助,升级过程中如果遇到问题,欢迎大家在评论区回复。

 

作者:京东科技 曲振富
来源:京东云开发者社区 转载请注明来源


Tags:JDK   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  Search: JDK  点击:(88)  评论:(0)  加入收藏
响应式编程又变天了?看JDK21虚拟线程如何颠覆!
命令式风格编程一直深受开发者喜爱,如 if-then-else、while 循环、函数和代码块等结构使代码易理解、调试,异常易追踪。然而,像所有好的东西一样,通常也有问题。这种编程风格导...【详细内容】
2023-12-28  Search: JDK  点击:(100)  评论:(0)  加入收藏
JDK11 升级 JDK17 最全实践干货来了
1、前言 上篇文章给大家带来了 JDK8 升级 JDK11 的最全实践,相信大家阅读后已经对 JDK11 有了比较深入的了解。2021 年 9 月 14 日,Oracle 发布了可以长期支持的 JDK17 版本,那...【详细内容】
2023-11-15  Search: JDK  点击:(329)  评论:(0)  加入收藏
一起来了解JDK1.8中的CAS与synchronized机制
在JDK1.8 中,ConcurrentHashMap是一个非常重要的线程安全的Map 类型。它采用了CAS 和synchronized 两种机制来实现线程安全,以保证在多线程环境下数据的一致性和正确性。首先,...【详细内容】
2023-11-01  Search: JDK  点击:(303)  评论:(0)  加入收藏
JDK21 性能提升 20 倍
JDK21 发布已经过去1个月时间了,除了每次发版必然更新的GC机制,和一些增强功能外,还引入了一个全新的概念&mdash;&mdash;虚拟线程。什么是虚拟线程先来看一下官方对虚拟线程(Vis...【详细内容】
2023-10-20  Search: JDK  点击:(242)  评论:(0)  加入收藏
一文详解 JDK1.8 的 Lambda、Stream、LocalDateTime
今天跟小伙伴们聊聊 Java中JDK1.8的一些新语法特性使用,主要是Lambda、Stream和LocalDate日期的一些使用讲解。LambdaLambda介绍Lambda 表达式(lambda expression)是一个匿名...【详细内容】
2023-10-10  Search: JDK  点击:(331)  评论:(0)  加入收藏
JDK命令行工具详解,这四个工具你都会用吗?
环境:JDK1.8.0_92JDK(Java Development Kit)提供了一系列的命令行工具,用于帮助开发人员进行问题排查。以下是关于JDK命令行工具问题排查的一些常见操作: 使用jps查看虚拟机进程:j...【详细内容】
2023-10-09  Search: JDK  点击:(298)  评论:(0)  加入收藏
JDK为什么废弃永久代,而引入元空间
今天我们来说说这个 JVM 的相关知识,因为面试简直是问到麻木的问题,那就是关于 JVM 的相关知识,今天了不起再次来和大家聊一下这个知识,我们从一些比较奇怪的问题说起,也不说那些...【详细内容】
2023-09-28  Search: JDK  点击:(258)  评论:(0)  加入收藏
图文并茂解释 Java JVM、JRE 和 JDK
在本章中,我们将讨论 Java 编程语言中 JVM、JRE 和 JDK 的一个重要定义。我们还讨论了 JVM、JRE 和 JDK 之间的区别。Java 技术既是一种编程语言又是一个平台。Java 编程语言...【详细内容】
2023-09-27  Search: JDK  点击:(297)  评论:(0)  加入收藏
如何给Alpine Linux安装Oracle JDK
Alpine使用的不是正统的glibc,对于一些强依赖glibc的系统建议不要使用Alpine,比如使用了Oracle JDK的系统,建议在Alpine换成OpenJDK。Alpine官方给出了Alpine的三大特征 Small...【详细内容】
2023-09-16  Search: JDK  点击:(251)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(19)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(24)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(55)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(68)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(72)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(88)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(105)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(95)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(74)  评论:(0)  加入收藏
站内最新
站内热门
站内头条