赶快看看Java11不然你就out了

47次阅读

共计 10020 个字符,预计需要花费 26 分钟才能阅读完成。

前言

赶快看看 Java11,不然你就 out 了!
这个是个人博客原文的链接。Java 更新的太快了,都学不过来了,最近了解一些 Java8 以后的一些特性,写下来希望对大家有帮助。

为什么选择 Java11

  • 容器环境支持,GC 等领域的增强。
  • 进行了瘦身,更轻量级,安装包体积小。
  • JDK11 是一个长期支持版。

特性介绍

由于直接从 Java8 跨越到 Java11,所以特性介绍就把 Java9-Java11 的部分特性一起介绍一下。想要了解 Java8 特性的朋友可以去我的博客找「Java8 系列」。

Jshell @since 9

Jshell 在 Java9 中就被提出来了,可以直接在终端写 Java 程序,回车就可以执行。Jshell 默认会导入下面的一些包, 所以在 Jshell 环境中这些包的内容都是可以使用的。

import java.lang.*;
import java.io.*;
import java.math.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.prefs.*;
import java.util.regex.*;
import java.util.stream.*;
1. 什么是 Jshell?

Jshell 是在 Java 9 中引入的。它提供了一个交互式 shell,用于快速原型、调试、学习 Java 及 Java API,所有这些都不需要 public static void main 方法,也不需要在执行之前编译代码。

2.Jshell 的使用

打开终端,键入 jshell 进入 jshell 环境,然后输入 /help intro 可以查看 Jshell 的介绍。

 lixiaoshuang@localhost  ~  jshell
|  欢迎使用 JShell -- 版本 11.0.2
|  要大致了解该版本, 请键入: /help intro

jshell> /help intro
|
|                                   intro
|                                   =====
|
|  使用 jshell 工具可以执行 Java 代码,从而立即获取结果。|  您可以输入 Java 定义(变量、方法、类等等),例如:int x = 8
|  或 Java 表达式,例如:x + x
|  或 Java 语句或导入。|  这些小块的 Java 代码称为“片段”。|
|  这些 jshell 工具命令还可以让您了解和
|  控制您正在执行的操作,例如:/list
|
|  有关命令的列表,请执行:/help

jshell>

Jshell 确实是一个好用的小工具,这里不做过多介绍,我就举一个例子,剩下的大家自己体会。比如我们现在就想随机生成一个 UUID,以前需要这么做:

  • 创建一个类。
  • 创建一个 main 方法。
  • 然后写一个生成 UUID 的逻辑,执行。

现在只需要, 进入打开终端键入 jshell, 然后直接输入 var uuid = UUID.randomUUID() 回车。就可以看到 uuid 的回显,这样我们就得到了一个 uuid。并不需要 public static void main(String[] args);

 lixiaoshuang@localhost  ~  jshell
|  欢迎使用 JShell -- 版本 11.0.2
|  要大致了解该版本, 请键入: /help intro

jshell> var uuid = UUID.randomUUID();
 uuid ==> 9dac239e-c572-494f-b06d-84576212e012
jshell>
3. 怎么退出 Jshell?

在 Jshell 环境中键入 /exit 就可以退出。

 lixiaoshuang@localhost  ~ 
 lixiaoshuang@localhost  ~  jshell
|  欢迎使用 JShell -- 版本 11.0.2
|  要大致了解该版本, 请键入: /help intro

jshell> var uuid = UUID.randomUUID();
uuid ==> 9dac239e-c572-494f-b06d-84576212e012

jshell> /exit
|  再见
 lixiaoshuang@localhost  ~ 

模块化(Module)@since 9

1. 什么是模块化?

模块化就是增加了更高级别的聚合,是 Package 的封装体。Package 是一些类路径名字的约定,而模块是一个或多个 Package 组成的封装体。

java9 以前:package => class/interface。

java9 以后:module => package => class/interface。

那么 JDK 被拆为了哪些模块呢?打开终端执行 java --list-modules 查看。

