十分钟内从 Java 8 升级到 Java 15 函数式编程 (Java 8) Streams (Java 8) Optional (Java 8) JShell (Java 9) 不可变集合的工厂方法 (Java 9) 使用 var 进行类型推断 (Java 10) 单源文件启动 (Java 11) Switch 表达式 (Java 12) 多行字符串 (Java 13) 数据类:record (Java 14) 不带强制类型转换的实例 (Java 14) 密封类 (Java 15) 额外奖励:从 Java 8 开始更新许可条款 总结

2025-06-07

十分钟内从 Java 8 升级到 Java 15

函数式编程(Java 8)

流(Java 8)

可选(Java 8)

JShell(Java 9)

不可变集合的工厂方法(Java 9)

类型推断var(Java 10)

单一源文件启动 (Java 11)

Switch 表达式 (Java 12)

多行字符串(Java 13)

数据类:record(Java 14)

instanceof无需 Cast (Java 14)

密封类(Java 15)

福利:从 Java 8 开始更新许可条款

包起来

本博客将为您呈现自 Java 7 以来新增的精彩功能示例。我将展示每个 Java 版本的至少一项重大改进,直至 2020 年秋季发布的 Java 15。Java 现在完全支持lambda 表达式函数式编程、通过 进行类型推断var、具有简单构造函数的不可变集合以及多行字符串。此外,还有一些令人兴奋的全新实验性功能,例如数据类record)和sealed类。最后,我将讨论Java REPL,它为快速实验提供了极高的价值。

目录

函数式编程(Java 8)

Java 8 新增了函数式编程和 Lambda 表达式作为语言特性。函数式编程的两个核心范式是:不可变值和将函数Streams提升为“一等公民”。数据会经过一系列修改步骤,每个步骤都会接受一些输入并将其映射到新的输出。函数式编程可以与 Java 中的和 空安全 monad( )一起使用Optional,如下所示……

流(Java 8)

对于普通的计算机程序来说,你经常需要处理一个值列表,并对每个值执行指定的转换。在 Java 8 之前,你必须使用for循环来完成这种转换,但是从现在开始,你可以使用Streams如下方法:



Stream.of("hello", "great")
    .map(s -> s + " world")
    .forEach(System.out::println);
> hello world
> great world


Enter fullscreen mode Exit fullscreen mode

map函数以 lambda 作为输入,它将应用于流中的所有元素。

Streams可以处理ListsSetsMaps(通过转换)。借助 Streams,您可以摆脱代码中几乎所有的循环!👌

可选(Java 8)

Java 中另一个常见问题是空指针异常。因此,Java 引入了Optional —— 一个包装了可能为空也可能不为空的引用的 monad。可以通过函数式的方式更新此 Optional :



Optional.of(new Random().nextInt(10))
    .filter(i -> i % 2 == 0)
    .map(i -> "number is even: " + i)
    .ifPresent(System.out::println);
> number is even: 6


Enter fullscreen mode Exit fullscreen mode

在上面的代码片段中,我们创建一个随机数,将其包装在一个 Optional 对象中,然后仅当它是偶数时才打印该数字。

JShell(Java 9)

终于,我们有了一个Java 的REPL,它的名字叫JShell!简而言之,JShell 允许你试验 Java 代码片段,而无需编写和编译完整的 Java 类。相反,你可以一次执行一个命令,并立即看到结果。这是一个简单的例子:



$ <JDK>/bin/jshell
jshell> System.out.println("hello world")
hello world


Enter fullscreen mode Exit fullscreen mode

熟悉 JavaScript 或 Python 等解释型语言的人们早已享受到了 REPL 的乐趣,但 Java 至今仍缺少这项功能。JShell 不仅允许定义变量,还允许定义更复杂的实体,例如多行函数、类和执行循环。此外,JShell 支持自动补全功能,如果您不知道某个 Java 类提供的具体方法,此功能会非常方便。

不可变集合的工厂方法(Java 9)

ListsJava 中很久以前就缺少了简单的初始化,但那个时代已经结束了。😅 以前你必须做这样的事情:



jshell> List<Integer> list = Arrays.asList(1, 2, 3, 4)
list ==> [1, 2, 3, 4]


Enter fullscreen mode Exit fullscreen mode

现在简化如下:



jshell> List<Integer> list = List.of(1, 2, 3, 4)
b ==> [1, 2, 3, 4]


Enter fullscreen mode Exit fullscreen mode

这个奇特的of(...)方法存在于ListSetMap中。它们都只用一行简单的代码就创建了一个不可变的对象。

类型推断var(Java 10)

Java 10 引入了新的var关键字,允许省略变量的类型。



jshell> var x = new HashSet<String>()
x ==> []

jshell> x.add("apple")
$1 ==> true


Enter fullscreen mode Exit fullscreen mode

在上面的代码片段中,编译器x可以推断出的类型是 HashSet。

此功能有助于减少样板代码提高可读性。但它也有一些限制:只能var在方法体内使用,并且编译器会在编译时推断类型,因此所有内容仍然是静态类型的。

单一源文件启动 (Java 11)

以前,当你编写一个由单个文件组成的简单 Java 程序时,必须先使用 编译该文件javac,然后使用 运行它java。在 Java 11 中,你可以使用一个命令完成这两个步骤:

Main.java



public class Main {
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}


Enter fullscreen mode Exit fullscreen mode


$ java ./Main.java
hello world


Enter fullscreen mode Exit fullscreen mode

对于仅由一个 Java 类组成的简单启动程序或实验,此启动单个源文件的功能将使您的生活更轻松。

Switch 表达式 (Java 12)

Java 12 为我们带来了Switch 表达式。下面简要展示一下该表达式与旧 switch 语句的区别。

旧的 switch 语句定义了程序的流程:



jshell> var i = 3
jshell> String s;
jshell> switch(i) {
   ...>     case 1: s = "one"; break;
   ...>     case 2: s = "two"; break;
   ...>     case 3: s = "three"; break;
   ...>     default: s = "unknown number";
   ...> }
jshell> s
s ==> "three"


Enter fullscreen mode Exit fullscreen mode

相比之下,新的 switch 表达式返回一个值:



jshell> var i = 3;
jshell> var x = switch(i) {
   ...>     case 1 -> "one";
   ...>     case 2 -> "two";
   ...>     case 3 -> "three";
   ...>     default -> "unknown number";
   ...> };
x ==> "three"


Enter fullscreen mode Exit fullscreen mode

总而言之,旧的 switch 语句用于程序流,而新的 switch 表达式解析为一个值。

请注意,这个新的 switch 语句是一种映射函数:有一个输入(在上面的例子中是i),并且有一个输出(在这里是x)。这实际上是一种模式匹配特性,有助于使 Java 更兼容函数式编程原则。Scala中已经提供了类似的 switch 语句一段时间了。

需要注意以下几点:

  • 我们不使用双点,而是使用箭头->
  • 没有必要break
  • 当考虑所有可能的情况时,可以省略默认情况
  • 要在 Java 12 中启用此功能,请使用--enable-preview --source 12

多行字符串(Java 13)

您是否曾经需要定义像 JSON 或 XML 那样冗长的多行字符串?到目前为止,您可能会将所有内容压缩到一行并使用换行符\n,但这会使字符串的可读性大大降低。Java 13 带来了多行字符串!💪

示例案例:



public class Main
{ 
  public static void main(String [] args)
  {
    var s = """
        {
            "recipe": "watermelon smoothie",
            "duration": "10 mins",
            "items": ["watermelon", "lemon", "parsley"]
        }""";
    System.out.println(s);
  }
}


Enter fullscreen mode Exit fullscreen mode

现在,我们通过单文件启动运行主要方法:



java --enable-preview --source 13 Main.java

{
    "recipe": "watermelon smoothie",
    "duration": "10 mins",
    "items": ["watermelon", "lemon", "parsley"]
}


Enter fullscreen mode Exit fullscreen mode

生成的字符串跨越多行,引号""保持不变,甚至制表符\t也被保留!

数据类:record(Java 14)

