JDK9新特性

#一、模块化系统

##1、背景

Java 已经发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越来越暴露出一些问题:

  • Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程序运行需要的class)
  • 当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。
  • 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
  • 类路径本身也存在问题: 你怎么知晓所有需要的 JAR 都已经有了, 或者是不是会有重复的项呢?

本质上讲,模块(module)的概念,其实就是package外再裹一层,也就是说,用模块来管理各个package,通过声明某个package暴露,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。

##2、设计理念

模块独立、化繁为简

模块化(以 Java 平台模块系统的形式)将 JDK 分成一组模块,可以在编译时,运行时或者构建时进行组合

##3、实现目标

  • 主要目的在于减少内存的开销
  • 只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开发和维护
  • 改进 Java SE 平台,使其可以适应不同大小的计算设备
  • 改进其安全性,可维护性,提高性能

4、示例

4.1、创建maven工程

创建一个名为java9java9test的子工程

JDK9新特性

4.2、添加model类:Person

/**
 * @Description: Person
 * @Author: vesus
 * @CreateDate: 2019/1/9 下午2:23
 * @Version: 1.0
 */
public class Person {

    private static final Logger logger = Logger.getLogger("Person");

    private String name  ;
    private int age  ;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

###4.3、在java下创建module-info.java文件,通过exports将对应路径下的类暴露出去。

/**
 * @Description: module java9
 * @Author: vesus
 * @CreateDate: 2019/1/9 下午2:27
 * @Version: 1.0
 */
module java9 {
    exports com.vesus.model ;
    requires java.logging;
}

4.4、java9test添加java9的依赖

<dependencies>
    <dependency>
        <groupId>com.vesus</groupId>
        <artifactId>java9</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

4.5、在java9test创建测试类

/**
 * @Description: 测试类
 * @Author: vesus
 * @CreateDate: 2019/1/9 下午2:24
 * @Version: 1.0
 */
public class PersonTest {

    public static void main(String[] args) {
        Person person = new Person("vesus",18);
        System.out.println(person);
    }
}

4.6、在java下创建module-info.java文件,通过requires将对应类引入进来。

/**
 * @Description: module java9test
 * @Author: vesus
 * @CreateDate: 2019/1/9 下午2:25
 * @Version: 1.0
 */
module java9test {
    requires java9 ;
}

#二、jShell命令

1、进入jshell

wndows中输入cmd进入命令行,再输入jshell命令进入jshell,mac的终端输入jshell命令进入jshell

JDK9新特性

2、基本使用

JDK9新特性

#三、多版本兼容jar包

##1、在java文件夹下创建Generator类

public class Generator {
    public String toString() {
        return "hello java 8!";
    }
}

2、在java文件夹下创建App类

public class App {
    public static void main(String[] args) {
        Generator generator = new Generator();
        System.out.println(generator.toString());
    }
}

3、在java9文件加下面创建Generator类

public class Generator {
    public String toString() {
        return "hello java 9!";
    }
}

4、打包编译

javac -d build --release 8  src/main/java/com/vesus/*.java
javac -d build9 --release 9  src/main/java9/com/vesus/*.java
jar --create --main-class=App --file  app.jar -C build . --release 9 -C build9 .

##5、执行

java -cp app.jar com.vesus.App

执行结果:

hello java 9!

#四、接口的私有方法

##1、简介

Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。

在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。

2、示例

###2.1、声明接口

/**
 * @Description: 声明带有private方法的接口
 * @Author: vesus
 * @CreateDate: 2019/1/9 下午5:33
 * @Version: 1.0
 */
public interface MyInterface {

    void sayHello();

    default String hello(){
        init();
        return "hello" ;
    }

    static String world (){
        return "world" ;
    }

    private void init(){
        System.out.println("--------init------------");
    }
}

###2.2、实现MyInterface接口

public class HelloWorld implements MyInterface {
    @Override
    public void sayHello() {
        hello();
        System.out.println("hello world");
    }
}

#五、钻石操作符的使用升级

我们将能够与匿名实现类共同使用钻石操作符(diamond operator)

在java 8中如下的操作是会报错的:

public void getValue(){
    Map<String,String> map = new HashMap<>(){};
}

编译报错信息:’<>’ cannot be used with anonymous classes

Java 9中使用

public void getValue(){
    Map<String,String> map = new HashMap<>(){
        @Override
        public boolean isEmpty() {
            return true;
        }
    };
}

#六、语法改进:try语句

在java 8 之前,我们习惯于这样处理资源的关闭:

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

java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过。

public void test2(){
    try (InputStreamReader reader = new InputStreamReader(System.in)){
        reader.read();
    }catch (Exception e){
        e.printStackTrace();
    }
}

java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的

public void test3(){
    InputStreamReader reader = new InputStreamReader(System.in) ;
    try (reader){
        reader.read();
    }catch (Exception e){
        e.printStackTrace();
    }
}

#七、下划线使用限制

在java 8 中,标识符可以独立使用“_”来命名

String _ = "hello"; 
System.out.println(_);

但是,在java 9 中规定“_”不再可以单独命名标识符了

#八、String存储结构变更

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

private final byte[] value;

#九、便利的集合特性:of()

Java 9增加了List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法来创建不可变集合

List strs = List.of("Hello", "World");
List strs List.of(1, 2, 3);
Set strs = Set.of("Hello", "World");
Set ints = Set.of(1, 2, 3);
Map maps = Map.of("Hello", 1, "World", 2);

#十、增强的Stream API

Stream 接口中添加了 4 个新的方法:dropWhile, takeWhile, ofNullable。还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代

List<String> strings = List.of("abc", "bc", "efg", "gg", "abcd", "jkl");
//输出集合元素直到遇到不符合条件的字符串
strings.stream().takeWhile(s -> !s.equals("gg")).forEach(System.out::println);
System.out.println();
//从不符合条件的字符串开始输出集合元素
strings.stream().dropWhile(s -> !s.equals("gg")).forEach(System.out::println);
//指定初始值创建顺序流,设置停止条件与每次迭代的操作
IntStream.iterate(0, x -> x <= 10, x -> x + 2).forEach(System.out::println);

#十一、全新的HTTP客户端API

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中。

HttpClient client = HttpClient.newHttpClient();
 
HttpRequest request = HttpRequest.newBuilder()
    .uri(new URI("https://www.baidu.com/"))
    .build();
 
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
 
System.out.println(response.statusCode());
System.out.println(response.body());