Java 11 新增 API 初探

Java 11 发布也有一段时间了。大家关注比较密切的,主要是 ZGC、黑匣、低开销堆采样等等新特性,还有就是 Oracle JDK 商用开始收费了。作为一个开发者,除了这些以外,我还比较在意 JDK 提供的接口发生了哪些变化。下面就来简单的罗列一下,相比 Java 10,新的 JDK 在 API 上有哪些变化。

集合

java.util.Collection

新增了一个默认方法

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

个人认为,该方法几乎是集合转数组的不二之选,预计后续会逐步废弃旧的 toArray 方法。

Collection<String> strs = ...;
String[] arr = strs.toArray(String[]::new);

字符串

java.lang.String

新增了几个处理空白字符串的方法。对 demo 类的小项目可以一用,成熟的项目大多都有 StringUtils 之类的第三方工具类来处理了,总的来说意义不大。

// 去除前后空格
public String strip()
// 去除前面的空格
public String stripLeading()
// 去除后面的空格
public String stripTrailing()
// 是否为空字符串或只包含空白字符,显然不能判断 null
public boolean isBlank()

另外还有两个新方法

// 按照换行符分割字符串并返回流
public Stream<String> lines()
// 构造一个重复 n 次的字符串
public String repeat(int count)

repeat(int) 暂时想不到实际应用的场景,在传统的 StringUtils 中也有提供。lines() 方法是对 split 方法的一个高频特例(以换行符分割)封装,同时返回 Stream 比数组更加友好。

java.lang.StringBuilder && StringBuffer

StringBuilder 和 StringBuffer 现在实现了 Comparable 接口,与此同时,CharSequence 接口也新增了一个 compare 的静态方法,比较两个字符串字典序。

文件和流

java.io.FileReader

新增了两个构造方法,允许指定文件的字符集

java.nio.file.Files

新增了 4 个静态工具方法,用于直接向文件中读写 String

public static String readString(Path path) throws IOException
public static String readString(Path path, Charset cs) throws IOException
public static Path writeString(Path path, CharSequence csq, OpenOption... options) throws IOException
public static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options) throws IOException

这里有一个小细节值得注意。 readString 本质上是调用原有的 readAllBytes 方法获得 byte 数组,然后用 byte 数组生成 String。但是生成 String 的时候并没有用我们常规的 new String(byte[]),而是使用了 JavaLangAccess.newStringNoRepl 方法。这是一个内部方法,作用是新建一个 String 对象,并且方法的调用者会放弃 byte 数组的拥有关系并传递给被调用者(应该是类似传递指针的操作),所以在创建 String 的过程中不会发生数组拷贝,在性能和 gc 上会有一定的优势(具体还没有测试过)。同样的,writeString 也调用了 JavaLangAccess.getBytesNoRepl 来避免额外的拷贝。

这几个方法在进行小文件读写时还是比较推荐使用的,一方面更简洁,另一方面性能也不会差。

java.io.InputStream

新增了两个方法

/**
 * 创建一个空的流,重复调用 close() 也没有副作用。在没有
 * close 的情况下,调用通常的读方法相当于读到普通流的末尾。
 */
public static InputStream nullInputStream()
public byte[] readNBytes(int len) throws IOException

nullInputStream() 用法暂时不明,看起来像是作为一个占位符,类似 Optional 一样,减少非空的判断,使代码更加简洁。

readNByte(int) 方法则提供了一个更加人性化的调用方式,不用传入 byte[] 数组,好处一是使代码更简洁,二是对 lambda 表达式更友好。

InputStream is = getInputStream();
// 旧接口
byte[] arr1 = new byte[4];
is.read(arr1);
// 新接口
byte[] arr2 = is.readNByte(4);
// 应用于 lambda 表达式
List<Integer> lenList = Arrays.asList(1, 2, 3, 4);
lenList.map(is::readNBytes);

java.io.OutputStream && Reader && Writer

与 InputStream 类似,分别新增了一个创建空的占位对象的静态方法。

java.nio.file.Path

将原本在 Paths 类下的两个静态工具方法移到了 Path 接口中,原 Paths 类下的方法考虑到兼容性还继续保留,但官方提示未来可能会废弃 Paths 类。我们在编码时可以考虑逐步进行迁移。

NIO

java.nio.xBuffer

所有 xBuffer 类都新增了一个 public int mismatch(xBuffer that) 方法,用于比较两个 Buffer 是否一致,如果一致返回 -1,不一致时返回第一位不一致的索引。涉及以下 Buffer 类

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

jdk.nio.Channels

Channels 是新增的工具类(区别于 java.nio.Channels),提供了一个静态方法用于创建 Channel

public static SelectableChannel readWriteSelectableChannel(FileDescriptor fd, SelectableChannelCloser closer)

