JDK11新特性

作者: 老鼠AI大米_Java全栈 | 来源:发表于2019-03-11 15:35 被阅读9次

    JDK11是一个LTS版本(Long-Term-Support),带来了很多新的特性,能够使代码更加简洁、方便,一起来看下。

    1、本地变量类型推断(Local Var)

    在Lambda表达式中,可以使用var关键字来标识变量,变量类型由编译器自行推断。

    // LocalVar.java
    import java.util.Arrays;
    public class LocalVar {
        public static void main(String[] args) {
            Arrays.asList("Java", "Python", "Ruby")
                .forEach((var s) -> {
                    System.out.println("Hello, " + s);
                });
        }
    }
    

    局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型。

    2、字符串加强

    String新增了strip()方法,和trim()相比,strip()可以去掉Unicode空格,例如,中文空格:

    // 判断字符串是否为空白
    " ".isBlank(); // true
    // 去除首尾空格
    " Javastack ".strip(); // "Javastack"
    // 去除尾部空格
    " Javastack ".stripTrailing(); // " Javastack"
    // 去除首部空格
    " Javastack ".stripLeading(); // "Javastack "
    // 复制字符串
    "Java".repeat(3);// "JavaJavaJava"
    // 行数统计
    "A\nB\nC".lines().count(); // 3
    

    3、集合加强

    自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。

    示例1:

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

    示例2:

    var list = new ArrayList<String>();
    var copy = List.copyOf(list);
    System.out.println(list == copy); // false
    

    示例1和2代码差不多,为什么一个为true,一个为false?

    来看下它们的源码:

    static <E> List<E> of(E... elements) {
      switch (elements.length) { // implicit null check of elements
        case 0:
            return ImmutableCollections.emptyList();
        case 1:
            return new ImmutableCollections.List12<>(elements[0]);
        case 2:
            return new ImmutableCollections.List12<>(elements[0], elements[1]);
        default:
            return new ImmutableCollections.ListN<>(elements);
      }
    }
    static <E> List<E> copyOf(Collection<? extends E> coll) {
        return ImmutableCollections.listCopy(coll);
    }
    static <E> List<E> listCopy(Collection<? extends E> coll) {
        if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
            return (List<E>)coll;
        } else {
            return (List<E>)List.of(coll.toArray());
        }
    }
    

    可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

    示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.

    注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

    上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

    4、Stream 加强

    Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。

    1. 增加单个参数构造方法,可为null
    Stream.ofNullable(null).count(); // 0
    
    1. 增加 takeWhile 和 dropWhile 方法
    Stream.of(1, 2, 3, 2, 1)
    .takeWhile(n -> n < 3)
    .collect(Collectors.toList()); // [1, 2]
    

    从开始计算,当 n < 3 时就截止。

    Stream.of(1, 2, 3, 2, 1)
    .dropWhile(n -> n < 3)
    .collect(Collectors.toList()); // [3, 2, 1]
    

    这个和上面的相反,一旦 n < 3 不成立就开始计算。

    1. iterate重载

    这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

    5、Optional 加强

    Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

    Optional.of("javastack").orElseThrow(); // javastack
    Optional.of("javastack").stream().count(); // 1
    Optional.ofNullable(null)
    .or(() -> Optional.of("javastack"))
    .get(); // javastack
    

    6、InputStream 加强

    InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。

    var classLoader = ClassLoader.getSystemClassLoader();
    var inputStream = classLoader.getResourceAsStream("javastack.txt");
    var javastack = File.createTempFile("javastack2", "txt");
    try (var outputStream = new FileOutputStream(javastack)) {
        inputStream.transferTo(outputStream);
    }
    

    7、HTTP Client API

    这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。

    来看一下 HTTP Client 的用法:

    var request = HttpRequest.newBuilder()
    .uri(URI.create("https://javastack.cn"))
    .GET()
    .build();
    var client = HttpClient.newHttpClient();
    // 同步
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
    // 异步
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println);
    

    上面的 .GET() 可以省略,默认请求方式为 Get!

    8、读写文件

    对Files类增加了writeStringreadString两个静态方法,可以直接把String写入文件,或者把整个文件读出为一个String:

    Files.writeString(
        Path.of("./", "tmp.txt"), // 路径
        "hello, jdk11 files api", // 内容
        StandardCharsets.UTF_8); // 编码
    String s = Files.readString(
        Paths.get("./tmp.txt"), // 路径
        StandardCharsets.UTF_8); // 编码
    

    这两个方法可以大大简化读取配置文件之类的问题。

    9、单文件代码

    这个功能允许你直接使用java解析器运行java代码。java文件会在内存中执行编译并且直接执行。唯一的约束在于所有相关的类必须定义在一个java文件中。
    最基础的案例
    把以下代码保存到Hello.java文件中:

    public class HelloWorld{
        public static void main(String[] args){
            System.out.println("Hello World!!!");
        }
    }
    

    我们将会按照下面的方法来执行上面的代码:

    PS G:\samples\java11\single-file> java HelloWorld.java
    Hello World!!!
    

    在上面的例子,我们仅仅只是在一个类中包含了一个main方法。我们直接使用java命令去执行这个.java文件。如果这个文件不是以.java结尾,我们可以使用—source参数来执行.
    包含命令行参数
    接下来的案例,我们传入一个参数

    public class Greeting{
        public static void main(String[] args){
            if ( args == null || args.length < 1 ){
                System.err.println("Name required");
                System.exit(1);
            }
            System.out.println(String.format("Hello %s!!", args[0]));
        }
    }
    

    我们把上面的代码保存到HelloGreeting.java文件中。注意,这个文件名字和类的名字不匹配。我们按照如下命令执行:

    PS G:\samples\java11\single-file> java HelloGreeting.Java sana
    Hello sana!!
    

    任何一个跟在文件名后面的参数都被作为方法的参数传入方法执行。
    我们把HelloGreeting.java直接重新命名为greeting(注意,没有.java后缀),我们再次执行:

    PS G:\samples\java11\single-file> java greeting sana
    Error: Could not find or load main class greeting
    Caused by: java.lang.ClassNotFoundException: greeting
    

    可以看到,在没有.java结尾的情况下,java编译器会尝试直接使用传入的名称作为类名去寻找.class文件。在这种情况下,我们需要使用—source选项:

    PS G:\samples\java11\single-file> java --source 11 greeting sana
    Hello sana!!
    

    一个文件中包含多个类
    文章开头我就提到,这个特性只是要求所有需要执行的代码是在同一个java文件中即可,而没有规定在这个java文件中只能有一个类。我们下面就来看看在一个java文件中包含多个类的情况:

    public class SimpleInterest{
        public static void main(String[] args){
            if ( args == null || args.length < 3 ){
                System.err.println("Three arguments required: principal, rate, period");
                System.exit(1);
            }
            int principal = Integer.parseInt(args[0]);
            int rate = Integer.parseInt(args[1]);
            int period = Integer.parseInt(args[2]);
            double interest = Maths.simpleInterest(principal, rate, period);
            System.out.print("Simple Interest is: " + interest);
        }
    }
     
    public class Maths{
     
        public static double simpleInterest(int principal, int rate, int period){
            return ( principal * rate * period * 1.0 ) / 100;
        }
    }
    

    我们来运行这个代码:

    PS G:\samples\java11\single-file> java .\SimpleInterest.java 1000 2 10
    Simple Interest is: 200.0
    

    在这个文件中,我们定义了多个类,但是在执行的时候,java编译器会运行这个文件中第一个类中的main方法(注:意思是,这个文件中的第一个类需要包含main方法,并且这个main方法作为运行的方法)

    Shebang文件
    在本小节中,我们会创建一个shebang文件。Shebang文件是Unix系统中常见的文件,以#!/path/to/executable作为文件的开头第一行,可以作为脚本小程序直接运行一段代码。

    我们来创建一个shebang文件:

    #!/g/jdk-11/bin/java --source 11
     
    public class SimpleInterest{
        public static void main(String[] args){
            if ( args == null || args.length < 3 ){
                System.err.println("Three arguments required: principal, rate, period");
                System.exit(1);
            }
            int principal = Integer.parseInt(args[0]);
            int rate = Integer.parseInt(args[1]);
            int period = Integer.parseInt(args[2]);
            double interest = Maths.simpleInterest(principal, rate, period);
            System.out.print("Simple Interest is: " + interest);
        }
    }
     
    public class Maths{
     
        public static double simpleInterest(int principal, int rate, int period){
            if ( rate > 100 ){
                System.err.println("Rate of interest should be <= 100. But given values is " + rate);
                System.exit(1);
            }
            return ( principal * rate * period * 1.0 ) / 100;
        }
    }
    

    当文件的名字不符合java命名规范的时候,就可以创建shebang文件来执行。比如我们上面的代码就可以保存在一个叫simpleInterest的文件中。我们需要按照下面的方式来运行:

    sanaulla@Sana-Laptop  /g/samples/java11/single-file (master)
    $ ./simpleInterest 1000 20 2
    Simple Interest is: 400.0
    

    在windows下,我们只能使用bash shell来执行。当然,还有诸如Cygwin,Windows 10 Ubuntu Support等工具来执行。

    相关文章

      网友评论

        本文标题:JDK11新特性

        本文链接:https://www.haomeiwen.com/subject/cfqbpqtx.html