如果您想比较不同版本的 Java API,有一个很棒的工具Java Version Almanac。
我们跳过中间的版本直接对比Java8和Java17和我们开发有关的差异。
添加了一个新的var关键字,允许以更简洁的方式声明局部变量。
// java 8
Map<String, List<MyDtoType>> myMap = new HashMap<String, List<MyDtoType>>();
List<MyDomainObjectWithLongName> myList = aDelegate.fetchDomainObjects();
// java 10
var myMap = new HashMap<String, List<MyDtoType>>();
var myList = aDelegate.fetchDomainObjects()
我们不能使用var 关键字接收 lambda 的值:
var fun = MyObject :: mySpecialFunction;
// 导致编译错误:(方法引用需要明确的目标类型)
但是,可以在 lambda 表达式中可以使用var
boolean isThereAneedle = stringsList.stream()
.anyMatch((@NonNull var s) -> s.equals(“needle”));
java17的switch case 可以更容易地以更易读的方式分组(注意没有break!)。
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
case SATURDAY, SUNDAY -> true;
};
还允许从代码块内部返回值的yield 关键字,它实际上是一个从 case 块内部工作的返回 值,并通过其switch设置该值。
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
System.out.println("Work work work");
yield false;
}
case SATURDAY, SUNDAY -> {
System.out.println("Yey, a free day!");
yield true;
}
};
java8时, 我们通常会写这样的代码
if (obj instanceof MyObject) {
MyObject myObject = (MyObject) obj;
// TODO
}
Java 现在可以在if 中创建一个局部变量, 它将替我们进行强转。
if (obj instanceof MyObject myObject) {
// TODO
}
此外,声明的变量可以在if 条件中使用
if (obj instanceof MyObject myObject && myObject.isValid()) {
// TODO
}
在switch 中的“no default”警告有没有让你烦恼?您涵盖了域接受的所有选项,但警告仍然存在。使用密封类就可以摆脱对instanceof 类型检查的警告的烦恼。
public abstract sealed class Animal permits Dog, Cat {
}
public final class Dog extends Animal {
}
public final class Cat extends Animal {
}
// 你可以这样获取他们而不用强迫症的写上else的默认结果。
if (animal instanceof Dog d) {
return d.woof();
} else if (animal instanceof Cat c) {
return c.meow();
}
String myWallOfText = """
______ _ _
| ___ \ | | (_)
| |_/ / __ ___| |_ _ _ _ ___
| __/ '__/ _ \ __| | | | / __|
| | | | | __/ |_| | |_| \__ \
\_| |_| \___|\__|_|\__,_|___/
"""
可以转义换行符并将字符串保持为单行
String text = """
又是写bug的一天, \
又是加班的一天。
"""
这相当于
String text = "又是写bug的一天,又是加班的一天"。
文本块可用于在您的代码中保留合理可读的 JSON 或 XML 模板。
MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.get();
在get不到对象时,该方法会抛出异常。Java 10 在 Optional 中引入了一个新方法orElseThrow(),感觉没什么用处,get不到还是要抛出异常。但是考虑到程序的可读性,写上瞬间有点严谨的感觉。
MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.orElseThrow();
// invert a Predicate, will be even shorter with static import
collection.stream()
.filter(Predicate.not(MyObject::isEmpty))
.collect(Collectors.toList());
// String got some new stuff too
“\nPretius\n rules\n all!”.repeat(10).lines().
.filter(Predictions.not(String::isBlank))
.map(String::strip)
.map(s -> s.indent(2))
.collect(Collectors.toList());
// no need to have an instance of array passed as an argument
String[] myArray= aList.toArray(String[]::new);
// read and write to files quickly!
// remember to catch all the possible exceptions though
Path path = Files.writeString(myFile, "Pretius Rules All !");
String fileContent = Files.readString(path);
// .toList() on a stream()
String[] arr={"a", "b", "c"};
var list = Arrays.stream(arr).toList();
从 Java 9 开始,G1 是默认的垃圾收集器。与 Parallel GC 相比,它减少了暂停时间,尽管它的总体吞吐量可能较低。G1的更新包括将未使用的已提交内存返回给操作系统的能力(JEP 346)。
ZGC 垃圾收集器已在 Java 11 中引入,并已在 Java 15 ( JEP 377 ) 中达到产品状态。它旨在进一步减少停顿。从 Java 13 开始,它也可以将未使用的已提交内存返回给操作系统 ( JEP 351 )。
JDK 14 中引入了 Shenandoah GC,并在 Java 15 ( JEP 379 ) 中达到了产品状态。它旨在保持低暂停时间并独立于堆大小。
更多的GC了解请转https://jet-start.sh/blog/2020/06/09/jdk-gc-benchmarks-part1。
在 Java 8 中如果您没有手动更改 GC,您仍然使用Parallel GC。简单地切换到 Java 17 可能会使您的应用程序运行得更快,并具有更一致的方法运行时间。切换到 ZGC 或 Shenandoah 可能会得到更好的结果。
最后,No-Op Garbage Collector(JEP 318),尽管它是一个实验性功能。这个垃圾收集器实际上不做任何工作,因此允许您精确测量应用程序的内存使用情况。如果您想保持尽可能低的内存操作吞吐量,则很有用。
Java 曾有一段时间不知道它正在容器中运行。它没有考虑容器的内存限制,而是读取可用的系统内存。因此,当您有一台具有 16 GB RAM 的机器,将容器的最大内存设置为 1 GB,并在其上运行 Java 应用程序时,该应用程序通常会失败,因为它会尝试分配比可用内存更多的内存容器。从 Java 10 开始,默认情况下启用容器集成。但是,这对您来说可能不是一个明显的改进,因为在 Java 8 更新 131 中引入了相同的更改,尽管它需要启用实验选项并使用-XX:+UseCGroupMemoryLimitForHeap。
为了使 JVM 启动得更快,CDS Archives 自 Java 8 发布以来经历了一些变化。从 JDK 12 开始,默认情况下启用在构建过程中创建 CDS 档案 ( JEP 341 )。JDK 13 ( JEP 350 ) 中的一项增强功能允许在每次应用程序运行后更新档案。
这篇文章演示了如何使用此功能来缩短应用程序的启动时间。
Java Flight Recorder ( JEP 328 ) 允许以较低的(目标 1%)性能成本监视和分析正在运行的 Java 应用程序。Java Mission Control允许摄取和可视化 JFR 数据。参阅教程以大致了解如何使用它以及从中可以获得什么。
简而言之,是的,你应该这样做。如果您有一个大型、高负载的企业应用程序并且仍然使用 Java 8,那么您肯定会看到更好的性能、更快的启动时间、迁移后更低的内存占用。开发该应用程序的程序员也应该更开心,因为语言本身有许多改进。
然而,这样做的成本很难估计,并且会因使用的应用程序服务器、库和应用程序本身的复杂性(或者更确切地说,它使用/重新实现的低级功能的数量)而有很大差异。
如果您的应用程序没有自定义类加载器,没有严重依赖 Unsafe、大量 sun.misc 或 sun.security 用法,那么您可能会没事。请参阅这篇文章,了解您可能需要进行的一些更改。
从版本 8 开始,Java 中删除了一些东西,包括 Nashorn JS 引擎、Pack200 API 和工具、Solaris/Sparc 端口、AOT 和 JIT 编译器、Java EE 和 Corba 模块。有些东西仍然存在,但不赞成删除,例如 Applet API 或安全管理器。由于有充分的理由将它们移除,因此无论如何您都应该重新考虑它们在您的应用程序中的使用。
Java17主要有如下几个特性:
JEP 306: Restore Always-Strict Floating-Point Semantics
JEP 356: Enhanced Pseudo-Random Number Generators
JEP 382: New macOS Rendering Pipeline
JEP 391: macOS/AArch64 Port
JEP 398: Deprecate the Applet API for Removal
JEP 403: Strongly Encapsulate JDK Internals
JEP 406: Pattern Matching for switch (Preview)
JEP 407: Remove RMI Activation
JEP 409: Sealed Classes
JEP 410: Remove the Experimental AOT and JIT Compiler
JEP 411: Deprecate the Security Manager for Removal
JEP 412: Foreign Function & Memory API (Incubator)
JEP 414: Vector API (Second Incubator)
JEP 415: Context-Specific Deserialization Filters