JDK 选择

17 篇文章 5 订阅

我接触Java很早,读大学的时候,那时候互联网还很不发达,整个产业甚至整个世界都和现在大不一样。大概是在1999年,我的朋友给我寄来了一套 visual age 个人版。这个个人版 IDE限制很多,例如不能创建超过98个类。我当时是个 Delphi 爱好者,这个开发工具给我带来了很多非常新鲜的体验,例如它的交互设计和事件绑定,和我习惯的 Delphi、VB完全不一样,和当年基于 MFC 的VC也非常不一样。
但是毕业之后,我先是成为了一个 Delphi 程序员,后来在 2003年开始转向 c sharp,python 成为了我业余时间最关注的技术。在接下来的十几年中,除了一次偶然的需要,我做了一些反编译工作,其它时间都没有再用到 Java。下一次用到它,要等到2015年以后。在这之后,我有大量的工作围绕 JVM 展开,但是我并不仅仅用到 Java 语言,很长时间里我的工作语言包含了 Clojure 、 Scala 、Java和 Go 。在近几年,我逐渐稳定在 Python、Java、 Scala 的组合。在工作中以 Python 和 Java 为主,业余时间的学习和研究,则主要是 Scala 。
介绍这个背景,是为了说明,我接下来的一些观点,基于我自身这些年的个人经历,可能有些观点并不典型。实际上我个人并不认为“大家都这样”是一个很强有力的理由。在2002年我开始翻译 Python Tutorial的时候,我并没有想过今天的 Python 如此火爆。“我觉得好”比“大家都说好”,要更重要的多。
在我刚工作的时候,读到过一本书,书中有一个观点影响了我此后几十年间的学习方向。“学明天的技术,而不是昨天的技术。因为当你学会它的时候,很可能新技术已经成为了成熟技术,成熟技术已经落伍。”
对于 JDK 的选择,从学习者和开发人员的角度,我有类似的,但是没有那么极端的建议,那就是,在可行的前提下,尽可能选择更新的版本。
一个常见的现象是不加思考的选择已经成为主流的版本,这个不良习气导致了在一些团队中,Java 8至今仍是不二的选择。但是 Java 8发布于 2014年,在这8年间,技术在发展,企业面对的问题也越来越复杂,停留在这个版本,意味着 Java 8不完美和有缺陷的地方,一直存在于这些项目中。而这其中一些基于 Java 8 可能需要“高级技巧”来解决的问题,仅仅是简单的升级 JVM 就可以解决。
但是 Java 的特殊在于,在 Java 9 的开发过程中,Oracle 的开发团队决定不再向传统的 Java 开发模式妥协(老 Java 程序员有些可能对Java 委员会多年来的各种政治斗争并不陌生),激进的转向每半年固定发版的方式推进研发。自这个版本中推出模块化功能,有一大批 Java 项目,因为依赖 XML或者 swing、awt或者 com.sun 命名空间中的某个类型,就出现了不兼容的问题。我还记得那段时间,我经常会因为要在 Java 10 或者 11上运行某个 clojure 项目(例如gorilla,这是一个类似python jupyter 的工具),要花很多时间去寻找线索,用蹩脚的英语去请教同行。
这种激烈的不兼容的升级,在过去近二十年的 Java 历史上从没有过,可谓喜忧参半。虽然面对了很多新的问题,但大多数是随着新版本的稳定可以得到解决的,而新的 JDK ,确实带来了一些重要的进步。
很多同行也许每天花大量时间在写 Java ,但是并没有认真考虑过如何优化自己的开发工作,这是他们认为 Java 8 足够好的一个重要原因。但是我也确实遇到过某个重要的工具库,因为前面说到的兼容问题,在 Java 9 之后就无法运行。因为换了工作,没有再用到那几个库,我至今不知道它们是否开始支持新版的 JDK ,但是这些事情也给我留下了深刻印象,那就是一个 Java 项目是否应该选择更高版本的 JDK ,其实没有唯一的正确答案。最好的办法是先梳理需要用到的工具体系,基于它们的兼容能力,选择一个尽可能新的版本。
我现在还在维护一个java8 版本的组合子类库 GitHub - MarchLiu/jaskell-java8: A Pure Java 8 Library for Java8 Projects 。可以算是那段工作的“后遗症”。我希望如果有一天我需要再去参与一个必须基于 Java 8的项目,至少还有一些工具代码可以用。
实际上在最初,我还维护过 Java 6 和 Java 7兼容的 Jaskell 实现。目前我已经放弃了这些项目,随着 Android 平台普遍的升级,这些版本的开发工作对我来说已经没有意义(当然,只要能创造商业价值,我相信世界上还运行着一些非常有意义的Java6甚至更古老的Java项目)。如果有过这些版本的 Java 经验,同行们应该知道它们和 Java 8 的差别有多大,例如在 Java 7引入的泛型定义简写(钻石操作符)

    @Test
    public void simple() throws Exception {
        List<String> data = Arrays.asList("Hello", "World");

        State<String, Integer, Integer> state = new SimpleState<>(data);
        Integer idx = state.status();
        Attempt<String, String, Integer, Integer> attemptIt = new Attempt<>(new Eq<>("Hello"));

        String re = attemptIt.parse(state);

        assertEquals(re, "Hello");
        assertNotEquals(idx, state.status());
    }