其中 SelectableChannelCloser 接口有 2 个方法,定义关闭和释放 Channel 的操作。

void implCloseChannel(SelectableChannel sc) throws IOException
void implReleaseChannel(SelectableChannel sc) throws IOException

个人猜测,由于 java.nio.Channels 主要提供的是 Channel 和流相关的操作,为了区别开所以分在了不同包下。但是从工程的角度来看,JDK 提供两个同名的类会使开发者难以理解,不是一种好的实现方式,推测未来可能进行重构。

java.nio.channels.Selector

新增了 3 个方法,允许传入 Consumer 直接操作 SelectionKey

public int select(Consumer<SelectionKey> action, long timeout) throws IOException
public int select(Consumer<SelectionKey> action) throws IOException
public int selectNow(Consumer<SelectionKey> action) throws IOException

java.lang.Class

Class 类新增了三个方法,用于获取嵌套类关系

// 获取宿主类。非嵌套类的宿主类是它本身。
public Class<?> getNestHost()
// 判断该类是否是某个类的嵌套类
public boolean isNestmateOf(Class<?> c)
// 返回某个类的嵌套类数组。第 1 个固定是宿主类,之后的是该宿主类的嵌套成员,但不保证顺序,同时也会包含自身
public Class<?>[] getNestMembers()

这个改动主要和 JEP 181: Nest-Based Access Control 有关。JEP 181 是 JVM 底层机制改动,对 Java 语法而言,类的可见性并没有改变,在此不展开讨论,暂时也没有想到使用场景。

另外有一点值得注意,lambda 表达式的类型和匿名内部类的宿主类是不一样的。

public class Test {
    public static void main(String[] args) {
        
        // class org.boreas.demo.jdk11.Test$$Lambda$14/0x0000000800066840
        System.out.println(((Function<Object, Object>) o -> null).getClass().getNestHost());
        // class org.boreas.demo.jdk11.Test
        System.out.println(new Function<>() {
            @Override
            public Object apply(Object o) {
                return null;
            }
        }.getClass().getNestHost());
    }

    public class NestClass {}
}

net

jdk.net.ExtendedSocketOptions

新增三个标准的 Keep-Alive 参数配置

TCP_KEEPIDLE: 链路空闲秒数,当 n 秒内未传数据时开始发心跳
TCP_KEEPINTERVAL: probe 重传间隔秒数,当未获得 ack 时再次发送 probe 的秒数
TCP_KEEPCOUNT: 最大重试次数,连续 n 次未获得 ack 后认为连接失效

java.net.http

这部分(JEP 321)主要是代码结构的优化。将 Java 9 引入的 HttpClient、WebSocket 由 jdk.incubator.http 包移动至 java.net.http 包下,并将相关内部类移入 jdk.internal.net.http 包下。

杂项

java.lang.Character.UnicodeBlock

UnicodeBlock 新增了 29 个常量,用于支持 Unicode 10,细节不表。

java.util.zip.Deflater && InFlater

分别新增了三个方法,允许使用 ByteBuffer 作为输入输出数据(此前只能使用 byte 数组),算是在性能上提供了一定优化的余地。

java.util.function.Predicate

新增了一个静态方法创建反向条件校验。实现是旧的 negate() 方法,目的也是为了优化在 lambda 表达式中的调用。

static <T> Predicate<T> not(Predicate<? super T> target)

java.util.concurrent.TimeUnit

新增一个方法用于时间单位换算

public long convert(Duration duration)

值得注意的是,该方法对纳秒做了优化,不会抛出数据类型溢出的异常

java.lang.ref.Refernce

禁用了 clone() 方法,调用时会抛出 CloneNoteSupportedException

java.lang.Thread

移除了 destroy() 方法

另外,Swing, TLS 1.3 相关的内容在此就不一一列举了。

以上。

推荐阅读更多精彩内容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    一个会写诗的程序员阅读 2,004评论 2 6
  • pdf下载地址:Java面试宝典 第一章内容介绍 20 第二章JavaSE基础 21 一、Java面向对象 21 ...
    王震阳阅读 78,878评论 25 511
  • 废话不多说,自己进入今天的主题 1、面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: - 抽象:...
    劳累的前端程序员阅读 1,488评论 1 31
  • 有人喜欢踏上陌生城市的土地,有人喜欢约会陌生的朋友,甚至有人喜欢打开快递包裹的瞬间,因为新奇感是生活的必需品...
    大厝悟藏阅读 47评论 0 1
  • 我们像一只迁徙的鸟 降落,俯冲,腾飞 将脸埋入土地 扭曲双翅 光着脚丫 试探天空与土地
    查理一猫阅读 17评论 0 0