Java 11中的新功能和API详解系列1

Java 11中的新功能和API详解系列1

  • 2018.9.27
  • 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。

JDK 11在语言语法方面有一个小改动,增加了相当数量的新API,以及运行单文件应用程序而无需使用编译器的能力。此外,可以看到删除了java.se.ee聚合器模块,这可能会影响将现有应用程序迁移到JDK 11。

JEP-323:Lambda参数的本地变量语法

Java 10中引入了局部变量类型推断(JEP-286)。这简化了代码编写,因为不再需要显式声明局部变量的类型,而是可以使用关键字var。JEP-323将此语法的使用扩展到Lambda表达式的参数。看下面的例子:

list.stream()
    .map((var s) -> s.toLowerCase())
    .collect(Collectors.toList());

当然,精明的Java程序员会指出Lambda表达式已经有类型推断了,因此在这种情况下使用var会是多余的。我们可以轻松地编写相同的代码:

list.stream()
    .map(s -> s.toLowerCase())
    .collect(Collectors.toList());

那么为什么要添加var支持呢?答案是,它针对一种特殊情况,即在想要为Lambda参数添加注释时的情况。如果没有涉及类型,那么无法执行此操作。为了避免使用显式类型,我们可以使用var来简化这一点,因此:

list.stream()
    .map((@Notnull var s) -> s.toLowerCase())
    .collect(Collectors.toList());

此特性需要更改Java语言规范(JLS,Java Language Specification),具体如下:

  • 第24页:var特殊标识符的描述
  • 第627页-30:Lambda参数
  • 第636页:Lambda表达式的运行时评估
  • 第746页:Lambda语法

JEP-330:启动单文件源代码程序

Java经常面临的一个批评是它的语法可能很冗长,与运行一个简单的应用程序相关的“仪式”可能会使初学者感觉难以接近。要编写一个只打印“Hello World”的应用程序,它需要用户先编写一个类,编写public static void main方法,在方法中使用System.out.println方法。完成此操作后,还必须使用javac编译源代码。最后,使用java执行编译后的字节码程序,然后才能看到“Hello World”的输出。而在当前的大多数脚本语言中,要打印“Hello World”,操作非常简单快捷。

JEP-330消除了编译单个文件应用程序的需要,因此现在可以直接键入:

java HelloWorld.java

Java启动程序会自动识别HelloWorld.java文件包含了Java源代码,并在执行之前将源代码编译为字节码文件,省去了编译这一步骤。

源文件名后放置的内容,在程序执行时作为参数传递。源文件名前放置的内容,在程序编译时和执行时作为参数传递给Java启动程序。这允许在命令行上设置类路径。与编译器相关的参数(例如类路径)将传递给javac进行编译。比如:

java -classpath /home/foo/java Hello.java Bonjour

相当于:

javac -classpath /home/foo/java Hello.java
java -classpath /home/foo/java Hello Bonjour

这个JEP还提供了’Shebang’支持。为了减少在命令行上提及Java启动程序的需要,可以在源文件的第一行包含如下内容。例如:

#!/usr/bin/java --source 11
    public class HelloWorld {
        ...

注:Shebang也叫Hashbang,在计算领域,它是由井号和感叹号(#!)开头的字符序列组成。在类Unix操作系统中,当使用带有shebang的文本文件时,就好像它是可执行文件那样,程序加载器会将除文件首行的其余部分作为解释器指令进行解析,执行指定的解释器程序,并将尝试运行脚本时最初使用的路径作为参数传递给它,以便程序可以将该文件用作输入数据。shebang行通常被解释器忽略,因为符号“#”是许多脚本语言中的注释标记;一些不使用哈希标记开始的语言解释器仍然可能会忽略shebang行以识别其目的。

必须使用–-source标识指定要使用的Java版本。

JEP-321:HTTP客户端(标准)

Java 9引入了一个新的API来提供对HTTP客户端协议(JEP-110)的支持。由于Java 9引入了Java平台模块系统(JPMS,Java Platform Module System),因此该API作为孵化器模块包含在内。孵化器模块旨在提供新API,而不使它们成为Java SE标准的一部分。开发人员可以尝试这些API并提供反馈。一旦进行了任何必要的更改(此API已在JDK 10中更新),就可以移动这些API,使之以成为标准库的一部分。

HTTP Client API现在是Java SE 11标准库的一部分。这向JDK引入了一个新的模块和包。此API定义的主要类型有:

  • HttpClient
  • HttpRequest
  • HttpResponse
  • WebSocket

此API可以用于同步或异步。异步模式需使用CompletableFutures和CompletionStages。

JEP-320:移除了Java EE和CORBA模块

通过在Java 9中引入JPMS,可以将单片rt.jar文件拆分为多个模块。JPMS的另一个优点是现在可以创建一个仅包含应用程序所需模块的Java运行时,从而大大减小了程序的尺寸。通过清晰定义的模块边界,现在可以更轻松地删除过时的Java API部分。这是JEP的作用;java.se.ee元模块包含六个子模块,这些模块不再是Java SE 11标准库的一部分,不包含在JDK中。受影响的模块有:

  • CORBA
  • corba
  • transaction
  • activation
  • xml.bind
  • xml.ws
  • xml.ws.annotation

自Java 9以来,这些模块已被弃用,默认情况下不包含在编译或运行时中。如果您尝试在JDK 9或JDK 10上编译或运行使用这些模块的API的应用程序,则程序会失败。如果在代码中使用这些模块中的API,则需要将它们作为单独的模块或库提供。

新API

Java 11中的许​​多新API都来自HTTP客户端模块,现在它已成为标准的一部分,以及包含Flight Recorder。

在这里列出的是除了java.net.http和jdk.jfr模块之外的所有新方法。另外也没有列出java.security模块中的新方法和类,它们非常特定于JEP-324和JEP-329的更改(有六个新类和八个新方法)。

java.io.ByteArrayOutputStream

  • void writeBytes(byte[]):将参数的所有字节写入输出流

java.io.FileReader

两个允许指定Charset的新构造函数

java.io.FileWriter

四个允许指定Charset的新构造函数

java.io.InputStream

  • io.InputStream nullInputStream():返回InputStream未读取任何字节的内容。当你第一次看这个方法(以及那些OutputStream,Reader,和Writer)时,可以将它们视为/dev/null,以丢弃不需要的输出或提供始终返回零字节的输入。

java.io.OutputStream

  • io.OutputStream nullOutputStream()

java.io.Reader

  • io.Reader nullReader()

java.io.Writer

  • io.Writer nullWriter()

java.lang.Character

  • String toString(int):这是现有方法的重载形式,但采用int而不是char。int是Unicode代码点。

java.lang.CharSequence

  • int compare(CharSequence, CharSequence):按CharSequence字典顺序比较两个实例。如果第一个序列按字典顺序小于、等于或大于第二个序列,则返回负值、零或正值。

java.lang.ref.Reference

  • lang.Object clone():这个令人很困惑。此Reference类不实现Cloneable接口和此方法将总是抛出CloneNotSupportedException。必须有一个理由将其包含在内,大概是为了将来的某些东西。

java.lang.Runtime

java.lang.System

这里没有新方法,但值得一提的是,该runFinalizersOnExit()方法现已从这两个类中删除(这可能是兼容性问题)。

java.lang.String

我认为这是JDK 11中新API的亮点之一。这里有几个有用的新方法。

  • boolean isBlank():如果字符串为空或仅包含空格代码点(codepoints),则返回true,否则返回false。
  • Stream lines():返回从此字符串中提取的行的流,由行终止符分隔。
  • String repeat(int):返回一个字符串,其值是此字符串重复计数次数的串联。
  • String strip():返回一个字符串,其值为此字符串,删除了所有首部和尾部的空格。
  • String stripLeading():返回一个字符串,其值为此字符串,并删除所有首部空格。
  • String stripTrainling():返回一个字符串,其值为此字符串,并删除所有尾部空格。

那么strip()方法与现有的trim()方法有何不同?答案是两者之间的空格定义有所不同。

java.lang.StringBuffer

java.lang.StringBuilder

这两个类都有一个新compareTo()方法,它接受一个StringBuffer/StringBuilder并返回一个int。此方法与CharSequence的compareTo()方法用法相同。

java.lang.Thread

没有其他方法,但已删除destroy()方法和stop(Throwable)方法。在stop()不带参数的方法仍然保留。这可能会出现兼容性问题。

java.nio.ByteBuffer

java.nio.CharBuffer

java.nio.DoubleBuffer

java.nio.FloatBuffer

java.nio.LongBuffer

java.nio.ShortBuffer

所有这些类现在都有一个mismatch()方法,用于查找并返回此缓冲区与给定缓冲区之间第一个不匹配的相对索引。

java.nio.channels.SelectionKey

  • int interestOpsAnd(int):以原子方式将此键的兴趣集设置为现有兴趣集和给定值的按位取交集(“与操作”)
  • int interestOpsOr(int):以原子方式将此键的兴趣集设置为现有兴趣集和给定值的按位取合集(“或操作”)

### java.nio.channels.Selector中

  • int select(java.util.function.Consumer, long):为相应通道准备好进行I/O操作的键选择并执行操作。long参数是超时timeout。
  • int select(java.util.function.Consumer):如上所述,除非没有超时。
  • int selectNow(java.util.function.Consumer):如上所述,除非是非阻塞。

java.nio.file.Files

  • String readString(Path):将文件中的所有内容读入字符串,使用UTF-8字符集从字节解码为字符。
  • String readString(Path, Charset):如上所述,除了使用指定的字符集Charset从字节到字符的解码。
  • Path writeString(Path, CharSequence, java.nio.file.OpenOption[]:将CharSequence写入文件。使用UTF-8字符集将字符编码为字节。
  • Path writeString(Path, CharSequence, java.nio.file.Charset, OpenOption[]:如上所述,除了Characters使用指定的Charset编码为字节。

java.nio.file.Path

  • Path of(String, String[]):通过转换路径字符串或字符串数组连接时形成的路径字符串,返回一个Path。
  • Path of(net.URI):通过转换URI返回一个Path。

java.util.Collection

  • Object[] toArray(java.util.function.IntFunction):返回一个包含此集合中所有元素的数组,使用提供的生成器函数来分配返回的数组。

java.util.concurrent.PriorityBlockingQueue

java.util.PriorityQueue

  • void forEach(java.util.function.Consumer):对Iterable的每个元素执行给定操作,直到处理完所有元素或操作抛出异常为止。
  • boolean removeAll(java.util.Collection):删除也包含在指定集合中的所有此集合的元素(可选操作)。
  • boolean removeIf(java.util.function.Predicate):删除此集合中满足给定谓词的所有元素。
  • boolean retainAll(java.util.Collection):仅保留此集合中包含在指定集合中的元素(可选操作)。

java.util.concurrent.TimeUnit

  • long convert(java.time.Duration):将给定的Duratio持续时间转换为long型。

java.util.function.Predicate

  • Predicate not(Predicate)。返回谓词,该谓词是提供的谓词的否定。

这个很有趣。例如,可以转换下面的代码:

lines.stream()
    .filter(s -> !s.isBlank())

转换为:

lines.stream()
    .filter(Predicate.not(String::isBlank))

而且,如果我们使用静态导入,还可以转变为:

lines.stream()
    .filter(not(String::isBlank))

这样的代码更简洁,更容易理解。

java.util.Optional

java.util.OptionalInt

java.util.OptionalDouble

java.util.OptionalLong

  • boolean isEmpty():如果某个值不存在,则返回true,否则返回false。

java.util.regex.Pattern

  • Predicate asMatchPredicate():它创建一个谓词,测试此模式是否与给定的输入字符串匹配。

java.util.zip.Deflater

  • int deflate(ByteBuffer):压缩输入数据并使用压缩数据填充指定的缓冲区。
  • int deflate(ByteBuffer, int):压缩输入数据并使用压缩数据填充指定的缓冲区。返回压缩的实际数据字节数。
  • void setDictionary(ByteBuffer):将压缩的预设字典设置为给定缓冲区中的字节。这是现有方法的重载形式,现在可以接受ByteBuffer而不是字节数组。
  • void setInput(ByteBuffer):设置压缩的输入数据。也是现有方法的重载形式。

java.util.zip.Inflater

  • int inflate(ByteBuffer):将字节解压缩到指定的缓冲区中。返回未压缩的实际字节数。
  • void setDictionary(ByteBuffer):将预设字典设置为给定缓冲区中的字节。现有方法的重载形式。
  • void setInput(ByteBuffer):设置解压缩的输入数据。现有方法的重载形式。

javax.print.attribute.standard.DialogOwner

这是JDK 11中的一个新类,它是一个属性类,用于支持请求打印或页面设置对话框保持显示在所有窗口或某个特定窗口的顶部。

javax.swing.DefaultComboBoxModel

javax.swing.DefaultListModel

  • void addAll(Collection):添加集合中存在的所有元素。
  • void addAll(int, Collection):从指定的索引开始添加集合中存在的所有元素。

javax.swing.ListSelectionModel

  • int[] getSelectedIndices():按递增顺序返回选择模型中所有选定索引的数组。
  • int getSelectedItemsCount():返回所选项目的数量。

jdk.jshell.EvalException

  • jshell.JShellException getCause():返回在执行客户端中由EvalException表示的throwable包装的原因cause,如果原因不存在(non-existent)或未知(unknown),则返回null。

你可能感兴趣的:(Java,11,Java工具)