Java9新特性

Java9 新特性

REPL (JShell)

REPL(Read Eval Print Loop)意为交互式的编程环境。

JShell 是 Java 9 新增的一个交互式的编程环境工具。它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。

执行JShell
C:\Users\zdk>jshell
|  欢迎使用 JShell -- 版本 11.0.11
|  要大致了解该版本, 请键入: /help intro

jshell>
查看JShell命令
jshell> /help
|  键入 Java 语言表达式, 语句或声明。
|  或者键入以下命令之一:
|  /list [<名称或 id>|-all|-start]
|       列出您键入的源
|  /edit <名称或 id>
|       编辑源条目
|  /drop <名称或 id>
|       删除源条目
|  /save [-all|-history|-start] <文件>
|       将片段源保存到文件
|  /open <file>
|       打开文件作为源输入
|  /vars [<名称或 id>|-all|-start]
|       列出已声明变量及其值
|  /methods [<名称或 id>|-all|-start]
|       列出已声明方法及其签名
|  /types [<名称或 id>|-all|-start]
|       列出类型声明
|  /imports
|       列出导入的项
|  /exit [<integer-expression-snippet>]
|       退出 jshell 工具
|  /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] ...
|       查看或更改评估上下文
|  /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]...
|       重置 jshell 工具
|  /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]...
|       重置和重放相关历史记录 -- 当前历史记录或上一个历史记录 (-restore)
|  /history [-all]
|       您键入的内容的历史记录
|  /help [<command>|<subject>]
|       获取有关使用 jshell 工具的信息
|  /set editor|start|feedback|mode|prompt|truncation|format ...
|       设置配置信息
|  /? [<command>|<subject>]
|       获取有关使用 jshell 工具的信息
|  /!
|       重新运行上一个片段 -- 请参阅 /help rerun
|  /<id>
|       按 ID 或 ID 范围重新运行片段 -- 参见 /help rerun
|  /-<n>
|       重新运行以前的第 n 个片段 -- 请参阅 /help rerun
|
|  有关详细信息, 请键入 '/help', 后跟
|  命令或主题的名称。
|  例如 '/help /list' 或 '/help intro'。主题:
|
|  intro
|       jshell 工具的简介
|  keys
|       a description of readline-like input editing
|  id
|       片段 ID 以及如何使用它们的说明
|  shortcuts
|       片段和命令输入提示, 信息访问以及
|       自动代码生成的按键说明
|  context
|       /env /reload 和 /reset 的评估上下文选项的说明
|  rerun
|       重新评估以前输入片段的方法的说明

jshell>
JShell 执行计算

以下实例执行 JShell 简单计算:

jshell> 3+5
$1 ==> 8

jshell> 9%4
$2 ==> 1

jshell> $1
$1 ==> 8
JShell使用和创建函数
jshell> int doubled(int i){return i*5;}
|  已创建 方法 doubled(int)

jshell> doubled(5)
$5 ==> 25

集合工厂方法

Java 9 List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。

新方法创建集合

Java 9 中,以下方法被添加到 List,Set 和 Map 接口以及它们的重载对象。

static <E> List<E> of(E e1, E e2, E e3);
static <E> Set<E>  of(E e1, E e2, E e3);
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3);
static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)
简单使用示例
    /**
     * Java 9 List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。
     * 之前的方式:set = Collections.unmodifiableSet(set);
     */
    public static void collectionFactoryTest() {
        var set = Set.of("set1", "set2");
        System.out.println(set);

        // set为不可变集合,这里新增元素会抛出异常:java.lang.UnsupportedOperationException
        // set.add("e");

        Map<String, String> map = Map.of("A","Apple","B","Boy","C","Cat");
        System.out.println(map);

        // 之前的不可变类实现方式
        // Collections.unmodifiableList(new ArrayList())
    }

私有接口方法

在 Java 9 中,一个接口中能定义如下几种变量/方法:

  • 常量
  • 抽象方法
  • 默认方法
  • 静态方法
  • 私有方法(新)
  • 私有静态方法(新)

示例:

interface Logging {
   String ORACLE = "Oracle_Database";
   String MYSQL = "MySql_Database";
 
