Loading

JDK9

1 模块化系统

1.1 JDK9之前没有解决的问题

  • 1️⃣Java运行环境代码臃肿、效率低:JDK9之前,每一个runtime自带开箱即用的所有编译好的平台类,这些类被一起打包到一个JRE文件叫做rt.jar。我们只需要将我们应用的类放到classpath中,这样runtime就可以找到,而其他的平台类就可以简单粗暴的从rt.jar文件中寻找。尽管我们的应用只用到了这个庞大的rt.jar的一部分,但是,这对JVM管理来说,不仅增加了非必要类的体积,还增加了性能负载。JDK9的模块化可以按需自定义runtime,这也是JDK9文件夹下没有jre目录的原因。
  • 2️⃣无法隐藏内部API和类型:很难真正的对代码进行封装,系统对于不同部分的代码无法分离。在早起我们实现封装都是需要依赖权限修饰符,而权限修饰符只能修改类、成员变量、成员方法。权限修饰符不能帮助我们对包进行隐藏,JDK9我们可以通过隐藏包从而隐藏包中的所有类。

1.2 模块化的设计理念

  • 模块独立、化繁为简:模块化将JDK分成一组模块,可以在编译时、运行时或构建时进行组合。

1.3 模块化的实现目标

  • 1️⃣主要目的在于减少内存的开销。
  • 2️⃣只需必要模块,而非全面JDK模块,可以简化各种类库和大型应用的开发和维护。
  • 3️⃣改进JavaSE平台,使其可以适用不同大小的计算设备。
  • 4️⃣改进其安全性,可维护性,提高性能。

1.4 应用示例

  • 应用示例结构化图:

模块化应用示例

  • 应用示例步骤:

    • 新建jdk9工程,其pom如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.sunxiaping</groupId>
        <artifactId>jdk9</artifactId>
        <packaging>pom</packaging>
        <version>1.0</version>
        <modules>
            <module>module1</module>
            <module>module2</module>
        </modules>
    
        <build>
            <plugins>
                <!-- 设置JDK 9版本 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>9</source>
                        <target>9</target>
                        <encoding>UTF-8</encoding>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 在jdk9工程下新建module1模块,其pom.xml如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>jdk9</artifactId>
            <groupId>com.sunxiaping</groupId>
            <version>1.0</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>module1</artifactId>
    </project>
    
    • 在module1模块下新建Person.java
    package com.sunxiaping.bean;
    
    public class Person {
        private String name;
    
        private Integer age;
    
        public Person() {
        }
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return this.age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + this.name + '\'' +
                    ", age=" + this.age +
                    '}';
        }
    }
    
    • 在module1中新建module-info.java
    //定义模块信息
    module module1 {
        //导出包:对外输出com.sunxiaping.bean包
        exports com.sunxiaping.bean;
    }
    

    在module1中定义模块信息

    • 在jdk9工程下新建module2模块,其pom.xml如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>jdk9</artifactId>
            <groupId>com.sunxiaping</groupId>
            <version>1.0</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>module2</artifactId>
     
    </project>
    
    • 在module2中新建module-info.java
    module module2 {
        requires module1;
    }
    

    创建输入模块信息

    • 在module2中添加module1的依赖,其pom.xml如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>jdk9</artifactId>
            <groupId>com.sunxiaping</groupId>
            <version>1.0</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>module2</artifactId>
        <dependencies>
            <dependency>
                <groupId>com.sunxiaping</groupId>
                <artifactId>module1</artifactId>
                <version>1.0</version>
                <scope>compile</scope>
            </dependency>
        </dependencies>
    </project>
    
    • 在module2中新建ModuleTest.java
    package com.sunxiaping.java;
    
    
    import com.sunxiaping.bean.Person;
    
    public class ModuleTest {
        public static void main(String[] args) {
            Person person = new Person("张三", 23);
    
            System.out.println("person = " + person);
        }
    }
    

2 交互式编程:JShell工具

2.1 什么是交互式编程?

  • 像Python和Scala之类的语言早就有了交互式编程环境REPL,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获取对程序的反馈。
  • 而JDK9之前的版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。

2.2 交互式编程的作用

  • 即时反馈。

2.3 应用示例

  • 调用JShell:
# 前提需要配置JDK9的环境变量
jshell

调出JShell

  • 获取帮助:
/help intro

