Java 9新特性概述

Java 9发布于2017年9月22日,距离Java 8版本发布已经过去了3年半,所以这个版本的新特性还是蛮多的,大概有90多个特性,不过大多数特性不太重要或者对于开发者来说不需要关注,这里就精选八个特性概述。

更多内容读者可自行阅读:OpenJDK Java 9官方文档

一、模块化

模块化的概念在Java 7就已经提出来了,由于它的复杂性,直到Java 9才实现。

1.1 什么是模块化

官方是这么定义的:该模块是一个具名的、自描述的代码和数据集合(the module,which is a named,self describing collection of code and data)。这到底是什么意思呢?

我们知道java文件时最小的可执行文件,为了更好的管理这些java文件,我们需要用package将同一类的java文件统一管理起来,然后可以根据package打包成jar文件。

java 9之前代码结构的层级关系是这样的:jar>package>java文件,Java 9在package之上加了一个module:jar>module>package>java文件,一个module可以包含多个package,也就是一个module可以管理多个package组件,这样可以让我们更好地组织和管理java应用程序的代码,以及更好地控制代码的可见新和依赖关系。

模块化的几个核心概念:

  • 模块:是模块化系统的基本单元,每个模块都有一个唯一的名称。

  • 模块路径:是一组包含模块的路径,用于在运行时指定程序所需的模块。

  • module-info.java文件:每个模块都包含这个文件,这个文件描述了模块的信息(模块名称、依赖关系以及其它模块信息等)。

  • 模块依赖性:在module-info.java文件中,可以用requires关键字声明各个模块之间的依赖关系。

  • 模块导出:在module-info.java文件中,可以用exports关键字声明哪些可以被其它模块访问。

1.2 为什么要模块化

java 8没有模块化的特性我们用着也很顺手啊,为什么java 9要引入模块化呢,主要有以下好处吧:

  • 隐藏内部实现:模块化系统允许开发者定义模块的公共API,同时隐藏模块的内部实现细节。这样,只有模块公开的API可以被其他模块访问,增强了代码的封装性。

  • 明确依赖关系:模块化特性使得模块之间的依赖关系更加明确。开发者可以指定模块所需的其他模块(即模块的依赖),并且可以明确指出哪些模块是可选的。这样可以减少依赖冲突和不必要的依赖,简化项目的依赖管理。

  • 减少启动时间:通过模块化,Java 9可以在启动时更高效地加载和链接模块,尤其是对于大型应用程序,这可以显著减少启动时间。

  • 简化构建过程:模块化系统使得大型应用程序可以分成多个模块进行开发和维护。每个模块可以独立编译、测试和部署,从而提高了开发和维护的效率。

  • 更严格的访问控制:通过模块化,Java 9可以更严格地控制模块间的访问权限。只有被公开的模块API可以被其他模块使用,这样有助于提高系统的安全性,防止不必要的类被暴露给其他模块。

  • 工具和库的改进:模块化为Java开发工具和库提供了更好的支持。例如,IDE(集成开发环境)和构建工具可以利用模块信息来提供更智能的提示和依赖管理。

  • 适应复杂应用程序:对于大型和复杂的系统,模块化帮助更好地组织代码,使得应用程序的结构更加清晰和可维护。这对企业级应用程序尤其重要。

1.3 module-info.java基本结构

module my.module {
    // 声明依赖的其他模块
    requires some.other.module;
    // 声明导出的包(暴露出去)
    exports com.example.package;
    // 声明模块所使用的服务
    uses com.example.service.MyService;
    // 声明提供服务的实现
    provides com.example.service.MyService with com.example.service.impl.MyServiceImpl;
}

解释:

  • module my.module: 定义模块的名称。

  • requires some.other.module: 声明当前模块依赖于 some.other.module 模块。

  • exports com.example.package: com.example.package 包公开给其他模块,使其可以被其他模块访问。

  • uses com.example.service.MyService: 声明当前模块依赖于 MyService 服务。

  • provides com.example.service.MyService with com.example.service.impl.MyServiceImpl: 声明当前模块提供了 MyService 服务的实现,具体实现类是 MyServiceImpl

