目录结构:

  • bin:该目录包含所有的命令
  • conf:包含用户可以编辑的配置文件,例如以前用的jrd\lib目录中的.properties  .policy文件
  • include:包含一些编译本地代码时使用的C/C++头文件
  • jmods:包含JMOD格式的平台模块,创建自定义运行映射时需要它。
  • legal:包含法律声明。
  • lib:包含非Window平台上动态链接的本地库,其子目录和文件不应由开发人员直接编辑和使用。
注:JDK9目录中不再有jre子目录

模块化系统:

jdk被分成一组模块,可以在编译时,运行时或构建时进行组合,减少内存开销,只需必要的模块,而不是全部,可以简化各种类库和大型应用的开发和维护。

如上图所示:
modela的module-info.java
module modela{
    exports com.cnblogs.bean;
}
modelb的module-info.java
module modelb{
    requires modela;
}
modelb的Test类
public class Test{
    public static void main(String[] args){
        Person person = new Person("jordan",23);
        System.out.println(person);   //结果Pershon{name='jordan',age=23}
    }
}
如果需要在modelb项目中使用modela项目的内容,必须在modela项目的module-info.java中定义exports com.cnblogs.bean将该目录下可以被其他模块使用,如果没有写,则包默认是封装在模块下,不被外界使用。而在modela项目中需要使用requires modela导入需要使用的模块名,则可以在modelb中使用modela定义exports的类(即才可以在Test类中使用Person类)。

module-info.java:该文件必须位于项目的根目录中。该文件用于定义模块需要什么依赖,以及那些包被外部使用。

exports:控制着那些包可以被其他模块访问到,所有不被exports的包默认都被封装在模块里面不被外界所使用。

requires:指明对其他模块的依赖。


jshell

JDK9新增了REPL(Read-Eval-Print Loop)工具jshell,jshell工具提供了一个交互式命令界面,可以评估声明,语句和表达式,无需编译即可返回执行结果。
只需要在cmd中输入jshell命令,即可启动jshell界面。




多版本兼容JAR

在同一个目录下可以创建一个具有相同名称的java文件。
例如:
F:\peter\ideaWork\java9test\src\java\com\cnblogs\TestJar.java  新建TestJar.java
public class TestJar {
 
     public String getValue(){
         return "JAVA 8";
     }
 }
F:\peter\ideaWork\java9test\src\java\com\cnblogs\Test.java 新建Test.java
public class Test {
  
      public static void main(String[] args) {
          TestJar testJar = new TestJar();
          System.out.println(testJar.getValue());
      }
 }
F:\peter\ideaWork\java9test\src\java9\com\cnblogs\TestJar.java  新建TestJar.java文件,向后兼容jdk9
public class TestJar {
 
     public String getValue(){
         return "JAVA 9";
     }
 }
