Java 14:记录,更简洁的instanceof,jpackage包装器,开关lambda和文本块

UPD。今天将是人们期待已久的Java 14发行版-即使它不是LTS-其中也包含足够的新功能。Java 14将在几个小时内上市-但您现在就可以了解它。



在Java 14中,无论是在编写代码的级别还是在API,GC和许多其他引擎罩的级别,都进行了足够的更改。我们可以肯定地说,如果您了解一些Kotlin或Python超级芯片-不用担心,它们很有可能很快就会出现在Java中。无论如何,今天的发行版包含其中一些。但是首先是第一件事。

在Java 14中,以下创新正在等待着我们:

  • JEP 305.为通过instanceof检查的对象分配引用。
  • JEP 343.封隔器jpackage(孵化器)。
  • JEP345。G1的基于NUMA的内存分配。
  • JEP349。通过JFR传输事件。
  • JEP352。不变的映射字节缓冲区。
  • JEP358。有关NullPointerException的提示。
  • 359年9月。记录。
  • JEP361。切换lambda。
  • JEP362。现已弃用Solaris和SPARC端口。
  • JEP 363.删除先前标记为“已弃用”的并发标记扫描垃圾收集器。
  • JEP364。在macOS上移植ZGC。
  • JEP365。将ZGC移植到Windows。
  • JEP366。现已弃用ParallelScavenge + SerialOld GC的组合。
  • JEP 367.从Pack200中删除工具和API(在Java 11中被标记为不推荐使用)。
  • JEP368。文本块。
  • JEP370。外部存储器访问API(孵化器)。

让我们谈谈每种改进,其中一些将被更详细地讨论。

JEP 305.为通过instanceof检查的对象分配引用




考虑通过instanceof检查对象类型的情况。如果我们想显式地将对象自定义为所需的类型而不冒ClassCastException的风险,则首先需要确保该对象是我们所需的类型。

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy) {  //,    -  Toy
            Toy t = (Toy) o;  //  
            System.out.println("Toy's name: " + t.getName());  //-   
        }
    }

在本示例中,这种情况一直发生,我们一直在:a)确保我们具有所需类型的对象,b)为其分配链接,c)根据我们的逻辑对它进行操作。在Oracle看来,他们在这种特定的结构中看到了一定数量的代码,并决定将其精简一步。现在您可以这样写:

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy t) {  //      
            System.out.println("Toy's name: " + t.getName());  //     
        }
    }

读者,这一切真的是多么方便和有用,让我来判断一下。是的,的确如此,这种构造可能在99%的与instanceof相关联的情况下发生,并且简化可能会扎根(或可能不会扎根)。时间会证明一切。

JEP 343. jpackage Packer(孵化器)




我们是开发人员,是个有头脑的人,您不会安装dzharnik来吓我们,但是一个简单的用户又不想知道在另外30亿台计算机上安装了JVM的情况,又不想知道Java跨平台,只想戳2如果使用Windows,则将.exe文件加倍,或者将.app应用程序拖至Applications文件夹(如果它具有罂粟),然后不蒸它?如何为程序员创造所有条件,以便他们将应用程序打包为最终用户熟悉的“可执行文件”?

Oracle似乎意识到了这个问题,并决定编写一个打包器,该打包器将立即以适合该平台的格式打包应用程序:

  • Linux:deb和rpm
  • macOS:pkg和dmg
  • Windows:MSI和EXE

看起来像这样:

$ jpackage --name myapp --input lib --main-jar main.jar --type exe

--name
myapp- 应用程序的未来名称--input lib-jar归档
文件的源--main -jar main.jar-包含主类的归档文件的名称
--type exe-应用程序将打包到的类型。

打包后,可以双击myapp.exe(如果有Windows)并将其安装为常规Windows应用程序。图形界面由JavaFX提供。

让我们尝试使用Windows平台构建这样的应用程序。

因此,让我们从这里开始该项目:
https : //github.com/promoscow/bouncer
在发送GET请求时,我们收到消息:“ Bounce successl”和时间戳。

把一个dzharnik。既然有了gradle,它就在build / libs文件夹中。



转到构建文件夹,输入所需的最少命令:

jpackage --name bouncer --input libs --main-jar bouncer.jar --type exe

我们得到了一个exe-shnik。



我们在可执行文件中戳两次,通常会安装该应用程序。
哇!



该应用程序已安装并且正在等待中。
在“程序文件”的bouncer文件夹中,可以运行bouncer.exe。



JEP 345. G1的NUMA分配的内存分配


存在这样的问题-NUMA,非统一内存访问,不相等的内存访问。换句话说,访问多段机器中的远程套接字可能会花费大量时间,尤其是对于G1垃圾收集器而言。在Java 14中,他们尝试如下解决此问题:

G1堆被组织为一组固定大小的区域。区域通常是物理页面的集合,尽管使用大页面(通过-XX:+ UseLargePages)时,多个区域可以组成一个物理页面。