   private void log(String message, String prefix) {
      getConnection();
      System.out.println("Log Message : " + prefix);
      closeConnection();
   }
   default void logInfo(String message) {
      log(message, "INFO");
   }
   default void logWarn(String message) {
      log(message, "WARN");
   }
   default void logError(String message) {
      log(message, "ERROR");
   }
   default void logFatal(String message) {
      log(message, "FATAL");
   }
   private static void getConnection() {
      System.out.println("Open Database connection");
   }
   private static void closeConnection() {
      System.out.println("Close Database connection");
   }
}

Java9 改进的进程API

在 Java 9 之前,Process API 仍然缺乏对使用本地进程的基本支持,例如获取进程的 PID 和所有者,进程的开始时间,进程使用了多少 CPU 时间,多少本地进程正在运行等。

Java 9 向 Process API 添加了一个名为 ProcessHandle 的接口来增强 java.lang.Process 类。

ProcessHandle 接口的实例标识一个本地进程,它允许查询进程状态并管理进程。

ProcessHandle 嵌套接口 Info 来让开发者逃离时常因为要获取一个本地进程的 PID 而不得不使用本地代码的窘境。

我们不能在接口中提供方法实现。如果我们要提供抽象方法和非抽象方法(方法与实现)的组合,那么我们就得使用抽象类。

ProcessHandle 接口中声明的 onExit() 方法可用于在某个进程终止时触发某些操作。

示例:
    /**
     * Java9 改进的进程API
     * @throws IOException io异常
     */
    public static void processAPI() throws IOException {
        ProcessBuilder pb = new ProcessBuilder("notepad.exe");
        String np = "Not Present";
        Process p = pb.start();
        ProcessHandle.Info info = p.info();
        System.out.printf("Process ID : %s%n", p.pid());
        System.out.printf("Command name : %s%n", info.command().orElse(np));
        System.out.printf("Command line : %s%n", info.commandLine().orElse(np));

        var infoInstant = info.startInstant().map(i -> i.atZone(ZoneId.systemDefault()).toLocalDateTime().toString()).orElse(np);
        System.out.printf("Start time: %s%n", infoInstant);

        var infoArguments = info.arguments().map(i -> Stream.of(i).collect(Collectors.joining(" "))).orElse(np);
        System.out.printf("Arguments : %s%n", infoArguments);

        System.out.printf("User : %s%n", info.user().orElse(np));
    }
执行结果:
Process ID : 27372
Command name : C:\Windows\System32\notepad.exe
Command line : Not Present
Start time: 2021-07-25T10:47:55.322
Arguments : Not Present
User : 朱登奎-LEGION\zdk

Java9 改进的Stream API

Java 9 改进的 Stream API 添加了一些便利的方法,使流处理更容易,并使用收集器编写复杂的查询。

Java 9 为 Stream 新增了几个方法:dropWhile、takeWhile、ofNullable,为 iterate 方法新增了一个重载方法。

takeWhile

使用一个断言作为参数,返回给定 Stream 的子集直到断言语句第一次返回 false。如果第一个值不满足断言条件,将返回一个空的 Stream。

takeWhile() 方法在有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素;在无序的 Stream 中,takeWhile 返回从开头开始的符合 Predicate 要求的元素的子集。

定义:

    default Stream<T> takeWhile(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        // Reuses the unordered spliterator, which, when encounter is present,
        // is safe to use as long as it configured not to split
        return StreamSupport.stream(
                new WhileOps.UnorderedWhileSpliterator.OfRef.Taking<>(spliterator(), true, predicate),
                isParallel()).onClose(this::close);
    }
dropWhile

dropWhile 方法和 takeWhile 作用相反的,使用一个断言作为参数,直到断言语句第一次返回 false 才返回给定 Stream 的子集。

iterator

方法允许使用初始种子值创建顺序(可能是无限)流,并迭代应用指定的下一个方法。 当指定的 hasNext 的 predicate 返回 false 时,迭代停止。

ofNullable

ofNullable 方法可以预防 NullPointerExceptions 异常, 可以通过检查流来避免 null 值。

如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。