JShell获取帮助

  • 基础使用:
//直接定义变量和方法,并使用定义的变量和方法
System.out.println("aa");

int a = 10;
int b= 20;
int c = a + b;

System.out.println(c);


public int add(int a,int b){
    return a + b;
}

int k = add(3,4);
System.out.println(k);

JShell的基本使用

  • 导入指定的包:
import java.util.*;

JShell导入指定的包

  • 默认已经导入如下的所有包:
/imports

JShell默认已经导入的包

  • 列出当前session里面的所有有效代码片段:
/list

JShell列出当前session里面的所有有效代码片段

  • 列出当前session下所有创建过的变量:
/var

JShell列出当前session下所有创建过的变量

  • 列出当前session下所有创建过的方法:
/methods

JShell列出当前session下所有创建过的方法

  • 使用外部编辑器来编写Java代码:
/edit

JShell使用外部编辑器来编写Java代码

  • 从外部加载源代码(需要在指定的目录下提供java文件):
/open 指定目录下的Java文件绝对路径

JShell从外部加载源代码

  • 没有受检异常(编译时异常):JShell在后台为我们隐藏了

JShell没有受检异常

  • 退出:
/exit

退出JShell

3 多版本兼容jar

3.1 概述

  • 当一个新版本的Java出现的时候,你的库用户要花费数年时间才会切换到新的版本。这就意味着库必须要兼容你要支持的最老的Java版本(通常是Java6或Java7)。这实际上意味着未来的很长一段时间,你都不能在库中运用Java9所提供的新特性。

  • 幸运的是,多版本兼容jar功能让你创建仅在特定版本的Java环境中运行库程序选择使用的class版本。

  • 举例:在下述场景中,root.jar可以在Java9中使用,不过A或B类使用的不是顶层的root.A或root.B这两个class,而是处在“META-INF/versions/9”下面的这两个。这是特别为Java9准备的class版本,可以运用Java9所提供的特性和库。同时,在早期的Java版本中使用这个jar也是能运行的,因为较老版本的Java只会看到顶层的A类或B类。

jar root
    - A.class
    - B.class
    - C.class
    - D.class
    - META-INF
      - versions 
        - 9
          - A.class
          - B.class    

3.2 应用示例

  • 提供必要的类:

    • 在src/main/java下新建Generator和Application:
    package com.sunxiaping;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * @author 许大仙
     * @version 1.0
     * @since 2020-11-30 15:29
     */
    public class Generator {
    
        public Set<String> createStrings(){
            Set<String> set = new HashSet<String>();
            set.add("java");
            set.add("8");
            return set;
        }
    }
    
    package com.sunxiaping;
    
    /**
     * @author 许大仙
     * @version 1.0
     * @since 2020-11-30 15:31
     */
    public class Application {
        public static void main(String[] args) {
            com.sunxiaping.Generator generator = new com.sunxiaping.Generator();
            System.out.println("generator = " + generator.createStrings());
        }
    }
    
    
    • 在src/main/java-9下新建Generator:
    package com.sunxiaping;
    
    import java.util.Set;
    
    /**
     * @author 许大仙
     * @version 1.0
     * @since 2020-11-30 15:32
     */
    public class Generator {
        public Set<String> createStrings(){
            return Set.of("Java", "9");
        }
    }
    
  • 打包:

javac -d build --release 8 src/mian/java/com/sunxiaping/*.java
javac -d build --release 9 src/mian/java-9/com/sunxiaping/*.java
jar --create --main-class=Application --file multijar.jar -C build . --release 9 -c build9 .

4 接口中定义私有方法

4.1 概述

  • Java8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
  • 在Java9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private了,此时方法将不会成为你对外暴露的API的一部分。

4.2 应用示例

  • 示例:
package com.sunxiaping;

/**
 * JDK9支持私有方法
 *
 * @author 许大仙
 * @version 1.0
 * @since 2020-11-30 16:11
 */
public interface DemoInterface {

   String name = "静态常量";

    /**
     * 抽象方法
     */
   void method();

    /**
     * 默认方法
     */
   default void method2(){
       method4();
   }

    /**
     * 静态方法
     */
   static void method3(){
       System.out.println("静态方法,访问修饰符是public");
   }

    /**
     * 私有方法
     */
   private void method4(){
       System.out.println("这是一个私有方法");
   }