在 Java 6中不能用,上例中的

State<String, Integer, Integer> state = new SimpleState<>(data); 

要完整的写为

State<String, Integer, Integer> state = 
        new SimpleState<String, Integer, Integer>(data); 

即使有 IDE 辅助,我也不愿意再写 Java 6 。
这么多年在研发工作上的努力,不仅仅包含争取一个更好的物质回报,也包含了能工作的更开心的选择权。对我来说,泛型、简化的类型定义、Lambda 等等,每一步都意味着更高效和更舒适的工作条件,值得我去争取。
如果一个程序员只用过 Java ,并且从 Java 6 升级到 Java 8,他开始使用 stream api、lambda,可能会觉得 java 8已经足够好,甚至我遇到不止一个人说“java8带来了纯函数式语言的强大功能”。但是实际上 Java 8里真正的函数式编程还很远,有很多函数式编程中优秀的特性都没有实现,或者实现的不完整,例如 java 8的 stream 甚至没有 dropWhile 和 takeWhile 。当然我个人并不主张在实际应用中追求纯粹的函数式编程,好的编程实践应该是对各种编程范式的组合利用,这也是我这几年转向 Scala 的重要原因。即使最新的 java 语言标准,其语言功能与 Scala 仍然无法相比,但是其中仍然有一些进步,虽然可能不会让程序运行的更快,但是可以带来更好的工作条件。
通常我们讨论技术选择时,性能会是问题核心,但是其实性能问题我看没那么重要,不然也就不会有那么多“Java8足够好”的声音。在 Java 10发布的时候,我当时就职的团队正好面对一个高性能节点的调优问题,Java 10 的垃圾回收算法 g1 ,比起老版本 JVM 的 CMS ,巨大的差距足够让所有人都放弃了争议。从此以后我在 Java 11、13和17,以及一些第三方的 JVM 实现如 graal 上,都做过类似的测试,G1 比起 CMS ,是有明显代差的。简单的在高版本的 JVM 上打开 g1 算法,足以很多基于 CMS 的“高级调优技巧”变得毫无意义。
顺便说一下,这里有个很奇怪的事情,Java 官方早就(大概是 Java 10 发布的时候)宣布 Java 默认的 GC 算法改为 g1,但是我实践上,无论openjdk 还是 Oracle jdk ,仍然要手工设定 gc 算法,才会使用 g1 。这也是一些同行感受不到高版本 Java 性能改进的原因。
我现在数据机上还在运行一些处理程序,就不去跑演示程序做比较了。就我的个人来说,仅仅 g1 GC这一个理由,足以让我们升级到更高的 JDK 。
例如我的数据机里,有这么一个环境变量:

 export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -verbose:gc"

它会告诉 JVM ,打开 G1算法,并显示 GC 日志。
更高版本的 JDK ,就意味着更完备的标准库,更方便的语法,更……
更多的不兼容风险。
也就是说,每一次向上升级,都应该对项目做足够的测试。就我个人而言,我的开发机上有很多个不同版本的 Java 环境。

我的选择标准是,对于 Jaskell Core,我选择 Scala 2.13 和 Play 能支持的最新版 Java 。对于 Jaskell Dotty ,选择 Scala 3 能支持的最高版本。这就导致我目前用的最多的,其实是 Java 11 。因为 Java 12或者 13的时候,对Java class loader的路径解析有一个修改,这个逻辑非常底层,造成了很多 Java 项目(特别是Scala和Clojure 项目)无法运行在 Java 13 ,例如 Play framework,至今如果用高版本的 Java 运行,都会报一个 cache 错误。但是很多时候我只是写一个很简单的算法程序,高版本的 Java 语言,总是用起来舒适的多。
近一段时间来,我开始用 sdkman (Home - SDKMAN! the Software Development Kit Manager) 管理开发环境的Java。
自从加入 csdn,我基于 play 工具栈写了很多数据处理程序,它们能运行的最高版本,就成了我的一个天花板。但是我同时也有一些探索性的小项目,会尽可能的用更高版本的 Java 实现,最近几个版本的 Java ,有一些非常好的进步,例如模式匹配、text block,都是可以有效提升开发体验的。包括邓草原老师前段时间在微博介绍的新的异常处理语法,都是很好的进步。特别是如果你的工具栈基于 quarkus,这个社区对 Java 语言的进步跟进的非常快,可以享受到更多高版本 Java 的便利。
最近两年没有用 Spring,Spring 社区的工作一般来说还是比较扎实的,可以根据自己用到的子集选择 JDK 版本,注意看一下文档有没有介绍版本的限定。但是一般来说,升级到 Java 11 并没有什么问题。G1和更完整的函数式编程风格,值得我们尝试。
我个人目前最喜欢的工具集仍然是 scala play framework 和 akka,即使到 Java 30,恐怕也不会把 java 变成 scala ,所以我的主力选择,仍然是 Java 11。当然,只要play和 akka 升级到 scala 3 和 JDK 15甚至 17,我肯定会跟进。
很抱歉说了这么多以后,我仍然没有给出一个确定的 JDK 选择,这种事情在我而言,仍然是一个没有“唯一正确”答案的问题。如果我们对自己的工具和技术有足够的了解,那无论 Java 还是 Python,这都是一个“从心所欲不逾规”的事情。

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ccat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值