Java 14 有哪些新特性?

记录为 Java 提供了一种正确实现数据类的能力,不再需要为实现数据类而编写冗长的代码。下面就来看看 Java 14 中的记录有哪些新特性。

作者 | Nathan Esquenazi

译者 | 弯月,责编 | 郭芮

以下为译文:

Java 14 即将在 2020 年 3 月正式发布。 Java 以 6 个月作为新版本的发布周期,和之前的版本发布一样,JDK 14 预计将在语言本身和 JVM 级别上带来一些新特性。

如果我们看一下特性列表,我们会注意到一些开发者非常期待的语言特性:记录 (records)、 switch 表达式(在 JDK 13 中就已经存在,不过仅仅是预览模式),模式匹配。下面让我们看下其中比较有趣的记录这一特性。

前提条件

我们需要 OpenJDK 网站中的 JDK 14 先期预览版本(https://jdk.java.net/14/)。

什么是一条记录?

记录表示“数据类” ,是用于保存纯数据的一种特殊的类。 其他语言中已经有类似记录的结构,比如 Kotlin 的数据类。 通过将类型声明为记录,通过类型即可表达意图,即只表示数据。 声明记录的语法比使用普通类要简单得多,普通类通常需要实现核心 Object 方法,如 equals 和 hashCode (通常称为“样板”代码)。 在对于模型类 (可能通过 ORM 持久化) 或数据传输对象 (DTOs) 等事物建模时,记录是一个不错的选择。

如果想知道记录如何在 Java 语言中实现的,可以参照枚举类型。 枚举也是一个具有特殊语义和优雅语法的类。 由于记录和枚举仍然是类,所以类中可用的许多特性都得到了保留,因此记录在设计的简单性和灵活性之间取得了平衡。

记录是一个预览语言特性,这意味着,尽管已经完全支持了这种特性,但是还没正式进入标准 JDK 中,目前只能通过激活标志来使用。 预览语言功能可能在未来的版本中更新或删除。 switch 达式也与之相似,它可能在未来的版本中永存。

一个记录的例子

下面给出一个记录的范例:

package examples;

record Person (String firstName, String lastName) {}

我们定义了一个 Person对象,包含firstNamelastName两个组件,记录的 body 为空。

然后我们对其进行编译。注意 --enable-preview选项。

javac --enable-preview --release 14 Person.java

Note: Person.java uses preview language features.

Note: Recompile with -Xlint:preview for details.

揭露其神秘面纱

正如前面提到的,记录只是一个用于保存和暴露数据的类。

接下来让我们来看看用 javap 工具生成的字节码:

javap -v -p Person.class

字节码:

Classfile examples/Person.class

Last modified Dec 22, 2019; size 1273 bytes

SHA-256 checksum 6f1b325121ca32a0b6127180eff29dcac4834f9c138c9613c526a4202fef972f

Compiled from "Person.java"

final class examples.Person extends java.lang.Record

minor version: 65535

major version: 58

flags: (0x0030) ACC_FINAL, ACC_SUPER

this_class: #8 // examples/Person

super_class: #2 // java/lang/Record

interfaces: 0, fields: 2, methods: 6, attributes: 4

Constant pool:

#1 = Methodref #2.#3 // java/lang/Record."":V

#2 = Class #4 // java/lang/Record

#3 = NameAndType #5:#6 // "":V

#4 = Utf8 java/lang/Record

#5 = Utf8

#6 = Utf8 V

#7 = Fieldref #8.#9 // examples/Person.firstName:Ljava/lang/String;

#8 = Class #10 // examples/Person

#9 = NameAndType #11:#12 // firstName:Ljava/lang/String;

#10 = Utf8 examples/Person

#11 = Utf8 firstName

#12 = Utf8 Ljava/lang/String;

#13 = Fieldref #8.#14 // examples/Person.lastName:Ljava/lang/String;

#14 = NameAndType #15:#12 // lastName:Ljava/lang/String;

#15 = Utf8 lastName

#16 = Fieldref #8.#9 // examples/Person.firstName:Ljava/lang/String;

#17 = Fieldref #8.#14 // examples/Person.lastName:Ljava/lang/String;

#18 = InvokeDynamic #0:#19 // #0:toString:(Lexamples/Person;)Ljava/lang/String;

#19 = NameAndType #20:#21 // toString:(Lexamples/Person;)Ljava/lang/String;

#20 = Utf8 toString

#21 = Utf8 (Lexamples/Person;)Ljava/lang/String;

#22 = InvokeDynamic #0:#23 // #0:hashCode:(Lexamples/Person;)I

#23 = NameAndType #24:#25 // hashCode:(Lexamples/Person;)I

#24 = Utf8 hashCode

#25 = Utf8 (Lexamples/Person;)I

#26 = InvokeDynamic #0:#27 // #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z

#27 = NameAndType #28:#29 // equals:(Lexamples/Person;Ljava/lang/Object;)Z

#28 = Utf8 equals

#29 = Utf8 (Lexamples/Person;Ljava/lang/Object;)Z

#30 = Utf8 (Ljava/lang/String;Ljava/lang/String;)V

#31 = Utf8 Code

#32 = Utf8 LineNumberTable

#33 = Utf8 MethodParameters

#34 = Utf8 Ljava/lang/String;

#35 = Utf8 I

#36 = Utf8 (Ljava/lang/Object;)Z

#37 = Utf8 SourceFile

#38 = Utf8 Person.java

#39 = Utf8 Record

#40 = Utf8 BootstrapMethods

#41 = MethodHandle 6:#42 // REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;

#42 = Methodref #43.#44 // java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;

#43 = Class #45 // java/lang/runtime/ObjectMethods

#44 = NameAndType #46:#47 // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;

#45 = Utf8 java/lang/runtime/ObjectMethods

#46 = Utf8 bootstrap

#47 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;

#48 = String #49 // firstName;lastName

#49 = Utf8 firstName;lastName

#50 = MethodHandle 1:#7 // REF_getField examples/Person.firstName:Ljava/lang/String;

#51 = MethodHandle 1:#13 // REF_getField examples/Person.lastName:Ljava/lang/String;

#52 = Utf8 InnerClasses

#53 = Class #54 // java/lang/invoke/MethodHandles$Lookup

#54 = Utf8 java/lang/invoke/MethodHandles$Lookup

#55 = Class #56 // java/lang/invoke/MethodHandles

#57 = Utf8 Lookup

{

private final java.lang.String firstName;

descriptor: Ljava/lang/String;

flags: (0x0012) ACC_PRIVATE, ACC_FINAL

private final java.lang.String lastName;

descriptor: Ljava/lang/String;

flags: (0x0012) ACC_PRIVATE, ACC_FINAL

public examples.Person(java.lang.String, java.lang.String);

descriptor: (Ljava/lang/String;Ljava/lang/String;)V

flags: (0x0001) ACC_PUBLIC

Code:

stack=2, locals=3, args_size=3

0: aload_0

1: invokespecial #1 // Method java/lang/Record."":V

4: aload_0

5: aload_1

6: putfield #7 // Field firstName:Ljava/lang/String;

9: aload_0

10: aload_2

11: putfield #13 // Field lastName:Ljava/lang/String;

14: return

LineNumberTable:

line 3: 0

MethodParameters:

Name Flags

firstName

lastName

public java.lang.String toString;

descriptor: Ljava/lang/String;

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(Lexamples/Person;)Ljava/lang/String;

6: areturn

LineNumberTable:

line 3: 0

public final int hashCode;

descriptor: I

flags: (0x0011) ACC_PUBLIC, ACC_FINAL

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokedynamic #22, 0 // InvokeDynamic #0:hashCode:(Lexamples/Person;)I

6: ireturn

LineNumberTable:

line 3: 0

public final boolean equals(java.lang.Object);

descriptor: (Ljava/lang/Object;)Z

flags: (0x0011) ACC_PUBLIC, ACC_FINAL

Code:

stack=2, locals=2, args_size=2

0: aload_0

1: aload_1

2: invokedynamic #26, 0 // InvokeDynamic #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z

7: ireturn

LineNumberTable:

line 3: 0

public java.lang.String firstName;

descriptor: Ljava/lang/String;

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: getfield #16 // Field firstName:Ljava/lang/String;

4: areturn

LineNumberTable:

line 3: 0

public java.lang.String lastName;

descriptor: Ljava/lang/String;

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: getfield #17 // Field lastName:Ljava/lang/String;

4: areturn

LineNumberTable:

line 3: 0

}

SourceFile: "Person.java"

Record:

java.lang.String firstName;

descriptor: Ljava/lang/String;

java.lang.String lastName;

descriptor: Ljava/lang/String;

BootstrapMethods:

0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;

Method arguments:

#8 examples/Person

#48 firstName;lastName

#50 REF_getField examples/Person.firstName:Ljava/lang/String;

#51 REF_getField examples/Person.lastName:Ljava/lang/String;

InnerClasses:

public static final #57= #53 of #55; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

我们要特别重视以下几点:

  1. 这个类被标记为 final ,意味着不能创建子类。
  2. 和所有的枚举都以 java.lang.Enum为基类一样, 所有的记录都以java.lang.Record为基类。
  3. 两个组件: firstNamelastName都是用privatefinal的。
  4. 有一个提供构造对象的公有构造函数:public examples.Person(java.lang.String, java.lang.String)。通过查看它的字节码,我们可以知道,这个构造函数只是将两个参数赋值给这两个组件。该构造函数等价于:

    public Person(String firstName, String lastName) {

    this.firstName = firstName;

    this.lastName = lastName;

    }

  5. 有两个获取对象值的方法,分别为 firstNamelastName.
  6. 自动生成 toString,hashCodeequals三个函数。他们都依赖invokedynamic来实现动态调用包含隐式实现函数在内的方法。从字节码中可以看到,有一个启动函数ObjectMethods.bootstrap来根据记录组件的名称和它的 Getter 函数,生成对应的函数。他们的表现和我们设想的一致:

Person john = new Person("John", "Doe");

System.out.println(john.firstName); // John

System.out.println(john.lastName); // Doe

System.out.println(john); // Person[firstName=John, lastName=Doe]

Person jane = new Person("Jane", "Dae");

Person johnCopy = new Person("John", "Doe");

System.out.println(john.hashCode); // 71819599

System.out.println(jane.hashCode); // 71407578

System.out.println(johnCopy.hashCode); // 71819599

System.out.println(john.equals(jane)); // false

System.out.println(john.equals(johnCopy)); // true

在记录的声明中添加成员

我们不能向记录中添加实例字段,这在意料之中,因为这种数据应该设置为组件。但是我们可以添加静态字段:

record Person(String firstName, String lastName){

static int x;

}

我们可以定义静态函数和实例函数来操作对象的状态。

record Person (String firstName, String lastName) {

static int x;

public static void dox{

x++;

}

public String getFullName{

return firstName + " " + lastName ;

}

}

我们也可以为记录添加构造函数,也可以编辑规范构造函数(带有两个字符串参数的构造函数)。如果你想重写规范构造函数,你可以编写一个不带参数的构造函数,不需要对属性进行赋值。

record Person (String firstName, String lastName) {

public Person {

if(firstName==||lastName==){

throw new IllegalArgumentException("firstName and lastName must not be ");

// 你可以忽略属性赋值,编译器会自动为你添加赋值代码

}

public Person(String fullName){

this(fullName.split("")[0], fullName.split("")[1]);

}

}

结论

记录为 Java 提供了一种正确实现数据类的能力,不再需要为实现数据类而编写冗长的代码。 这让编写纯数据类代码从几行缩减为一行代码。 还有一些其他预览的语言特性可以和记录搭配使用,比如模式匹配。 如果想深入了解记录和相关背景,请参阅 Brian Goetz 的 OpenJDK 文档(https://cr.openjdk.java.net/~briangoetz/amber/datum.html)。

推荐:开化县属于哪个市?

无棣县属于哪个市?

原文地址:https://www.cnblogs.com/1994july/p/12266126.html

时间: 2024-11-13 08:02:46

Java 14 有哪些新特性?的相关文章

Java核心技术之Java8新特性-Lambda表达式

1 总体说明 Java8新特性概述 函数式接口 Lambda表达式(闭包) 2 Java8新特性概述 Oracle公司于2014年3月发布了Java8正式版,该版本是自JDK5.0以来最具革命性的版本. Java8为Java语言.编译器.类库和JVM带来了大量的新特性.接下来的内容将会详细说明Java8在Java语言方面的新特性以及它们的使用场景. 3 函数式接口 Java8引入的一个核心概念是函数式接口(Functional Interfaces):如果一个接口定义一个唯一的抽象方法,那么这个

JAVA JDK1.5-1.9新特性

JAVA JDK1.5-1.9新特性 1.5 1.自动装箱与拆箱: 2.枚举(常用来设计单例模式) 3.静态导入 4.可变参数 5.内省 1.6 1.Web服务元数据 2.脚本语言支持 3.JTable的排序和过滤 4.更简单,更强大的JAX-WS 5.轻量级Http Server 6.嵌入式数据库 Derby 1.7 1,switch中可以使用字串了 2.运用List tempList = new ArrayList<>(); 即泛型实例化类型自动推断 3.语法上支持集合,而不一定是数组 4

java Servlet 3.0新特性例子

迟到的作业,Servlet3.0 已经出来很久了也没去关注,一直守旧于老的技术,今天抽时间学习了一下补个作业. Servlet3.0  特性: 1.Servlet.Filter.Listener无需在web.xml中进行配置,可以通过Annotation进行配置: 2.模块化编程,即将各个Servlet模块化,将配置文件也分开配置. 3.Servlet异步处理,应对复杂业务处理: 4.异步Listener,对于异步处理的创建.完成等进行监听: 5. 文件上传API简化: 备注:tomcat7.0

Java引入的一些新特性

Java引入的一些新特性 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本. Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等. 随着代码库越来越大,其创建也越来越复杂,这也就造成了我们在编写代码的过程中很难真正地对代码进行封装,类路径本身也存在一些问题,越来越多的问题也随之而来,比如数据和内存的溢出等,但是越来越复杂的代码库也为我们程序的编写

黑马程序员——java高新---JDK1.5新特性和反射

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.JDK1.5新特性 ——>静态导入 import和import static之间的区别: 1.import是导入一个类或某个包中所有的类. 2.import static是导入一个类中的某个静态成员或所有的静态成员. 注意: 1.当导入的两个类中有同名成员时,需要在成员前加上相应的类名. 2.当类名重名时,需要指定具体的包名. 3.方法重名时,需要指定具体所属的对象或者类. 代码示例: 1

java强化篇(一)---java 1.5的新特性

Java1.5的新特性 1.     快捷键使用技巧 快捷键的位置:General->keys,设置alt+/键(丁:content a 就会搜索出)进行内容提示时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove binding这个按钮. 1.显示系统提示:ALT+/ 2.程序代码自动排版:Ctrl+Shift+F(jsp文件是全部重排,java文件是可以对选定的代码重排) 3.自动汇入所需要的类别:Ctrl+Shift+O(

黑马程序员------Java中jdk1.5新特性

Java培训.Android培训.iOS培训..Net培训.期待与您交流! JDK1.5新特性: 为什么会出现新特性: 新的技术出现是为了解决老的问题,Java语言为了提高开发者的开发效率,对之前的某些不利于提高效率的技术进行改进. 静态导入: 静态导入:可以导入某个类下的静态方法,静态导入后,可以不写类名而直接使用此类下的静态方法. 语法:import static 包名.类名.静态方法 代码示例: package com.itheima.day1; /** * 静态导入 * @author

Java系列 – 用Java8新特性进行Java开发太爽了(续)

本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome-2/ 前言 上周, 我们谈论了关于Java8的新特性有那些, 什么是函数式编程, 什么是Lambda表达式, 这周让我们继续谈论这些新特性. 本周, 我们会聊一下什么是Stream API, 以及什么是Optional. Stream API你让我想重写我以前的所有代码 "Stream API你让我想重写我以前的所有代码",

Objective-C(14)Xcode7新特性

nullability nullability特性用来指明指针是否可以为nil,通过这种特性能够更清晰的表达API的意图. 同时编译器在编译时会对代码进行nullability检查,提示必要的警告. 使用时需要添加到NS_ASSUME_NONNULL_BEGIN - NS_ASSUME_NONNULL_END之间 对象指针:nullable    nonnull C指针: __nonnull    __nullable     __null_unspecified null_resettable