原文地址:https://blogs.oracle.com/javamagazine/post/java-11-tricks-generics-inheritance-jshell?source=:so:tw:or:awr:jav:::&SC=:so:tw:or:awr:jav:::&pcode=

dev.java 网站是所有 Java 开发人员的巨大资源。以下是从该网站收集的 11 个技巧。

下载本文的 PDF

[Nicolai Parlog 是 Oracle 的开发人员倡导者,他最近制作了一个高度含咖啡因且非常有趣的视频,展示了用于记录和模式、泛型和 lambda 等的 11 个 Java 技巧jpackage——jshell所有这些都是从 dev.java 中精心挑选的。这是与Java Magazine文章相同的信息。享受!——埃德。]

技巧 #1:紧凑的记录构造函数

你知道如何使用记录对数据载体进行建模,以及如何在构建过程中验证传入的数据,但你知道你不必列出所有记录构造函数的参数吗?

记录的规范构造函数每个组件有一个参数,但在其紧凑形式中,你不必列出所有参数。你不能分配字段——这发生在你之后的编译器生成的代码中——但你可以重新分配参数,这会导致相同的结果。

  1. public record Range(int start, int end) {
  2. public Range {
  3. if (end <= start) {
  4. throw new IllegalArgumentException();
  5. }
  6. }
  7. }

技巧 #2:序列化记录

你知道每个对象都可以使用黑魔法进行序列化,但是你知道*记录不需要这种偏差吗?构造函数参数和访问器的保证存在使序列化与对象模型一起工作,并使你可以轻松创建可靠的可序列化记录

技巧 #3:jpackage 与模块

