1 模块化系统

1.1 JDK9之前没有解决的问题

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

1.2 模块化的设计理念

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

1.3 模块化的实现目标

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

1.4 应用示例

  • 应用示例结构化图:

模块化应用示例.png

  • 应用示例步骤:

    • 新建jdk9工程,其pom如下:

      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      3. xmlns="http://maven.apache.org/POM/4.0.0"
      4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      5. <modelVersion>4.0.0</modelVersion>
      6. <groupId>com.sunxiaping</groupId>
      7. <artifactId>jdk9</artifactId>
      8. <packaging>pom</packaging>
      9. <version>1.0</version>
      10. <modules>
      11. <module>module1</module>
      12. <module>module2</module>
      13. </modules>
      14. <build>
      15. <plugins>
      16. <!-- 设置JDK 9版本 -->
      17. <plugin>
      18. <groupId>org.apache.maven.plugins</groupId>
      19. <artifactId>maven-compiler-plugin</artifactId>
      20. <configuration>
      21. <source>9</source>
      22. <target>9</target>
      23. <encoding>UTF-8</encoding>
      24. <showWarnings>true</showWarnings>
      25. </configuration>
      26. </plugin>
      27. </plugins>
      28. </build>
      29. </project>
    • 在jdk9工程下新建module1模块,其pom.xml如下:

      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <project xmlns="http://maven.apache.org/POM/4.0.0"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      5. <parent>
      6. <artifactId>jdk9</artifactId>
      7. <groupId>com.sunxiaping</groupId>
      8. <version>1.0</version>
      9. </parent>
      10. <modelVersion>4.0.0</modelVersion>
      11. <artifactId>module1</artifactId>
      12. </project>
    • 在module1模块下新建Person.java ```java package com.sunxiaping.bean;

public class Person { private String name;

  1. private Integer age;
  2. public Person() {
  3. }
  4. public Person(String name, Integer age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public String getName() {
  9. return this.name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public Integer getAge() {
  15. return this.age;
  16. }
  17. public void setAge(Integer age) {
  18. this.age = age;
  19. }
  20. @Override
  21. public String toString() {
  22. return "Person{" +
  23. "name='" + this.name + '\'' +
  24. ", age=" + this.age +
  25. '}';
  26. }

}

  1. - module1中新建module-info.java
  2. ```java
  3. //定义模块信息
  4. module module1 {
  5. //导出包:对外输出com.sunxiaping.bean包
  6. exports com.sunxiaping.bean;
  7. }

在module1中定义模块信息.png

  • 在jdk9工程下新建module2模块,其pom.xml如下: ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0

    1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    jdk9 com.sunxiaping 1.0 4.0.0 module2

  1. - module2中新建module-info.java
  2. ```java
  3. module module2 {
  4. requires module1;
  5. }

创建输入模块信息.png

  • 在module2中添加module1的依赖,其pom.xml如下:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xmlns="http://maven.apache.org/POM/4.0.0"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <parent>
    6. <artifactId>jdk9</artifactId>
    7. <groupId>com.sunxiaping</groupId>
    8. <version>1.0</version>
    9. </parent>
    10. <modelVersion>4.0.0</modelVersion>
    11. <artifactId>module2</artifactId>
    12. <dependencies>
    13. <dependency>
    14. <groupId>com.sunxiaping</groupId>
    15. <artifactId>module1</artifactId>
    16. <version>1.0</version>
    17. <scope>compile</scope>
    18. </dependency>
    19. </dependencies>
    20. </project>
  • 在module2中新建ModuleTest.java ```java package com.sunxiaping.java;

import com.sunxiaping.bean.Person;

public class ModuleTest { public static void main(String[] args) { Person person = new Person(“张三”, 23);