在本文介绍的所有新功能中,这可能是我最兴奋的一个:Java终于有了数据类!这些类使用record关键字声明,并具有自动 Getter、构造函数和 equals() 方法等。简而言之,你可以摆脱一大堆样板代码!🙌🎉



jshell> record Employee (String name, int age, String department) {}
|  created record Employee

jshell> var x = new Employee("Anne", 25, "Legal");
x ==> Employee[name=Anne, age=25, department=Legal]

jshell> x.name()
$2 ==> "Anne"


Enter fullscreen mode Exit fullscreen mode

Scala 具有与case 类类似的功能,而 Kotlin 具有数据类的功能。在 Java 中,到目前为止,许多开发人员都在使用Lombok,它提供了许多现在启发 Java 14 的功能。records更多详细信息,请参阅这篇Baeldung 文章

instanceof无需 Cast (Java 14)

Java 的早期版本已经包含以下instanceof关键字:



Object obj = new String("hello");
if (obj instanceof String) {
  System.out.println("String length: " + ((String)obj).length());
}


Enter fullscreen mode Exit fullscreen mode

不幸的是:首先我们检查其s类型String,然后再次对其进行强制转换以检索其长度。

现在有了 Java 14,编译器已经足够智能,可以在进行 instanceof 检查后自动推断类型



Object obj = new String("hello");
if (obj instanceof String mystr) {
  System.out.println("String length: " + mystr.length());
}


Enter fullscreen mode Exit fullscreen mode

密封类(Java 15)

使用seal 关键字,你可以限制哪些类可以扩展给定的类或接口。以下是示例:



public sealed interface Fruit permits Apple, Pear {
    String getName();
}

public final class Apple implements Fruit {
    public String getName() { return "Apple"; }
}

public final class Pear implements Fruit {
    public String getName() { return "Pear"; }
}


Enter fullscreen mode Exit fullscreen mode

那么这对我们有什么帮助呢?好吧,现在你知道有多少了。这实际上是朝着完全支持模式匹配的Fruits方向迈出的重要一步,你可以将类视为枚举。此功能与之前解释过的新表达式完美结合sealedswitch

福利:从 Java 8 开始更新许可条款

本文的最后一个主题是许可。大多数人都听说Oracle 停止了 Java 8 (免费商业版)的更新。因此,您可以选择以下方式:

  • 使用较新的 Oracle JDK版本(Oracle 在每个版本发布后仅提供 6 个月的免费安全更新)
  • 使用旧版 JDK需要您自行承担风险
  • 使用旧的OpenJDK Java 版本,这些版本仍然可以从开源社区或第三方供应商获得安全更新
  • 向 Oracle 支付高级支持费用(例如 Java 8:支持至 2030 年)

您可以在下面看到每个 JDK 的暂定 Oracle 支持期限:

Oracle Java SE 支持路线图

Oracle 的新许可模式受到新发布周期的影响:Oracle 每 6 个月将发布一个新 Java 版本。新的发布周期有助于 Oracle 更快地改进 Java,通过实验性功能更快地获得反馈,并赶上ScalaKotlinPython等更现代的语言。

如果您对更多许可细节感兴趣,请查看这篇Medium文章。

包起来

Java 在过去 6 年中取得了长足的进步,自那时以来实际上已经有 8 个新的 Java 版本!🚀 与其他基于 JVM 的竞争对手(Scala 和 Kotlin)相比,所有这些令人敬畏的新功能都有助于使 Java 成为一种有竞争力的选择。

如果您正在寻找有关 Java 8 以来的新 Java 功能的更多深入详细信息,我可以推荐Andrew 的这篇 DEV 文章以及David Csakvari 的这篇文章

写这篇文章的过程非常愉快🔥 感谢阅读😊 你最喜欢最近发布的 Java 版本中的哪个功能?我非常期待你的反馈!谢谢!

文章来源:https://dev.to/pmgysel/from-java-8-to-java-15-in-ten-minutes-22f7
PREV
使用 React Hooks 将数据从子组件传递到父组件
NEXT
告别 Dockerfiles:使用 Cloud Native Buildpacks 构建安全且优化的 Node.js 容器镜像