lixiaoshuang@localhost  ~ java --list-modules
java.base@11.0.2
java.compiler@11.0.2
java.datatransfer@11.0.2
java.desktop@11.0.2
java.instrument@11.0.2
java.logging@11.0.2
java.management@11.0.2
java.management.rmi@11.0.2
java.naming@11.0.2
java.net.http@11.0.2
java.prefs@11.0.2
java.rmi@11.0.2
java.scripting@11.0.2
java.se@11.0.2
java.security.jgss@11.0.2
java.security.sasl@11.0.2
java.smartcardio@11.0.2
java.sql@11.0.2
java.sql.rowset@11.0.2
java.transaction.xa@11.0.2
java.xml@11.0.2
java.xml.crypto@11.0.2
jdk.accessibility@11.0.2
jdk.aot@11.0.2
jdk.attach@11.0.2
jdk.charsets@11.0.2
jdk.compiler@11.0.2
jdk.crypto.cryptoki@11.0.2
jdk.crypto.ec@11.0.2
jdk.dynalink@11.0.2
jdk.editpad@11.0.2
jdk.hotspot.agent@11.0.2
jdk.httpserver@11.0.2
jdk.internal.ed@11.0.2
jdk.internal.jvmstat@11.0.2
jdk.internal.le@11.0.2
jdk.internal.opt@11.0.2
jdk.internal.vm.ci@11.0.2
jdk.internal.vm.compiler@11.0.2
jdk.internal.vm.compiler.management@11.0.2
jdk.jartool@11.0.2
jdk.javadoc@11.0.2
jdk.jcmd@11.0.2
jdk.jconsole@11.0.2
jdk.jdeps@11.0.2
jdk.jdi@11.0.2
jdk.jdwp.agent@11.0.2
jdk.jfr@11.0.2
jdk.jlink@11.0.2
jdk.jshell@11.0.2
jdk.jsobject@11.0.2
jdk.jstatd@11.0.2
jdk.localedata@11.0.2
jdk.management@11.0.2
jdk.management.agent@11.0.2
jdk.management.jfr@11.0.2
jdk.naming.dns@11.0.2
jdk.naming.rmi@11.0.2
jdk.net@11.0.2
jdk.pack@11.0.2
jdk.rmic@11.0.2
jdk.scripting.nashorn@11.0.2
jdk.scripting.nashorn.shell@11.0.2
jdk.sctp@11.0.2
jdk.security.auth@11.0.2
jdk.security.jgss@11.0.2
jdk.unsupported@11.0.2
jdk.unsupported.desktop@11.0.2
jdk.xml.dom@11.0.2
jdk.zipfs@11.0.2
2. 为什么这么做?

大家都知道 JRE 中有一个超级大的 rt.jar(60 多 M),tools.jar 也有几十兆,以前运行一个 hello world 也需要上百兆的环境。

  • 让 Java SE 程序更加容易轻量级部署。
  • 强大的封装能力。
  • 改进组件间的依赖管理,引入比 jar 粒度更大的 Module。
  • 改进性能和安全性。
3. 怎么定义模块?

模块的是通过 module-info.java 进行定义,编译后打包后,就成为一个模块的实体。下面来看下最简单的模块定义。

4. 模块的关键字
  • open

    用来指定开放模块, 开放模块的所有包都是公开的,public 的可以直接引用使用, 其他类型可以通过反射得到。

    open module module.one {
        // 导入日志包
       requires java.logging;
    
    }
  • opens

    opens 用来指定开放的包, 其中 public 类型是可以直接访问的, 其他类型可以通过反射得到。

    module module.one {opens <package>;}
  • exports

    exports 用于指定模块下的哪些包可以被其他模块访问。

    module module.one {
        
        exports <package>;
        
        exports <package> to <module1>, <module2>...;
    }
  • requires

    该关键字声明当前模块与另一个模块的依赖关系。

    module module.one {requires <package>;}
  • uses、provides…with…

    uses 语句使用服务接口的名字, 当前模块就会发现它, 使用 java.util.ServiceLoader 类进行加载, 必须是本模块中的, 不能是其他模块中的. 其实现类可以由其他模块提供。

    module module.one {
    
        // 对外提供的接口服务 , 下面指定的接口以及提供服务的 impl,如果有多个实现类,用用逗号隔开
        uses < 接口名 >;
    
        provides < 接口名 > with < 接口实现类 >,< 接口实现类 >;
    
    }

var 关键字 @since 10

1.var 是什么?

var 是 Java10 中新增的局部类型变量推断。它会根据后面的值来推断变量的类型,所以 var 必须要初始化。

例:

var a;       ❌
var a = 1;   ✅
2.var 使用示例
  • var 定义局部变量

    var a = 1; 
    等于
    int a = 1;
  • var 接收方法返回时

    var result = this.getResult();
    等于
    String result = this.getResult();
  • var 循环中定义局部变量

    for (var i = 0; i < 5; i++) {System.out.println(i);
    }
    等于
    for (int i = 0; i < 5; i++) {System.out.println(i);
    }
  • var 结合泛型

    var list1 = new ArrayList<String>();  // 在 <> 中指定了 list 类型为 String
    等于
    List<String> list1 = new ArrayList<>();
    
    var list2 = new ArrayList<>();        //<> 里默认会是 Object
  • var 在 Lambda 中使用(java11 才可以使用)

    Consumer<String> Consumer = (var i) -> System.out.println(i);
    等于
    Consumer<String> Consumer = (String i) -> System.out.println(i);
3.var 不能再哪里使用?
  • 类成员变量类型。
  • 方法返回值类型。
  • Java10 中 Lambda 不能使用 var,Java11 中可以使用。

增强 api

1. 字符串增强 @since 11
// 判断字符串是否为空白
" ".isBlank();                     // true

// 去除首尾空格
"Hello Java11".strip();          // "Hello Java11"

// 去除尾部空格 
"Hello Java11".stripTrailing();  // "Hello Java11"

// 去除首部空格 
"Hello Java11".stripLeading();   // "Hello Java11"

// 复制字符串
"Java11".repeat(3);                // "Java11Java11Java11"

// 行数统计
"A\nB\nC".lines().count();         // 3
2. 集合增强

从 Java 9 开始,jdk 里面就为集合(List、Set、Map)增加了 of 和 copyOf 方法。它们用来创建不可变集合。

  • of() @since 9
  • copyOf() @since 10

示例一:

        var list = List.of("Java", "Python", "C"); // 不可变集合
        var copy = List.copyOf(list);         //copyOf 判断是否是不可变集合类型,如果是直接返回
        System.out.println(list == copy);    // true

        var list = new ArrayList<String>();  // 这里返回正常的集合
        var copy = List.copyOf(list);        // 这里返回一个不可变集合
        System.out.println(list == copy);    // false

示例二:

        var set = Set.of("Java", "Python", "C");
        var copy = Set.copyOf(set);
        System.out.println(set == copy);     // true

        var set1 = new HashSet<String>();
        var copy1 = List.copyOf(set1);
        System.out.println(set1 == copy1);   // false

示例三:

        var map = Map.of("Java", 1, "Python", 2, "C", 3);
        var copy = Map.copyOf(map);
        System.out.println(map == copy);     // true

        var map1 = new HashMap<String, Integer>();
        var copy1 = Map.copyOf(map1);
        System.out.println(map1 == copy1);   // false

注意:使用 of 和 copyOf 创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常,使用 Set.of()不能出现重复元素、Map.of()不能出现重复 key,否则回报 java.lang.IllegalArgumentException。

3.Stream 增强 @since 9

Stream 是 Java 8 中的特性,在 Java 9 中为其新增了 4 个方法:

  • ofNullable(T t)

    此方法可以接收 null 来创建一个空流

    以前
    Stream.of(null);  // 报错
    现在
    Stream.ofNullable(null);
  • takeWhile(Predicate<? super T> predicate)

    此方法根据 Predicate 接口来判断如果为 true 就 取出 来生成一个新的流, 只要碰到 false 就终止,不管后边的元素是否符合条件。

            Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20);
            Stream<Integer> takeWhile = integerStream.takeWhile(t -> t % 2 == 0);
            takeWhile.forEach(System.out::println);   // 6,10
  • dropWhile(Predicate<? super T> predicate)

    此方法根据 Predicate 接口来判断如果为 true 就 丢弃 来生成一个新的流, 只要碰到 false 就终止,不管后边的元素是否符合条件。

            Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20);
            Stream<Integer> takeWhile = integerStream.dropWhile(t -> t % 2 == 0);
            takeWhile.forEach(System.out::println);  //11,15,20
  • iterate 重载

    以前使用 iterate 方法生成无限流需要配合 limit 进行截断

            Stream<Integer> limit = Stream.iterate(1, i -> i + 1).limit(5);
            limit.forEach(System.out::println);   //1,2,3,4,5

    现在重载后这个方法增加了个判断参数

            Stream<Integer> iterate = Stream.iterate(1, i -> i <= 5, i -> i + 1);
            iterate.forEach(System.out::println);  //1,2,3,4,5