你知道jpackage,但是等等:真的知道[jpackage](https://dev.java/learn/jpackage/)吗?

jpackage是一个命令行工具,它将整个 Java 应用程序作为输入并生成一个完全独立的应用程序映像,这意味着它包含你的代码、依赖项和 Java 运行时。jpackage使用 为你的应用程序创建运行时,jlink你可以通过 完全配置jpackage它,或者你可以将路径传递给你已经创建的运行时映像。

其他配置选项jpackage包括指定应用程序元数据的能力,例如图标和许可证、安装选项和启动器以及 JVM 和程序选项。

jpackage``deb以特定于平台的格式生成输出,例如rpmLinux 或Windows 的exeand msi

既然你知道jpackage了,你是否知道它可以为模块化和非模块化应用程序完成所有这些工作?

  1. # generate an application image for a modular application:
  2. jpackage --type app-image -n name -p modulePath -m moduleName/className
  3. # for a nonmodular application:
  4. jpackage --type app-image -i inputDir -n name --main-class className --main-jar myJar.jar

技巧 #4:跨操作系统运行时映像

说到jlink,你知道你可以使用它来创建跨操作系统的运行时映像吗?

假设你的构建服务器运行 Linux,并且你需要一个 Windows 运行时映像。你只需下载并解压缩与运行 Linux 的版本相同的 Windows JDK,jlink然后将 Windowsjmods文件夹添加到 Linuxjlink可执行文件的模块路径。

  1. # download JDK for Windows and unpack into jdk-win
  2. # create the image with the jlink binary from the system's JDK
  3. # (in this example, Linux)
  4. $ jlink
  5. --module-path jdk-win/jmods:mods
  6. --add-modules com.example.main
  7. --output app-image

技巧 #5:标记中断并继续

你知道如何使用该break语句来退出内部循环,但是你是否知道你也可以给breaka 标签以打破适当标记的外部循环?你也可以这样做continue,它会跳过最内层循环的当前迭代的其余部分,因为如果你给它传递一个 label,它将跳过标记循环的迭代。

然而,仅仅因为你可以,并不意味着你应该。小心使用这个技巧。

  1. class ContinueWithLabelDemo {
  2. public static void main(String[] args) {
  3. String searchMe = "Look for a substring in me";
  4. String substring = "sub";
  5. boolean foundIt = false;
  6. int max = searchMe.length() -
  7. substring.length();
  8. test:
  9. for (int i = 0; i <= max; i++) {
  10. int n = substring.length();
  11. int j = i;
  12. int k = 0;
  13. while (n-- != 0) {
  14. if (searchMe.charAt(j++) != substring.charAt(k++)) {
  15. continue test;
  16. }
  17. }
  18. foundIt = true;
  19. break test;
  20. }
  21. System.out.println(foundIt ? "Found it" : "Didn't find it");
  22. }
  23. }

技巧 #6:模式匹配中的布尔表达式

你知道模式匹配,但是你知道你可以在同一个布尔表达式中使用模式匹配引入的变量吗?

String比如用object 来检查实例对象是否是类型instanceof String s,就可以直接开始使用s,比如用 来检查是否s非空&& !s.isEmpty()。这适用于ifJava 16 中的语句和switchJava 17 中的预览。

  1. Object object = // ...
  2. if (object instanceof String s && !s.isEmpty())
  3. System.out.println("Non-empty string");
  4. else
  5. System.out.println("No string or empty.");

技巧 #7:通用通配符和子类型

你知道泛型并且 aList<Integer>不扩展 a List<Number>如图 1所示。

Medium (1).webp

图 1.通用继承

但是你是否知道,如果添加通配符,你可以创建类型层次结构,如图 2所示?AList<? extends Integer>实际上确实扩展了 a List<? extends Number>。反之, aList<? super Number>扩展了 a List<? super Integer>

Medium.webp

图 2.使用通配符的通用继承

技巧 #8:创建和链接谓词

你知道如何编写 lambdas 来创建谓词,但是你知道接口提供了许多方法来创建和组合它们吗?为布尔公式调用实例方法andor或。negate

你还没有谓词吗?没问题!静态工厂方法not对于反转方法引用很有用。如果你将某个对象传递给isEqual,你将创建一个谓词来检查实例是否与该对象相等。

  1. Predicate<String> isEqualToDuke = Predicate.isEqual("Duke");
  2. Predicate<Collection<String>> isEmpty = Collection::isEmpty;
  3. Predicate<Collection<String>> isNotEmpty = Predicate.not(isEmpty);

技巧 #9:创建和链接比较器

如果你认为这很酷,请屏住呼吸比较器:你知道如何实现它们,但是你知道还有更多的工厂方法和组合方法吗?

要比较 long、double、float 和其他值,请使用对其静态compare方法的方法引用。

  1. Comparator<Integer> comparator = Integer::compare;

如果你想通过其中一个属性来比较对象,请将提取它的函数传递给静态方法Comparator.comparing。要先按一个属性排序,然后再按另一个属性排序,请创建两个比较器,然后将它们与实例方法链接thenComparing

  1. Comparator<User> byFirstName = Comparator.comparing(User::getFirstName);
  2. Comparator<User> byLastName = Comparator.comparing(User::getLastName);
  3. Comparator<User> byName = byFirstName.thenComparing(byLastName);

需要一个Comparator使用Comparable对象compareTo方法的实例?静态工厂方法naturalOrder适合你。如果这是错误的方法,只需调用reversed它。

啊,那讨厌的东西怎么办null?不用担心:将 a 传递ComparatornullsFirstnullsLast创建一个Comparator可以满足你需要的。

  1. Comparator<Integer> natural = Comparator.naturalOrder();
  2. Comparator<Integer> naturalNullsLast = Comparator.nullsLast(natural);

技巧 #10:将源文件作为脚本执行

你知道你可以使用该java命令来启动单个源文件,而无需先手动编译该文件。但是你知道你可以使用此功能通过三个简单的步骤在 Java 中编写完整的脚本吗?

首先,在指向java可执行文件的源文件中添加一个 shebang 行,然后--source是编写代码的 Java 版本。

  1. #!/path/to/your/bin/java --source 16
  2. public class HelloJava {
  3. public static void main(String[] args) {
  4. System.out.println("Hello " + args[0]);
  5. }
  6. }

其次,重命名文件,使其不以.java. 这是一个很好的机会来给它一个好的命令行风格的名字。

第三,使用 . 使文件可执行chmod +x。你去吧:Java脚本!

技巧 #11:使用所有导入加载 jshell

你知道如何启动 jshell 进行一些快速实验,但你知道你不必手动导入所有内容吗?只需jshell使用该选项启动JAVASE并导入所有 Java SE 包,你就可以开始工作了。