    /**
     * 私有静态方法
     */
   private static void method5(){
       System.out.println("私有静态方法");
   }
}

5 钻石操作符的升级

5.1 概述

  • JDK9允许匿名内部类和<>一起使用。

5.2 应用示例

  • 示例:
package com.sunxiaping;

import java.util.ArrayList;
import java.util.List;

/**
 * 钻石标识符的升级:JDK允许钻石标识符和匿名内部类一起使用
 *
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 08:39
 */
public class Demo {
    public static void main(String[] args) {
        //创建继承于ArrayList的匿名内部类
        List<String> list = new ArrayList<>() {
        };
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");

        list.forEach(System.out::println);
    }
}

6 异常处理升级

  • JDK8之前的写法:
package com.sunxiaping;


import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 08:51
 */
public class TryDemo {

    /**
     * JDK8之前的写法
     */
    public static void main(String[] args) {
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        try {
            System.out.println("请输入:");
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            String str;
            while (null != (str = bufferedReader.readLine())) {
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStreamReader != null) {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • JDK8的写法:
package com.sunxiaping;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 08:51
 */
public class TryDemo {

    /**
     * JDK8的写法:也被称为try-with-resources
     * 要求资源对象的实例化,必须放在try的一对()内
     */
    public static void main(String[] args) {
        System.out.println("请输入:");
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {
            String str;
            while (null != (str = bufferedReader.readLine())) {
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • JDK9的写法:
package com.sunxiaping;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 08:51
 */
public class TryDemo {

    /**
     * JDK9:可以在try()中调用已经实例化的资源对象
     */
    public static void main(String[] args) {
        System.out.println("请输入:");
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        try (bufferedReader) {
            String str;
            while (null != (str = bufferedReader.readLine())) {
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

7 下划线命名标识符的限制

  • 在JDK8之前,标识符可以独立使用"_"来命名:
package com.sunxiaping;

import org.junit.Test;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 10:08
 */
public class VariableDemo {

    /**
     * JDK8中,标识符是可以使用"_"来命名的
     */
    @Test
    public void test() {
        String _ = "hello";
        System.out.println("_ = " + _);
    }

}
  • JDK9中规定"_"不可以单独命名标识符,如果使用,会报错:
package com.sunxiaping;

import org.junit.Test;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 10:08
 */
public class VariableDemo {

    /**
     * JDK9中规定"_"不可以单独命名标识符,如果使用,会报错
     */
    @Test
    public void test() {
        String _ = "world";
        System.out.println("_ = " + _);
    }

}

正常情况下,脑残才会使用"_"来命名标识符。

8 String底层存储结构的变化

  • String不再用char[]来存储,而是改成了byte[]加上编码标记,节约了一些空间。

9 创建只读集合

9.1 概述

  • 要创建一个只读的、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。
package com.sunxiaping;

import org.junit.Test;

import java.util.*;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 17:29
 */
public class CollectionMapDemo {

    /**
     * 创建一个只读特点的集合
     */
    @Test
    public void test() {
        List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");

        List<String> newList = Collections.unmodifiableList(list);

//        newList.add("ee");   //不能执行,会抛出异常

        newList.forEach(System.out::println);
    }

    /**
     * 创建一个只读特点的集合
     */
    @Test
    public void test2() {
        List<String> list = Collections.unmodifiableList(Arrays.asList("aa", "bb", "cc"));

//        list.add("ee");   //不能执行,会抛出异常
        list.forEach(System.out::println);

        Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("aa", "bb", "cc")));

//        set.add("ee"); //不能执行,会抛出异常
        set.forEach(System.out::println);

        Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {
            {
                put("aa", "aa");
                put("bb", "bb");
                put("cc", "cc");
            }
        });

//        map.put("ee", "ee"); //不能执行,会抛出异常

        map.forEach((k, v) -> System.out.println(k + ":" + v));

    }
    
}
  • JDK9因此引入了方便的方法,使得类似的事情更容易表达。

JDK9的快速创建只读集合

  • 调用集合中的静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能可英语Set和List,也可用于Map的类似形式。此时得到的集合,是不可变得,继续添加元素到这些集合中,会抛出异常。
  • 由于Java8中接口方法的实现,可以直接在List、Set和Map的接口内定义这些方法,便于调用。

9.2 应用示例

  • 示例:
package com.sunxiaping;

import org.junit.Test;

import java.util.*;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 17:29
 */
public class CollectionMapDemo {

    /**
     * 创建一个只读特点的集合
     */
    @Test
    public void test() {
        List<String> list = List.of("aa", "bb", "cc");

//        list.add("ee"); //不能执行,会抛出异常

        list.forEach(System.out::println);
    }
}

10 增强的Stream API

10.1 概述

  • Java的Stream API是Java标准库最好的改进之一,让开发者能够快速运算,从而能够有效的利用数据并行计算。Java8提供的Stream能够利用多核架构实现声明式的数据处理。
  • 在Java9中,Stream API变得更好,Stream接口中添加了4个新的方法:dropWhile、takeWhile、ofNullable,还有iterate方法的新重载方法,可以让你提供一个Predicate来指定什么时候结束迭代。
  • 除了对Stream本身的扩展,Optional和Stream之间的结合也得到了改进。现在可以通过Optional的新方法stream()将一个Optional对象转换为一个Stream对象。

10.2 应用示例

  • 示例:
package com.sunxiaping;

import org.junit.Test;

import java.util.List;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 19:05
 */
public class StreamAPITest {

    /**
     * takeWhile:用于从Stream中获取一部分数据,接收一个Predicate来进行选择。在有序的Stream中,takeWhile返回从开头开始的尽量多的元素。
     */
    @Test
    public void test() {
        List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);

        list.stream().takeWhile((x) -> x <= 50).forEach(System.out::println); //45 43 -9

        list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        list.stream().takeWhile((x) -> x <= 5).forEach(System.out::println); //1 2 3 4 5
    }

    /**
     * dropWhile:和takeWhile相反,返回剩余的元素
     */
    @Test
    public void test2() {
        List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);

        list.stream().dropWhile((x) -> x <= 50).forEach(System.out::println); //99 67 88 22 49

        list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        list.stream().dropWhile((x) -> x <= 5).forEach(System.out::println); //6 7 8 9 10
    }

    /**
     * ofNullable:Java8中的Stream不能完全为null,否则会报空指针异常。而Java9中的ofNullable方法允许我们创建一个氮元素的Stream,可以包含一个非空元素,也可以创建一个空的Stream
     */
    @Test
    public void test3() {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, null);

        stream1.forEach(System.out::println);

        System.out.println("------------------");

        //如果只有单个元素,此元素不能为null,否则会抛出NullPointerException
//        Stream<Object> stream2 = Stream.of(null);
//
//        stream2.forEach(System.out::println); //NullPointerException

        //Java9新增ofNullable,允许单元素为null
        Stream<Object> stream = Stream.ofNullable(null);
        stream.forEach(System.out::println);
    }


    /**
     * iterate
     */
    @Test
    public void test4() {
        Stream.iterate(0, x -> x + 1).limit(5).forEach(System.out::println);
        
        Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
    }
}
  • 示例:
package com.sunxiaping;

import org.junit.Test;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 19:25
 */
public class OptionalTest {

    @Test
    public void test() {

        List<String> list = List.of("aa", "bb", "cc", "dd");

        Stream<List<String>> stream = Optional.ofNullable(list).stream();

        stream.forEach(System.out::println);

        Stream<String> stream2 = Optional.ofNullable(list).stream().flatMap(x -> x.stream());

        stream2.forEach(System.out::println);
    }
}

11 多分辨率图像API

11.1 概述

  • 在Mac上,JDK已经支持视网膜显示,但是在Linux和Windows上,它并没有。在那里,Java程序在当前的高分辨率的屏幕上可能看起来非常小,不能使用它们。这是因为像素用于这些系统的大小计算(无论像素实际有多大)。毕竟,高分辨率显示器的有效部分是像素非常小。
  • JEP 263以这样的方式扩展了JDK,即 Windows 和 Linux 也考虑到像素的大小。为此,使用比现在更多的现代 API: Direct2D for Windows和 GTK +, 而不是 Xlib for Linux。图形,窗口和文本由此自动缩放。
  • JEP 251 还提供处理多分辨率图像的能力,即包含不同分辨率的相同图像的文件。根据相应屏幕的 DPI 度量,然后以适当的分辨率使用图像。

11.2 产生背景

  • 新的 API 定义在 java.awt.image 包下。
  • 将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体。
  • 获取这个图像的所有变体。
  • 获取特定分辨率的图像变体-表示一张已知分辨率单位为 DPI 的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。
  • 基于当前屏幕分辨率大小和运用的图像转换算法,java.awt.Graphics 类可以从接口 MultiResolutionImage 获取所需的
    变体。
  • MultiResolutionImage 的基础实现是java.awt.image.BaseMultiResolutionImage。

12 全新的HTTP客户端API

12.1 概述

  • HTTP,用于传输网页的协议,早在 1997 年就被采用在目前的 1.1版本中。直到 2015 年, HTTP2 才成为标准。
  • HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。 HTTP/1.1 依赖于请求/响应周期。 HTTP/2 允许服务器“push”数据:它可以发送比客户端请求更多的数据。 这使得它可以优先处理并发送对于首先加载网页至关重要的数据。
  • Java 9 中有新的方式来处理 HTTP 调用。 它提供了一个新的 HTTP客户 端( HttpClient ),它 将 替 代 仅 适 用 于 blocking 模 式 的HttpURLConnection(HttpURLConnection是在HTTP 1.0的时代创建的,
    并使用了协议无关的方法),并提供对 WebSocket 和 HTTP/2 的支持。
  • 此外, HTTP 客户端还提供 API 来处理 HTTP/2 的特性,比如流和服务器推送等功能。
  • 全新的 HTTP 客户端 API 可以从 jdk.incubator.httpclient 模块中获取。因为在默认情况下,这个模块是不能根据 classpath 获取的,需要使用add modules 命令选项配置这个模块,将这个模块添加到 classpath
    中。
  • 全新的 HTTP 客户端 API 可以从 jdk.incubator.httpclient 模块中获取。因为在默认情况下,这个模块是不能根据 classpath 获取的,需要使用 add modules 命令选项配置这个模块,将这个模块添加到 classpath
    中。

12.2 应用示例

  • 示例:

  • module-info.java

/**
   * @author 许大仙
   * @version 1.0
   * @since 2020-12-01 20:07
   */
module jdk9 {
    requires jdk.incubator.httpclient;
    requires junit;
}
  • HttpClientTest.java
package com.sunxiaping;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;

import java.io.IOException;
import java.net.URI;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2020-12-01 19:55
 */
public class HttpClientTest {
  public static void main(String[] args) {
      HttpClient httpClient = HttpClient.newHttpClient();
      HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.baidu.com")).GET().build();
      HttpResponse<String> response = null;
      try {
          response = httpClient.send(request, HttpResponse.BodyHandler.asString());
      } catch (IOException e) {
          e.printStackTrace();
      } catch (InterruptedException e) {
          e.printStackTrace();
      }

      System.out.println(response.statusCode());
      System.out.println(response.version().name());
      System.out.println(response.body());
  }
}

13 Deprecated的相关API

  • Java9废弃或者移除了几个不常用的功能。其中最主要的是Applet API,现在是标记为废弃的。
  • 随着对安全要求的提高,主流浏览器已经取消对Java浏览器插件的支持。HTML5的出现也进一步加速了它的消亡。开发者现在可以使用Java Web Start这样的技术来代替Applet,它可以实现从浏览器启动应用程序或者安装应用程序。
  • 同时,appletviewer工具也被标记为废弃。

14 智能Java编译工具

  • 智能Java编译工具(sjavac)的第一个阶段始于JEP139这个项目,用于在多核处理器情况下提升JDK的编译速度。如今,这个项目已经进入第二阶段,即JEP199,其目的是改进Java编译工具,并取代目前JDK编译工具javac,继而成为Java环境默认的通用的智能编译工具。
  • JDK9还更新了javac编译器以便能够将Java9代码编译运行在低版本的Java中。

15 统一的JVM日志系统

  • 日志是解决问题的唯一有效途径:曾经很难知道导致JVM性能问题和导致JVM崩溃的根本原因。不同的JVM日志的碎片化和日志选项(例如:JVM组件对于日志使用的是不同的机制和规则),这使得JVM难以进行调试。
  • 解决该问题最佳方法:对所有的JVM组件引入一个单一的系统,这些JVM组件支持细粒度的和易配置的JVM日志。

16 javadoc的HTML5支持

  • Java8:生成的Java帮助文档是在HTML4中,而HTML4已经是很早的标准了。
  • Java9:javadoc的输出,现在是兼容HTML5的标准。

17 JavaScript引擎升级:Nashorn

  • Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的Javascript 运行时。 Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为Java 提供一个 Javascript 引擎。
  • JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的API。 这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类,就能够分析 ECMAScript 代码。

18 Java动态编译器

18.1 产生背景

  • Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到Java,很多 C/C++ 开发者还是会不屑地评价为启动慢,吃内存。
  • 简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机可以理解的二进制代码,而不是真正的可执行的本地代码, 需要 Java虚拟机进行解释和编译,这带来了额外的开销。

18.2 使用说明

  • JIT(Just-in-time)编译器可以在运行时将热点编译成本地代码,速度很快。但是 Java 项目现在变得很大很复杂,因此 JIT 编译器需要花费较长时间才能热身完,而且有些 Java 方法还没法编译,性能方面也会下降。 AoT 编译就是为了解决这些问题而生的。

  • 在 JDK 9 中, AOT(JEP 295: Ahead-of-Time Compilation)作为实验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换成类似类库一样的文件。虽然仍处于试验阶段,但这个功能使得 Java应用在被虚拟机启动之前能够先将 Java 类编译为原生代码。此功能旨在改进小型和大型应用程序的启动时间,同时对峰值性能的影响很小。

  • 但是 Java 技术供应商Excelsior的营销总监Dmitry Leskov担心 AoT 编译技术不够成熟,希望 Oracle 能够等到 Java 10 时有个更稳定版本才发布。

  • 另外 JVMCI(JEP 243: Java-Level JVM Compiler Interface)等特性,对于整个编程语言的发展,可能都具有非常重要的意义,虽然未必引起了广泛关注。目前 Graal Core API 已经被集成进入 Java 9,虽然还只是初始一小步,但是完全用 Java 语言来实现的可靠的、高性能的动态编译器,似乎不再是遥不可及,这是 Java 虚拟机开发工程师的福音。

  • 与此同时,随着 Truffle 框架和 Substrate VM 的发展,已经让个别信心满满的工程师高呼“One VM to Rule Them All!”, 也许就在不远的将来 Ploygot 以一种另类的方式成为现实。

19 总结和展望

19.1 在Java9中看不到什么?

19.1.1 一个标准化和轻量级的JSON API

  • 一个标准化和轻量级的 JSON API 被许多 java 开发人员所青睐。但是由于资金问题无法在 Java 9 中见到,但并不会削减掉。 Java 平台首席架构师 Mark Reinhold 在 JDK 9 邮件列中说:“这个 JEP 将是平台上的一个有用的补充,但是在计划中,它并不像 Oracle 资助的其他功能那么重要,可能会重新考虑 JDK 10 或更高版本中实现。”

19.1.2 新的货币API

  • 对许多应用而言货币价值都是一个关键的特性,但 JDK 对此却几乎没有任何支持。严格来讲,现有的 java.util.Currency 类只是代表了当前 ISO 4217 货币的一个数据结构,但并没有关联的值或者自定义货
    币。 JDK 对货币的运算及转换也没有内建的支持,更别说有一个能够代表货币值的标准类型了。
  • 此前, Oracle 公布的 JSR 354 定义了一套新的 Java 货币 API:JavaMoney,计划会在 Java 9 中正式引入。但是目前没有出现在 JDK 9中。
  • 不过,如果你用的是 Maven 的话,可以做如下的添加,即可使用相关的 API 处理货币:
<dependency>
	<groupId>org.javamoney</groupId>
	<artifactId>moneta</artifactId>
	<version>0.9</version>
</dependency>

19.2 展望

  • 随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java 技术的发展方向。 传统的大型企业或互联网应用,正在被云端、 容器化应用、模块化的微服务甚至是函数(FaaS,Function-as-a-Service) 所替代。
  • Java虽然标榜面向对象编程,却毫不顾忌的加入面向接口编程思想,又扯出匿名对象之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有余。 Java对标C/C++,以抛掉内存管理为卖点,却又陷入了JVM优化的噩梦。选择比努力更重要,选择Java的人更需要对它有更清晰的认识。
  • Java 需要在新的计算场景下,改进开发效率。 这话说的有点笼统,我谈一些自己的体会, Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。
posted @ 2020-12-02 10:11  许大仙  阅读(520)  评论(0编辑  收藏  举报