如果在JVM初始化期间添加+ XX:+ UseNUMA参数,则区域将平均分布在可用NUMA节点的总数上。

Oracle承诺,尽管这种方法并不完全灵活,但他们将来会改进它。

JEP 349.通过JFR传输事件


Java中有一个东西,例如JFR-Java Flight Recording-动态记录事件,它使您可以在应用程序运行时监视大约500种事件。问题在于它们大多数只能在日志中查看。Oracle提供的改进是实现一个处理程序,例如,一个lambda函数,该函数将响应事件而被调用。

看起来是这样的:

try (var rs = new RecordingStream()) {
  rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
  rs.onEvent("jdk.CPULoad", event -> System.out.println(event.getFloat("machineTotal")));
  rs.start();
}

此事件每秒都会显示处理器向控制台的负载。



JEP358。关于NullPointerException的提示


另一个明显的便利,旨在简化对代码中错误的搜索。想象一个建筑-一个星球,这个星球上有很多国家,每个国家都有很多城市。

public class Planet {

    private List<Country> countries;
    //...
}

public class Country {

    private List<City> cities;
    //...
}

public class City {

    private String name;
    //...
}

我们决定显示地球上所有城市的哈希码:

planet.getCountries().forEach(c -> c.getCities().forEach(city -> city.hashCode()));

但是他们没有考虑字段的强制初始化。在某些时候,他们得到了NullPointerException:

Exception in thread "main" java.lang.NullPointerException
	at ru.xpendence.jep_358_nullpointerexception.Main.main(Main.java:19)

我们哪个字段为空?行星?国家?城市??? 我们不知道。我们将断点放在最右边,然后叹了口气,降低了性能。

在Java 14中,NullPointerException具有更多信息:

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "cities" because "countries" is null
     at Main.main(Main.java:21)
     ...

立即一切都清楚了。国家为空。

JEP 359.记录




难怪Oracle将发布周期更改为六个月。 IT行业的结构变化甚至使这些领导者的行动速度也更快。并且,例如,如果Kotlin和Python出现时受到Java的影响(无论如何,这就是Wikipedia所说的Python),那么Java可以说是在关注它的追随者。关于Python,它的价格会更低一些,但是在Kotlin中可以精确地看到Oracle的以下功能。它与数据类有关,在Java中现在称为记录。

什么是数据类?让我们编写一个常规的POJO:

public class Station {

    private String name;
    private Coordinates coordinates;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Coordinates getCoordinates() {
        return coordinates;
    }

    public void setCoordinates(Coordinates coordinates) {
        this.coordinates = coordinates;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PlainStation that = (PlainStation) o;
        return Objects.equals(name, that.name) &&
                Objects.equals(coordinates, that.coordinates);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, coordinates);
    }

    @Override
    public String toString() {
        return "PlainStation{" +
                "name='" + name + '\'' +
                ", coordinates=" + coordinates +
                '}';
    }
}

在这里,我们拥有一切-getters,setters,equals,hashcode,toString ...为了以某种方式摆脱这种耻辱,好人提出了Lombok。

在Jetbrains上,通过为Kotlin发明数据类,以一种更为彻底的方式一次解决了该问题。具有所有标准方法的此类如下所示:

data class Station(val name: String, val coordinates: Coordinates)

就是这样。在Oracle中,似乎看了看这个设计并做了完全一样的事情,只记录了:

public record RecordStation(String name, List<Coordinates> coordinates) {}

它包含标准的getter,setter,equals,hashcode和toString。
这种类型的类已经可以在IDEA 2020.1中创建。

与POJO有什么区别?


  • 记录不能从任何类继承。
  • Record不能具有对象的任何其他字段,除了描述类时在构造函数中声明的字段(是的,顺便说一下,这是默认的构造函数)。静态-可以。
  • 字段隐式为final。对象是隐式最终的。具有所有后果,例如不可能被抽象化。

事实证明,这是一个不变的数据类,并非旨在用于与其相关的某些复杂逻辑操作,而是旨在用于数据传输,仅此而已。

JEP 361.开关Lambdas




甲骨文似乎已经采取了紧紧的行动。在当前版本之前,它是一个相当庞大的设计,看起来像这样:

    public static void translateDayOfWeekOld(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY":
                System.out.println("");
                break;
            case "TUESDAY":
                System.out.println("");
                break;
            case "WEDNESDAY":
                System.out.println("");
                break;
            case "THURSDAY":
                System.out.println("");
                break;
            case "FRIDAY":
                System.out.println("");
                break;
            case "SATURDAY":
                System.out.println("");
                break;
            case "SUNDAY":
                System.out.println("");
                break;
            default:
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
        }
    }