4.Optional 增强 @since 9
  • stream()

    如果为空返回一个空流,如果不为空将 Optional 的值转成一个流。

            // 返回 Optional 值的流
            Stream<String> stream = Optional.of("Java 11").stream();
            stream.forEach(System.out::println);    // Java 11
            
            // 返回空流
            Stream<Object> stream = Optional.ofNullable(null).stream();
            stream.forEach(System.out::println);    // 
  • ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

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

            Optional<Integer> optional = Optional.of(1);
            optional.ifPresentOrElse(x -> System.out.println("Value:" + x),() ->
                    System.out.println("Not Present."));    //Value: 1
            
            optional = Optional.empty();
            optional.ifPresentOrElse(x -> System.out.println("Value:" + x),() ->
                    System.out.println("Not Present."));    //Not Present.
  • or(Supplier<? extends Optional<? extends T>> supplier)

            Optional<String> optional1 = Optional.of("Java");
            Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present");
            optional1 = optional1.or(supplierString);
            optional1.ifPresent(x -> System.out.println("Value:" + x));  //Value: Java
    
            optional1 = Optional.empty();
            optional1 = optional1.or(supplierString);
            optional1.ifPresent(x -> System.out.println("Value:" + x)); //Value: Not Present
5.InputStream 增强 @since 9
        String lxs = "java";
        try (var inputStream = new ByteArrayInputStream(lxs.getBytes());
             var outputStream = new ByteArrayOutputStream()) {inputStream.transferTo(outputStream);
            System.out.println(outputStream);    //java
        }

HTTP Client API

改 api 支持同步和异步两种方式,下面是两种方式的示例:

        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://www.baidu.com/"))
                .build();
        var client = HttpClient.newHttpClient();
        // 同步
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());

        // 异步
        CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        // 这里会阻塞
        HttpResponse<String> response1 = sendAsync.get();
        System.out.println(response1.body());

直接运行 java 文件

我们都知道以前要运行一个.java 文件,首先要 javac 编译成.class 文件,然后在 java 执行:

// 编译
javac Java11.java
// 运行
java Java11

在 java11 中,只需要通过 java 一个命令就可以搞定

java Java11.java

移除内容

  • com.sun.awt.AWTUtilities。
  • sun.misc.Unsafe.defineClass 使用 java.lang.invoke.MethodHandles.Lookup.defineClass 来替代。
  • Thread.destroy() 以及 Thread.stop(Throwable) 方法。
  • sun.nio.ch.disableSystemWideOverlappingFileLockCheck 属性。
  • sun.locale.formatasdefault 属性。
  • jdk snmp 模块。
  • javafx,openjdk 是从 java10 版本就移除了,oracle java10 还尚未移除 javafx,而 java11 版本将 javafx 也移除了。
  • Java Mission Control,从 JDK 中移除之后,需要自己单独下载。
  • Root Certificates:Baltimore Cybertrust Code Signing CA,SECOM,AOL and Swisscom。
  • 在 java11 中将 java9 标记废弃的 Java EE 及 CORBA 模块移除掉。

完全支持 Linux 容器(包括 docker)

许多运行在 Java 虚拟机中的应用程序(包括 Apache Spark 和 Kafka 等数据服务以及传统的企业应用程序)都可以在 Docker 容器中运行。但是在 Docker 容器中运行 Java 应用程序一直存在一个问题,那就是在容器中运行 JVM 程序在设置内存大小和 CPU 使用率后,会导致应用程序的性能下降。这是因为 Java 应用程序没有意识到它正在容器中运行。随着 Java 10 的发布,这个问题总算得以解决,JVM 现在可以识别由容器控制组(cgroups)设置的约束。可以在容器中使用内存和 CPU 约束来直接管理 Java 应用程序,其中包括:

  • 遵守容器中设置的内存限制
  • 在容器中设置可用的 CPU
  • 在容器中设置 CPU 约束

Java 10 的这个改进在 Docker for Mac、Docker for Windows 以及 Docker Enterprise Edition 等环境均有效。

总结

推荐个群

本人创建的 java 技术交流群,感兴趣的朋友可以扫码加入哦!

参考

https://segmentfault.com/a/11…

感谢大家的观看,希望多多关注哦。如有错误,烦请指正。

正文完
 0