  1. System.out.println("person = " + person);
  2. }

}

  1. <a name="0b904450"></a>
  2. # 2 交互式编程:JShell工具
  3. <a name="663a898c"></a>
  4. ## 2.1 什么是交互式编程?
  5. - 像Python和Scala之类的语言早就有了交互式编程环境REPL,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获取对程序的反馈。
  6. - 而JDK9之前的版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。
  7. <a name="2cabffea"></a>
  8. ## 2.2 交互式编程的作用
  9. - 即时反馈。
  10. <a name="18309c2c"></a>
  11. ## 2.3 应用示例
  12. - 调用JShell:
  13. ```shell
  14. # 前提需要配置JDK9的环境变量
  15. jshell

调出JShell.png

  • 获取帮助:
  1. /help intro

JShell获取帮助.png

  • 基础使用:
  1. //直接定义变量和方法,并使用定义的变量和方法
  2. System.out.println("aa");
  3. int a = 10;
  4. int b= 20;
  5. int c = a + b;
  6. System.out.println(c);
  7. public int add(int a,int b){
  8. return a + b;
  9. }
  10. int k = add(3,4);
  11. System.out.println(k);

JShell的基本使用.png

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

JShell导入指定的包.png

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

JShell默认已经导入的包.png

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

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

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

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

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

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

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

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

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

JShell从外部加载源代码.png

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

JShell没有受检异常.png

  • 退出:
  1. /exit

退出JShell.gif

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类。
  1. jar root
  2. - A.class
  3. - B.class
  4. - C.class
  5. - D.class
  6. - META-INF
  7. - versions
  8. - 9
  9. - A.class
  10. - B.class

3.2 应用示例

  • 提供必要的类:
    • 在src/main/java下新建Generator和Application: ```java 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 createStrings(){

    1. Set<String> set = new HashSet<String>();
    2. set.add("java");
    3. set.add("8");
    4. return set;

    } } java package com.sunxiaping;

/**

  • @author 许大仙
  • @version 1.0
  • @since 2020-11-30 15:31 */ public class Application { public static void main(String[] args) {

    1. com.sunxiaping.Generator generator = new com.sunxiaping.Generator();
    2. System.out.println("generator = " + generator.createStrings());

    } } ```

    • 在src/main/java-9下新建Generator: ```java package com.sunxiaping;

import java.util.Set;