二、String存储结构变更

这个类我们已经很熟悉了,Java 9之前的存储结构是char[],每个char占2个字节,经过JDK开发人员调研了成千上万个程序的heap dump信息后发现大多数字符串都是以Latin-1字符编码表示的,它只需要1个字节就够了,这就减少了50%的存储空间。

所以,在Java 9中将String的存储结构改为了byte[]:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    @Stable
    private final byte[] value;
    //...
}

三、集合新增工厂方法

只读集合在我们编程中或许大多数人都没使用过,只读就意味着不可变,是线程安全的。但仔细想下,使用场景还是蛮少的,没有线程安全的场景就用非线程安全的集合就好(List、Map),有线程安全问题肯定意味着有修改操作,直接用线程安全类的集合,所以只读集合没啥人用。还有一方面的原因是Java 9之前创建一个只读集合太复杂了。

Java 8:

public static void main(String[] args) {
    List<String> list=new ArrayList<>();
    list.add("a");
    list.add("b");
    //转为只读集合
    list = Collections.unmodifiableList(list);
    list.add("c");//java.lang.UnsupportedOperationException
}

转为只读集合后,如果还对集合进行修改就会抛出UnsupportedOperationException异常。

Java 9:

public static void main(String[] args) {
    List<String> list = List.of("a", "b");
    list.add("c");//java.lang.UnsupportedOperationException
}

简单多了。

ps:不允许有null,否则抛NPE异常。

四、Stream 新增API

java 9在Java 8的基础上,对Stream类新增了几个方法。

4.1 ofNullable()

主要作用是简化处理可能包含null值的集合,避免显示地检查或过滤null值。

public static <T> Optional<T> ofNullable(T value)

public static void main(String[] args) {
    List<String> list = Arrays.asList("a", null, null, "d");
    System.out.println("list = " + list);
    list.stream()
            .flatMap(Stream::ofNullable)
            .forEach(System.out::println);
}

输出:

list = [a, null, null, d]
a
d

4.2 iterate()方法

Java 8中的iterate()方法用于创建一个无限流,其元素由给定的初始值和一个生成下一个元素的函数产生,一般配和limit()使用,java 9重载了iterate()方法:

public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

public static void main(String[] args) {
    //Java 8
    Stream.iterate(1,i->i+2).limit(10).forEach(i-> System.out.print(i+" "));
    System.out.println();
    //Java 9
    Stream.iterate(1,n->n<=10,i->i+2).forEach(i-> System.out.print(i+" "));
}

输出:

1 3 5 7 9 11 13 15 17 19 
1 3 5 7 9 

4.3 dropWhile和takeWhile

这俩方法的作用是,允许我们根据谓词条件从流中选择或删除元素,直到遇到第一个不满足条件的元素。

default Stream<T> dropWhile(Predicate<? super T> predicate)

default Stream<T> takeWhile(Predicate<? super T> predicate)

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 1, 4, 10);
    //dorpWhile:会从流的开始,跳过满足条件的所有元素,直到找到第一个不满足条件的元素,
    //一旦找到第一个不满足条件的元素,就停止跳过元素
    list.stream()
            .dropWhile(i->i<5)
            .forEach(i-> System.out.print(i+" "));
    System.out.println();
    //takeWhile与dorpWhile相反
    //从流的开始,选择满足条件的所有元素,直到找到第一个不满足条件的元素,
    //一旦找到第一个不满足条件的元素,就停止选择元素。
    list.stream()
            .takeWhile(i->i<5)
            .forEach(i-> System.out.print(i+" "));
}

输出:

5 6 7 1 4 10 
1 2 3 4 

五、Optional增强

Java 9中也对Optional类做了一些增强,主要是新增了三个方法。

5.1 or()

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