执行命令编译如下:
F:\peter\ideaWork\java9test>javac -d build --release 8 src/java/com/cnblogs/*.java

F:\peter\ideaWork\java9test>javac -d build9 --release 9 src/java9/com/cnblogs/TestJar.java

F:\peter\ideaWork\java9test>jar --create --main-class=Test --file multijar.jar -C build . --release 9 -C build9 .
在JDK9运行如下:
F:\peter\ideaWork\java9test>java -version
openjdk version "9"
OpenJDK Runtime Environment (build 9+181)
OpenJDK 64-Bit Server VM (build 9+181, mixed mode)

F:\peter\ideaWork\java9test> java -cp multijar.jar com.cnblogs.Test
JAVA 9
在JDK8运行如下:
F:\peter\ideaWork\java9test>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

F:\peter\ideaWork\java9test>java -cp multijar.jar com.cnblogs.Test
JAVA 8

接口的私有方法

在JDK8中接口运行使用静态方法和默认方法后,JDK9可以在接口中使用私有方法。
public interface MyInterface{
    void normalInterfaceMethod(); //抽象方法
    
    default void defaultMethod(){
        //jdk8特性之默认方法,接口中可以拥有方法体
        privateMethod();//可以调用私有方法
    }
    
    private void privateMethod(){  //jdk9新特性之,接口中可以用于私有方法
        System.out.println("接口中的私有方法");
    }
}

改进try-with-resources

JDK8中新增了try-with-resources语句,可以自动关闭需要关闭的资源文件但是必须在try语句后的括号中初始化需要关闭的资源。在JDK9中改进了try-with-resources语句,你可以在try外初始化资源,然后在try后的括号中添加需要自动关的资源即可。

JDK8之前

InputStreamReader reader = null;
try{
    reader = new InputStreamReader(System.in);
}catch(IOException e){
    e.printStackTrace();
}finally{
    if(reader != null){
        try{
            reader.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

JDK8  try后的括号中初始化资源,可以实现自动关闭

try(InputStreamReader reader = new InputStreamReader(System.in)){
    
}catch(IOException e){
    e.printStackTrace();
}

JDK9  可以在try外进行初始化,在括号内引用,即可实现资源自动关闭。

InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try(reader;writer){
    
}catch(IOException e){
    e.printStackTrace();
}

改进钻石操作符

Set<String> set = new HashSet<>(){
    //匿名实现类重写add方法。
    @Override
    public boolean add(String s) {
        System.out.println("执行add方法");
        return super.add(s);
    }
};
set.add("1");

限制使用单独下划线标识符

String _ = "hello"; //JDK8使用下划线单独命名
System.out.println(_);  //hello

//JDK9会报异常

String存储结构变更

JDK9之前String底层使用char数据存储数据private final char value[]  ,JDK9将String底层存储数据改为byte数组存储数据private final byte[] value
包括StringBuffer和StringBuilder,都是将以往char数组改为byte数组

快速创建只读集合

JDK9在List、Set和Map集合中新增了of静态方法,快速创建只读集合。
JDK9之前:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
//设为只读List集合
list = Collections.unmodifiableList(list);
System.out.println(list);
JDK9:
List<String> list  = List.of("A","B","C");
//同理:Set.of("A","B","C");    Map.of("A","B","C")

增强Stream API

JDK9在Stream接口中新增4个方法:dropWhile、takeWhile、ofNullable,为iterate方法新增重载方法。

takeWhile

takeWhile可以用于从 Stream 中获取一部分数据,接受一个 Predicate 来进行选择,在有序的 Stream 中,takeWhile 返回从头开始的尽可能多的元素。
List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().dropWhile((x) -> x < 80 ).forEach(System.out::println);
//takeWhile将会按照list集合有序的从45开始到第一个不符合条件为止的所有结果。

dropWhile

dropWhile 的方法刚好与 takeWhile相反,返回剩余的元素。
List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().dropWhile((x) -> x < 80 ).forEach(System.out::println);
//返回剩余的元素

ofNullable

在JDK8 中 Stream 不能完全为null,否则会报空指针异常。而在JDK9 中 ofNullable 方法允许创建一个为空的 Stream。
Stream<String> stream1 = Stream.of(null);
System.out.println(stream1.count());  //NullPointerException

Stream<String> stream1 = Stream.of("AA",null);
System.out.println(stream1.count()); //不报异常

List<String> list = new ArrayList<>();
list.add("A");
list.add(null);
System.out.println(list.stream().count());

//ofNullable() :允许值为 null
Stream<Object> stream = Stream.ofNullable(null);
System.out.println(stream.count());
Stream<String> stream2 = Stream.ofNullable("Hello World");
System.out.println(stream2.count());
iterate()重载方法
//JDK使用iterate方法,需要配合limit截止
Stream.iterate(1,(x) -> x+1).limit(10).forEach(System.out::print);
System.out.println();

//JDK9可以直接使用Predicate截止
Stream.iterate(1,(x) -> x<=10,(x) -> x+1).forEach(System.out::print);

改进Optional类
Optional 类是在JDK8中新增的类,主要是为了解决空指针异常。在JDK9中对这个类进行了改进,主要是新增了三个方法:stream,ifPresentOrElse 和 or 。
stream
stream方法将Optional转为一个 Stream,如果Optional 没有值就返回一个 Stream.empty
List<String> list = List.of("A","B","C","D","E","F");
Optional<List<String>> optional = Optional.of(list);
optional.stream().forEach(System.out::println);   //[A, B, C, D, E, F]

Optional<Object> optional1 = Optional.empty();
System.out.println(optional.stream().count());  //1

ifPresentOrElse   

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
如果 Optional 包含值,则对其包含的值调用函数 action,即 action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse 还有第二个参数 emptyAction。如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()。
//如果Optional包含值,执行action.accept方法
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse(x -> System.out.println("Value:"+x),() -> System.out.println("没有值"));
optional = Optional.empty();

//如果optional不包含值,执行emptyAction.run方法。
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
        System.out.println("没有值."));


//结果:   Value:1   没有值
or方法:
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
如果Optional有值,返回 Optional 指定的值,否则返回一个预设的值
//如果有值返回值。
Optional<String> optional1 = Optional.of("1");
Supplier<Optional<String>> supplierString = () -> Optional.of("没有值");
optional1 = optional1.or(supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));

//如果有值返回值。
Optional<String> optional1 = Optional.of("1");
Supplier<Optional<String>> supplierString = () -> Optional.of("没有值");
optional1 = optional1.or(supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));

多分辨率图像API

在 java.awt.image 包下新增了支持多分辨率图片的API,用于支持多分辨率的图片。
  1. 将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体。
  2. 获取这个图像的所有变体。
  3. 获取特定分辨率的图像变体,表示一张已知分辨率单位为 DPI 的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。
  4. java.awt.image.MultiResolutionImage接口的基础实现java.awt.image.BaseMultiResolutionImage获取所需要的变体。
  5. 通过接口的getResolutionVariant(double destImageWidth, double destImageHeight)方法,根据分辨率获取图像。

全新的HTTP客服端API

HTTP,用于传输网页的协议,早在 1997 年就被采用在目前的 1.1版本中。直到 2015 年,HTTP2 才成为标准
HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据
  • HTTP/1.1 依赖于请求/响应周期。
  • HTTP/2 允许服务器“push”数据:它可以发送比客户端请求更多的数据。 这使得它可以优先处理并发送对于首先加载网页至关重要的数据。
JDK9 中有新的方式来处理 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中。

智能Java编译工具

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

统一的JVM日志系统

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

Javadoc的HTML5支持

DK8 生成的java帮助文档是在 HTML4 中。而HTML4 已经是很久的标准了。

JDK9 的javadoc,现支持HTML5 标准。

Java动态编译器

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时有个更稳定版本才发布。