示例:

    /**
     * 输出:
     * takeWhile:abc
     * dropWhile:efhi
     * iterator:369
     * ofNullable:0
     */
    public static void streamAPI() {
        System.out.print("takeWhile:");
        Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty()).forEach(System.out::print);

        System.out.print("\ndropWhile:");
        Stream.of("a","b","c","","e","f","","h","i").dropWhile(s->!s.isEmpty()).forEach(System.out::print);

        System.out.print("\niterator:");
        IntStream.iterate(3, x -> x < 10, x -> x + 3).forEach(System.out::print);
    
        System.out.print("\nofNullable:");
        System.out.println(Stream.ofNullable(null).count());
    }

Java9 改进的Optional类

Optional 类在 Java 8 中引入,Optional 类的引入很好的解决空指针异常。。在 java 9 中, 添加了三个方法来改进它的功能:

  • stream()
  • ifPresentOrElse()
  • or()
stream

stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。

ifPresentOrElse

ifPresentOrElse 方法的改进就是有了 else,接受两个参数 Consumer 和 Runnable。

ifPresentOrElse 方法的用途是,如果一个 Optional 包含值,则对其包含的值调用函数 action,即 action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse 还有第二个参数 emptyAction —— 如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()。

or

如果值存在,返回 Optional 指定的值,否则返回一个预设的值。

示例:

    /**
     * 输出:
     * [A, B]
     * Not Present.
     * Present Value:A
     * Not Present.
     * Present Value:B
     * Optional[Not Present.]
     * Optional[A]
     * Optional[Not Present.]
     * Optional[B]
     */
    public static void optionalAPI() {
        List<Optional<String>> optionalList = Arrays.asList (Optional.empty(), Optional.of("A"), Optional.empty(), Optional.of("B"));
        
        // stream
        List<String> stringList = optionalList.stream().flatMap(Optional::stream).collect(Collectors.toList());
        System.out.println(stringList);

        // ifPresentOrElse
        optionalList.forEach(optional -> optional.ifPresentOrElse(o -> System.out.println("Present Value:" + o), 
            () -> System.out.println("Not Present.")));

        // or
        optionalList.forEach(optional -> System.out.println(optional.or(() -> Optional.of("Not Present."))));
    }

Java9 改进的 CompletableFuture API

Java 9 对 CompletableFuture 做的改进:

  • 支持 delays 和 timeouts
  • 提升了对子类化的支持
  • 新的工厂方法
支持 delays 和 timeouts
public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

timeout(单位在 java.util.concurrent.Timeunits units 中,比如 MILLISECONDS )前以给定的 value 完成这个 CompletableFutrue。返回这个 CompletableFutrue。

public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)

如果没有在给定的 timeout 内完成,就以 java.util.concurrent.TimeoutException 完成这个 CompletableFutrue,并返回这个 CompletableFutrue。

增强了对子类化的支持

做了许多改进使得 CompletableFuture 可以被更简单的继承。比如,你也许想重写新的 public Executor defaultExecutor() 方法来代替默认的 executor

另一个新的使子类化更容易的方法是:

public <U> CompletableFuture<U> newIncompleteFuture()
新的工厂方法

Java 8引入了 CompletableFuture completedFuture(U value) 工厂方法来返回一个已经以给定 value 完成了的 CompletableFuture。Java 9以 一个新的 CompletableFuture failedFuture(Throwable ex) 来补充了这个方法,可以返回一个以给定异常完成的 CompletableFuture

除此以外,Java 9 引入了下面这对 stage-oriented 工厂方法,返回完成的或异常完成的 completion stages:

  • CompletionStage completedStage(U value): 返回一个新的以指定 value 完成的CompletionStage ,并且只支持 CompletionStage 里的接口。
  • CompletionStage failedStage(Throwable ex): 返回一个新的以指定异常完成的CompletionStage ,并且只支持 CompletionStage 里的接口。

Others

平台级模块系统
进程API的改进

之前启动一个进程的唯一方法是:Runtime.getRuntime().exec();Java9新增ProcessHandle接口来增强java.lang.Process类。

try-with-resources的改进

jdk1.7开始引入了try-with-resources使得代码变得简洁,增强代码可读性,也能更好地管理资源避免内存泄漏。
但jdk1.7中try语句中不能直接使用外部声明的任何资源,需要重新声明一个额外的引用,Java9对这里进行了改进,可以直接引用外部声明的资源。

posted @ 2021-05-04 22:38  Abserver  阅读(86)  评论(0)    收藏  举报