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:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。