/**

  • @author 许大仙
  • @version 1.0
  • @since 2020-11-30 15:32 */ public class Generator { public Set createStrings(){
    1. return Set.of("Java", "9");
    } } ```
  • 打包:
  1. javac -d build --release 8 src/mian/java/com/sunxiaping/*.java
  1. javac -d build --release 9 src/mian/java-9/com/sunxiaping/*.java
  1. jar --create --main-class=Application --file multijar.jar -C build . --release 9 -c build9 .

4 接口中定义私有方法

4.1 概述

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

4.2 应用示例

  • 示例:
  1. package com.sunxiaping;
  2. /**
  3. * JDK9支持私有方法
  4. *
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-11-30 16:11
  8. */
  9. public interface DemoInterface {
  10. String name = "静态常量";
  11. /**
  12. * 抽象方法
  13. */
  14. void method();
  15. /**
  16. * 默认方法
  17. */
  18. default void method2(){
  19. method4();
  20. }
  21. /**
  22. * 静态方法
  23. */
  24. static void method3(){
  25. System.out.println("静态方法,访问修饰符是public");
  26. }
  27. /**
  28. * 私有方法
  29. */
  30. private void method4(){
  31. System.out.println("这是一个私有方法");
  32. }
  33. /**
  34. * 私有静态方法
  35. */
  36. private static void method5(){
  37. System.out.println("私有静态方法");
  38. }
  39. }

5 钻石操作符的升级

5.1 概述

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

5.2 应用示例

  • 示例:
  1. package com.sunxiaping;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * 钻石标识符的升级:JDK允许钻石标识符和匿名内部类一起使用
  6. *
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2020-12-01 08:39
  10. */
  11. public class Demo {
  12. public static void main(String[] args) {
  13. //创建继承于ArrayList的匿名内部类
  14. List<String> list = new ArrayList<>() {
  15. };
  16. list.add("aa");
  17. list.add("bb");
  18. list.add("cc");
  19. list.add("dd");
  20. list.forEach(System.out::println);
  21. }
  22. }

6 异常处理升级

  • JDK8之前的写法:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. import java.util.Scanner;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2020-12-01 08:51
  11. */
  12. public class TryDemo {
  13. /**
  14. * JDK8之前的写法
  15. */
  16. public static void main(String[] args) {
  17. InputStreamReader inputStreamReader = null;
  18. BufferedReader bufferedReader = null;
  19. try {
  20. System.out.println("请输入:");
  21. bufferedReader = new BufferedReader(new InputStreamReader(System.in));
  22. String str;
  23. while (null != (str = bufferedReader.readLine())) {
  24. System.out.println(str);
  25. }
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. } finally {
  29. if (inputStreamReader != null) {
  30. try {
  31. inputStreamReader.close();
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. if (bufferedReader != null) {
  37. try {
  38. bufferedReader.close();
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }
  44. }
  45. }
  • JDK8的写法:
  1. package com.sunxiaping;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-01 08:51
  8. */
  9. public class TryDemo {
  10. /**
  11. * JDK8的写法:也被称为try-with-resources
  12. * 要求资源对象的实例化,必须放在try的一对()内
  13. */
  14. public static void main(String[] args) {
  15. System.out.println("请输入:");
  16. try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {
  17. String str;
  18. while (null != (str = bufferedReader.readLine())) {
  19. System.out.println(str);
  20. }
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  • JDK9的写法:
  1. package com.sunxiaping;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-01 08:51
  8. */
  9. public class TryDemo {
  10. /**
  11. * JDK9:可以在try()中调用已经实例化的资源对象
  12. */
  13. public static void main(String[] args) {
  14. System.out.println("请输入:");
  15. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
  16. try (bufferedReader) {
  17. String str;
  18. while (null != (str = bufferedReader.readLine())) {
  19. System.out.println(str);
  20. }
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

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

  • 在JDK8之前,标识符可以独立使用”_”来命名:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2020-12-01 10:08
  7. */
  8. public class VariableDemo {
  9. /**
  10. * JDK8中,标识符是可以使用"_"来命名的
  11. */
  12. @Test
  13. public void test() {
  14. String _ = "hello";
  15. System.out.println("_ = " + _);
  16. }
  17. }
  • JDK9中规定”_”不可以单独命名标识符,如果使用,会报错:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2020-12-01 10:08
  7. */
  8. public class VariableDemo {
  9. /**
  10. * JDK9中规定"_"不可以单独命名标识符,如果使用,会报错
  11. */
  12. @Test
  13. public void test() {
  14. String _ = "world";
  15. System.out.println("_ = " + _);
  16. }
  17. }

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

8 String底层存储结构的变化

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

9 创建只读集合

9.1 概述

  • 要创建一个只读的、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.util.*;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-01 17:29
  8. */
  9. public class CollectionMapDemo {
  10. /**
  11. * 创建一个只读特点的集合
  12. */
  13. @Test
  14. public void test() {
  15. List<String> list = new ArrayList<>();
  16. list.add("aa");
  17. list.add("bb");
  18. list.add("cc");
  19. List<String> newList = Collections.unmodifiableList(list);
  20. // newList.add("ee"); //不能执行,会抛出异常
  21. newList.forEach(System.out::println);
  22. }
  23. /**
  24. * 创建一个只读特点的集合
  25. */
  26. @Test
  27. public void test2() {
  28. List<String> list = Collections.unmodifiableList(Arrays.asList("aa", "bb", "cc"));
  29. // list.add("ee"); //不能执行,会抛出异常
  30. list.forEach(System.out::println);
  31. Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("aa", "bb", "cc")));
  32. // set.add("ee"); //不能执行,会抛出异常
  33. set.forEach(System.out::println);
  34. Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {
  35. {
  36. put("aa", "aa");
  37. put("bb", "bb");
  38. put("cc", "cc");
  39. }
  40. });
  41. // map.put("ee", "ee"); //不能执行,会抛出异常
  42. map.forEach((k, v) -> System.out.println(k + ":" + v));
  43. }
  44. }
  • JDK9因此引入了方便的方法,使得类似的事情更容易表达。

JDK9的快速创建只读集合.png

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

9.2 应用示例

  • 示例:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.util.*;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-01 17:29
  8. */
  9. public class CollectionMapDemo {
  10. /**
  11. * 创建一个只读特点的集合
  12. */
  13. @Test
  14. public void test() {
  15. List<String> list = List.of("aa", "bb", "cc");
  16. // list.add("ee"); //不能执行,会抛出异常
  17. list.forEach(System.out::println);
  18. }
  19. }

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 应用示例

  • 示例:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.util.List;
  4. import java.util.stream.Stream;
  5. /**
  6. * @author 许大仙
  7. * @version 1.0
  8. * @since 2020-12-01 19:05
  9. */
  10. public class StreamAPITest {
  11. /**
  12. * takeWhile:用于从Stream中获取一部分数据,接收一个Predicate来进行选择。在有序的Stream中,takeWhile返回从开头开始的尽量多的元素。
  13. */
  14. @Test
  15. public void test() {
  16. List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);
  17. list.stream().takeWhile((x) -> x <= 50).forEach(System.out::println); //45 43 -9
  18. list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  19. list.stream().takeWhile((x) -> x <= 5).forEach(System.out::println); //1 2 3 4 5
  20. }
  21. /**
  22. * dropWhile:和takeWhile相反,返回剩余的元素
  23. */
  24. @Test
  25. public void test2() {
  26. List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);
  27. list.stream().dropWhile((x) -> x <= 50).forEach(System.out::println); //99 67 88 22 49
  28. list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  29. list.stream().dropWhile((x) -> x <= 5).forEach(System.out::println); //6 7 8 9 10
  30. }
  31. /**
  32. * ofNullable:Java8中的Stream不能完全为null,否则会报空指针异常。而Java9中的ofNullable方法允许我们创建一个氮元素的Stream,可以包含一个非空元素,也可以创建一个空的Stream
  33. */
  34. @Test
  35. public void test3() {
  36. Stream<Integer> stream1 = Stream.of(1, 2, 3, null);
  37. stream1.forEach(System.out::println);
  38. System.out.println("------------------");
  39. //如果只有单个元素,此元素不能为null,否则会抛出NullPointerException
  40. // Stream<Object> stream2 = Stream.of(null);
  41. //
  42. // stream2.forEach(System.out::println); //NullPointerException
  43. //Java9新增ofNullable,允许单元素为null
  44. Stream<Object> stream = Stream.ofNullable(null);
  45. stream.forEach(System.out::println);
  46. }
  47. /**
  48. * iterate
  49. */
  50. @Test
  51. public void test4() {
  52. Stream.iterate(0, x -> x + 1).limit(5).forEach(System.out::println);
  53. Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
  54. }
  55. }
  • 示例:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.util.List;
  4. import java.util.Optional;
  5. import java.util.stream.Stream;
  6. /**
  7. * @author 许大仙
  8. * @version 1.0
  9. * @since 2020-12-01 19:25
  10. */
  11. public class OptionalTest {
  12. @Test
  13. public void test() {
  14. List<String> list = List.of("aa", "bb", "cc", "dd");
  15. Stream<List<String>> stream = Optional.ofNullable(list).stream();
  16. stream.forEach(System.out::println);
  17. Stream<String> stream2 = Optional.ofNullable(list).stream().flatMap(x -> x.stream());
  18. stream2.forEach(System.out::println);
  19. }
  20. }

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
  1. /**
  2. * @author 许大仙
  3. * @version 1.0
  4. * @since 2020-12-01 20:07
  5. */
  6. module jdk9 {
  7. requires jdk.incubator.httpclient;
  8. requires junit;
  9. }
  • HttpClientTest.java
  1. package com.sunxiaping;
  2. import jdk.incubator.http.HttpClient;
  3. import jdk.incubator.http.HttpRequest;
  4. import jdk.incubator.http.HttpResponse;
  5. import java.io.IOException;
  6. import java.net.URI;
  7. /**
  8. * @author 许大仙
  9. * @version 1.0
  10. * @since 2020-12-01 19:55
  11. */
  12. public class HttpClientTest {
  13. public static void main(String[] args) {
  14. HttpClient httpClient = HttpClient.newHttpClient();
  15. HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.baidu.com")).GET().build();
  16. HttpResponse<String> response = null;
  17. try {
  18. response = httpClient.send(request, HttpResponse.BodyHandler.asString());
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. System.out.println(response.statusCode());
  25. System.out.println(response.version().name());
  26. System.out.println(response.body());
  27. }
  28. }

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 处理货币:
  1. <dependency>
  2. <groupId>org.javamoney</groupId>
  3. <artifactId>moneta</artifactId>
  4. <version>0.9</version>
  5. </dependency>

19.2 展望

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