处理一个条件至少需要三行-情况,动作,中断。现在,借助开关的增强功能,我们可以将上述构造缩短为:

    public static void translateDayOfWeek(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY" -> System.out.println("");
            case "TUESDAY" -> System.out.println("");
            case "WEDNESDAY" -> System.out.println("");
            case "THURSDAY" -> System.out.println("");
            case "FRIDAY" -> System.out.println("");
            case "SATURDAY" -> System.out.println("");
            case "SUNDAY" -> System.out.println("");
            default -> {
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
            }
        }
    }

同意,非常紧凑,时尚并且带有lambda。决定是否值得。

顺便说一句,按照旧规则编写的IDEA 2020.1开关建议使用新的方式仔细地重写。



JEP362。Solaris和SPARC端口现已弃用


这里的一切都很简单。Oracle认为花在支持Solaris和SPARC端口上的资源是不值得的,因此有空的员工应该转向开发新功能。

JEP 363.删除先前标记为“已弃用”的并发标记扫描垃圾收集器


CMS垃圾收集器的删除在两年前的第9版中进行了讨论。在这段时间里,两个垃圾收集器出来了-ZGC和Shenandoah。同时,没有一个值得信赖的Oracle贡献者关注CMS支持。

通常,医生先说太平间,然后再说太平间。

JEP 364、365。在macOS和Windows上移植ZGC


我们通常在Linux平台上部署应用程序,但是对于本地开发,我们经常使用Windows和Mac。为了满足这些需求,Java 14将ZGC垃圾收集器移植到了这两个平台上。

JEP366。现在不建议使用ParallelScavenge + SerialOld GC组合


当年轻的ParallelScavenge与SerialOld结合使用时,极少使用的垃圾收集器配置。这样做的原因尚不清楚,因为ParallelScavenge是并行的,而SerialOld相反。包括这种组合需要特殊的手鼓舞蹈,并且需要大量的血液开发者。“ Oracle关心您”,因此它将此配置标记为过时的,希望将其尽快发送到CMS垃圾收集器。

兄弟们,让我们高兴。

JEP 367.删除Pack200工具和API(在Java 11中被标记为不推荐使用)


轮到pack200,unpack200和API pack200来进行应有的休息。原因是该封隔器过时了。曾几何时,当Internet成为调制解调器并且速度为56k(boomers记住)时,JDK必须被抽出数小时。发明了一种压缩程序,可以更好地压缩JDK并减少下载时间。而且,它们可以压缩applet和客户端应用程序。但是时间过去了,以目前的速度,封隔器是无关紧要的。

以下软件包将被删除:

java.util.jar.Pack200
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker

以及jdk.pack模块。

JEP 368.文本块




一次,Java对Python产生了影响(包括其他六种语言)。由于Python迅速普及并与Java和C IT图表的其他领导者一起充满信心,因此偷窥无可厚非。一次,Java 9引入了JShell,它与pythonium Jupiter非常相似。现在,文本块的时机已到。

当我们需要写一行时,我们写一行:

String s = "";

当我们需要编写一个格式化的字符串时,我们可以编写如下代码:
String oldHtml = "<html>\n\t<body>\n\t\t<p>Hi all!</p>\n\t</body>\n</html>";

文本完全不可读。因此,在Java 14中,问题得以解决。现在,使用三引号,您可以编写任何文本:

        String html = """
                      <html>
                          <body>
                              <p>Hi all!</p>
                          </body>
                      </html>
                      """;

它更加方便和可读。我们可以简单地将任何文本复制到代码中,而不必使用制表符和连字符。美丽!如果我们只需要在不做连字符的情况下将文本转移到该文本的另一行,则可以使用新的文字-反斜杠。该符号概括地说,其后的转移不是转移。例:

        String text = """
                    \
                 ,  , \
                     \
                   . \
                , ,  ! \
                  ; \
                    \
                   .
                """;

结论:

众神仍给你金色的日子,金色的夜晚,懒的处女盯着你。朋友们,玩,唱歌!失去短暂的夜晚;和你的粗心的喜悦通过眼泪我微笑。


JEP 370.外部存储器访问API(孵化器)


碰巧一个应用程序访问诸如Ignite,mapDB,memcached等的外部内存。用于访问它的现有API相当有效,但是Oracle需要全局的东西。因此出现了MemorySegment,MemoryAddress和MemoryLayout的抽象。当该功能处于孵化器中时,每个人仍然可以对ByteBuffer,Unsafe和JNI感到满意。

结论


读者,我不了解您,但是我喜欢自Java 9以来Oracle切换到的六个月发布周期。现在Oracle并没有设定发布绝对稳定版本的任务,但是复杂的javista并不难跟上一项功能或另一项功能的稳定性。用于开发和测试孵化器中的某些东西。该语言已变得更加活跃,多变,非常大胆的创新,并从其他语言中汲取了灵感。是的,有人无法跟上版本的变化,但是我们的专业要求我们跟上发行的步伐,因此无论我们是否想要满足Java 14

,我都会像往常一样将一个项目附加在github上,并附上代码示例:[单击此处]

All Articles