十分钟内从 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)
Streams
(Java 8)Optional
(Java 8)JShell
(Java 9)- 不可变集合的工厂方法(Java 9)
- 类型推断
var
(Java 10) - 单一源文件启动(Java 11)
- Switch 表达式(Java 12:实验性,完整功能:Java 14)
- 多行字符串(Java 13:实验性,完整功能:Java 15)
- 数据类:
record
(Java 14:实验性) instanceof
无需 Cast(Java 14:实验性)- 密封类(Java 15:实验性)
- 福利:从 Java 8 开始更新许可条款
函数式编程(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
该map
函数以 lambda 作为输入,它将应用于流中的所有元素。
Streams
可以处理Lists
、Sets
和Maps
(通过转换)。借助 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
在上面的代码片段中,我们创建一个随机数,将其包装在一个 Optional 对象中,然后仅当它是偶数时才打印该数字。
JShell(Java 9)
终于,我们有了一个Java 的REPL,它的名字叫JShell!简而言之,JShell 允许你试验 Java 代码片段,而无需编写和编译完整的 Java 类。相反,你可以一次执行一个命令,并立即看到结果。这是一个简单的例子:
$ <JDK>/bin/jshell
jshell> System.out.println("hello world")
hello world
熟悉 JavaScript 或 Python 等解释型语言的人们早已享受到了 REPL 的乐趣,但 Java 至今仍缺少这项功能。JShell 不仅允许定义变量,还允许定义更复杂的实体,例如多行函数、类和执行循环。此外,JShell 支持自动补全功能,如果您不知道某个 Java 类提供的具体方法,此功能会非常方便。
不可变集合的工厂方法(Java 9)
Lists
Java 中很久以前就缺少了简单的初始化,但那个时代已经结束了。😅 以前你必须做这样的事情:
jshell> List<Integer> list = Arrays.asList(1, 2, 3, 4)
list ==> [1, 2, 3, 4]
现在简化如下:
jshell> List<Integer> list = List.of(1, 2, 3, 4)
b ==> [1, 2, 3, 4]
这个奇特的of(...)
方法存在于List、Set和Map中。它们都只用一行简单的代码就创建了一个不可变的对象。
类型推断var
(Java 10)
Java 10 引入了新的var关键字,允许省略变量的类型。
jshell> var x = new HashSet<String>()
x ==> []
jshell> x.add("apple")
$1 ==> true
在上面的代码片段中,编译器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");
}
}
$ java ./Main.java
hello world
对于仅由一个 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"
相比之下,新的 switch 表达式返回一个值:
jshell> var i = 3;
jshell> var x = switch(i) {
...> case 1 -> "one";
...> case 2 -> "two";
...> case 3 -> "three";
...> default -> "unknown number";
...> };
x ==> "three"
总而言之,旧的 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);
}
}
现在,我们通过单文件启动运行主要方法:
java --enable-preview --source 13 Main.java
{
"recipe": "watermelon smoothie",
"duration": "10 mins",
"items": ["watermelon", "lemon", "parsley"]
}
生成的字符串跨越多行,引号""
保持不变,甚至制表符\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"
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());
}
不幸的是:首先我们检查其s
类型String
,然后再次对其进行强制转换以检索其长度。
现在有了 Java 14,编译器已经足够智能,可以在进行 instanceof 检查后自动推断类型:
Object obj = new String("hello");
if (obj instanceof String mystr) {
System.out.println("String length: " + mystr.length());
}
密封类(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"; }
}
那么这对我们有什么帮助呢?好吧,现在你知道有多少了。这实际上是朝着完全支持模式匹配的Fruits
方向迈出的重要一步,你可以将类视为枚举。此功能与之前解释过的新表达式完美结合。sealed
switch
福利:从 Java 8 开始更新许可条款
本文的最后一个主题是许可。大多数人都听说Oracle 停止了 Java 8 (免费商业版)的更新。因此,您可以选择以下方式:
- 使用较新的 Oracle JDK版本(Oracle 在每个版本发布后仅提供 6 个月的免费安全更新)
- 使用旧版 JDK需要您自行承担风险
- 使用旧的OpenJDK Java 版本,这些版本仍然可以从开源社区或第三方供应商获得安全更新
- 向 Oracle 支付高级支持费用(例如 Java 8:支持至 2030 年)
您可以在下面看到每个 JDK 的暂定 Oracle 支持期限:
Oracle 的新许可模式受到新发布周期的影响:Oracle 每 6 个月将发布一个新 Java 版本。新的发布周期有助于 Oracle 更快地改进 Java,通过实验性功能更快地获得反馈,并赶上Scala、Kotlin和Python等更现代的语言。
如果您对更多许可细节感兴趣,请查看这篇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