public static void main(String[] args) {
    Optional<String> optional = Optional.of("橡皮人");
    Optional<Object> empty = Optional.empty();
    //optional = 橡皮人
    System.out.println("optional = " + optional.or(()->Optional.of("day day up")).get());
    //empty = day day up
    System.out.println("empty = " + empty.or(()->Optional.of("day day up")).get());
}

5.2 isPresentOrElse()

Java 8有个isPresent()方法,ifPresentOrElse()方法是对其的增强。

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

public static void main(String[] args) {
    //Java 8
    Optional<String> empty = Optional.empty();
    if(empty.isPresent()){
        //不为空
    }else {
        //为空
        System.out.println("empty is null");
    }
    //Java 9
    empty.ifPresentOrElse(System.out::println, () -> System.out.println("empty is null"));
}

输出:

empty is null
empty is null

5.3 stream()

允许我们将一个Optional对象转为一个Stream流对象,以便更能灵活处理。

public static void main(String[] args) {
    Optional<String> str = Optional.of("hello world!!!");
    //HELLO WORLD!!!
    str.stream().map(String::toUpperCase).forEach(System.out::println);
}

六、CompletableFuture增强

在Java 9中,CompletableFuture引入了支持超时和延迟执行的API,主要新增了以下几个方法:

  • CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
  • CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit) Executor
  • delayedExecutor(long delay, TimeUnit unit, Executor executor)

解释:

  • orTimeout():若在指定的超时时间之前未完成,则会以异常(TimeoutException)结束。

  • completeOnTimeout():若在指定的超时时间之前未完成,则以给定的value为任务结果。

  • delayedExecutor():可以将任务在延迟指定时间后执行。

七、接口支持私有化

Java 9接口支持私有方法,可以将通用的功能封装在私有方法中,但它有几个限制:

  • 接口私有方法不能是抽象的。

  • 私有方法只能在接口内部使用。

  • 私有方法不会继承给接口的子接口。

  • 私有静态方法可以在其他静态和非静态接口方法中使用。

举个例子:

public interface MyInterface {
​
    default void defaultMethod() {
        System.out.println("defaultMethod");
        privateMethod();
        privateStaMethod();
    }
​
    private void privateMethod(){System.out.println("privateMethod");}
​
    private static void privateStaMethod(){System.out.println("privateStaticMethod");}
​
    static void staticMethod(){
        System.out.println("staticMethod");
        privateStaMethod();
    }
}
​
@Test
void test() {
    MyInterface myInterFaceImpl=new MyInterFaceImpl();
    myInterFaceImpl.defaultMethod();
    MyInterface.staticMethod();
​
}

输出:

defaultMethod
privateMethod
privateStaticMethod
staticMethod
privateStaticMethod

八、try-with-resources语法糖改进

日常开发中,有很多资源需要我们用完即关,那么try-with-resource语法糖就不需要我们再在finally里进行手动关闭了,要使用这个语法糖有个必要条件就是:资源必须实现AutoCloseable或Closeable接口。

Java 7 try-with-resource写法:

public static void main(String[] args) throws FileNotFoundException {
    //Java 7
    try (FileInputStream fis1 = new FileInputStream("file.txt");
         FileInputStream fis2 = new FileInputStream("file.txt");
         FileInputStream fis3 = new FileInputStream("file.txt");) {
        //代码逻辑
    } catch (IOException e) {
        //处理异常
    }
}

Java 9try-with-resource写法:

public static void main(String[] args) throws FileNotFoundException {
    //Java 9
    FileInputStream fis1 = new FileInputStream("file.txt");
    FileInputStream fis2 = new FileInputStream("file.txt");
    FileInputStream fis3 = new FileInputStream("file.txt");
    try (fis1;fis2;fis3) {
        //代码逻辑
    } catch (IOException e) {
        //处理异常
    }
}

在上述例子中,由于FileInputStream都实现了Closeable接口,所以在try执行完之后,会依次关闭。

End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橡 皮 人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值