JDK9新特性
文章目录
- 新特性:
- 1.模块化系统
- 使用模块化
- module-info.java:
- exports:
- opens:
- requires:
- provides:
- uses:
- 2.JShell
- 启动Jshell
- 执行计算
- 定义变量
- 定义方法
- 定义类
- 帮助命令
- 查看定义的变量:/vars
- 查看定义的方法:/methods
- 查看定义的类:/types
- 列出输入源条目:/list
- 编辑源条目:/edit
- 删除源条目:/drop
- 保存文件:/save
- 打开文件:/open
- 重置jshell:/reset
- 查看引入的包:/imports
- 退出jshell:/exit
- 3.集合工厂方法
- 4.接口的改变
- 5.Stream API
- ofNullable
- iterate
- takeWhile
- dropWhile
- 6.Optional新增方法
- 7.Deprecated注解
- 8.钻石操作符<>
- 9.异常处理
- 10.String底层变更
- 11.不能使用下划线作为变量名
- 12.try-with-resources 优化
- 13.CompletableFuture增强
- 14.垃圾收集器
新特性:
1.模块化系统
模块化系统是 Java9 架构的一次重大变革,它旨在解决长期以来 Java 应用所面临的一些结构性问题,特别是在大型系统和微服务架构中。
如果把 Java 8 比作单体应用,那么引入模块系统之后,从 Java 9 开始,Java 就华丽的转身为微服务。模块系统,项目代号 Jigsaw,最早于 2008 年 8 月提出,2014 年跟随 Java 9 正式进入开发阶段,最终跟随 Java 9 发布于 2017 年 9 月。
那么什么是模块系统?官方的定义是A uniquely named, reusable group of related packages, as well as resources (such as images and XML files) and a module descriptor。模块的载体是 jar 文件,一个模块就是一个 jar 文件,但相比于传统的 jar 文件,模块的根目录下多了一个 module-info.class 文件,也即 module descriptor。 module descriptor 包含以下信息:
- 模块名称
- 依赖哪些模块
- 导出模块内的哪些包(允许直接 import 使用)
- 开放模块内的哪些包(允许通过 Java 反射访问)
- 提供哪些服务
- 依赖哪些服务
我们知道在 Java 中, Java 文件是最小的可执行文件,为了更好地管理这些 Java 文件,我们需要用 package 将同一类的 Java 文件统一管理起来,多个 package 文件、Java 文件可以打包成一个 jar 文件,现在 Java 9 在 package 上面增加 module,一个 module 可以包含多个 package,所以从代码结构上来看层级关系是这样的:jar > module > package > java 文件。
引入模块化其实在一定程度上增加编码的复杂度,特别是对于之前习惯于传统的包结构的开发者来说。然而,引入模块化有着一些非常重要的优势,这些优势可以帮助我们更好地组织、管理和维护我们的Java应用程序,提高开发效率和代码质量。
- 更好的代码组织:模块化允许开发者将代码划分为独立的模块,每个模块都可以作为一个功能单元进行开发和测试。这有助于编写高内聚低耦合的代码,并清晰地管理模块间的依赖关系。
- 提升性能与可伸缩性:通过模块化,JDK 和 JRE 可以重新安排到可互操作的模块中,支持创建可在小型设备上执行的可扩展运行时。模块化还使得 Java 应用更容易适配到更小的设备中,这对于嵌入式系统和物联网设备尤为重要。
- 改善安全和维护性:模块化 JDK 和 JRE 可以提高安全性、维护性,并允许定制运行时环境。例如,如果某个网络应用不需要 Swing 图形库,可以在打包应用时选择不包含该库,从而减少性能消耗。
- 提高编译效率:在 Java 9 中,构建系统通过 JEP 201 进行编译和实施模块边界,增强了在构建时编译模块和识别模块边界的能力。
- 促进大型项目管理:对于大型和复杂的应用程序,模块化可以将应用分解为完成特定功能的小块,这有助于简化开发过程和管理依赖关系。
- 支持运行时组合:模块化允许在运行时动态组合不同的模块,提供了更大的灵活性和可定制性。
- 便于迁移和维护:模块化可以帮助开发者逐步迁移旧的非模块化代码库至新的模块化结构,而不必一开始就全面转换整个代码库。
模块化怎么体现的呢?下图是Java 8与Java 9的目录结构:
可以看出 Java 9 中没有jre,没有rt.jar,没有tools.jar,而是多了一个 jmods,该文件夹下都是一个一个的模块。
在Java 9之前的项目中,一个简单的"hello world"程序,也需要引入rt.jar文件,导致生成的jar包比较庞大。然而,Java 9引入了模块化的概念,使得情况有了改变。现在,使用Java 9及更高版本,你只需要引入程序所依赖的模块,而不是整个rt.jar,这使得构建简单程序时所生成的jar包大小大大减小。这种模块化的方式使得Java应用程序更加轻量化、灵活,也更容易管理和维护。
使用模块化
创建两个模块:testA、testB
testA:com.linging.config、com.linging.model、module-info.java
testB:Main、module-info.java
public class User {private String name;private String age;//getter and setter...@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';}private void hello(){System.out.println("访问private方法");}protected void hello2(){System.out.println("访问protected方法");}public void hello3(){System.out.println("访问public方法");}
}
module-info.java:
module-info.java 文件是 Java 模块系统的核心组成部分,用于定义 Java 模块的信息
。从 JDK 9 开始,这个文件被用来描述模块的元数据,包括模块名称、它导出的包、它打开的包以及其他模块依赖等信息
。
module <模块名> {// 模块声明,例如导出包、打开包、依赖其他模块等
}
exports:
exports
用于在编译时和运行时允许其他模块访问指定包中的public
成员,不能访问private
、protected
或包私有的成员。
exports <包名>;
module testA {exports com.linging.model;
}
opens:
opens
用于在运行时允许其他模块通过反射
访问指定包中的所有类型及其成员,包括private
和protected
成员。
//对所有模块开放
opens <包名>;
//对指定模块开放
opens <包名> to <模块1>, <模块2>...;
module testA {exports com.linging.model;opens com.linging.model;
}
module testA {exports com.linging.model;opens com.linging.model to testB;
}
User user = new User();
Class<? extends User> aClass = user.getClass();// private方法
Method hello = aClass.getDeclaredMethod("hello");
hello.setAccessible(true);
hello.invoke(user);// protected方法
Method hello2 = aClass.getDeclaredMethod("hello2");
hello2.setAccessible(true);
hello2.invoke(user);//public方法
Method hello3 = aClass.getDeclaredMethod("hello3");
hello3.invoke(user);
requires:
requires
关键字用于声明一个模块对其它模块的依赖关系。这意味着当前模块需要使用到另一个模块所提供的类或服务。在 module-info.java 文件中,requires 子句用于指定这些依赖关系。
//在编译时和运行时都需要该模块
requires <模块名>;
//静态依赖:表示该模块在编译时是必需的,但在运行时是可选的。也就是说,如果在运行时该模块不存在,程序仍然可以启动,只要不执行依赖于该模块的代码
requires static <模块名>;
//传递性依赖:使用transitive关键字表示该模块的依赖是传递的。这意味着如果模块A依赖于模块B,并且模块B使用了requires transitive声明依赖于模块C,那么模块A也将隐式地依赖于模块C
requires transitive <模块名>;
module testB {requires testA;
}
provides:
provides
关键字用于声明模块提供的服务实现。它与uses
关键字一起使用,实现了服务提供者接口(SPI)机制,允许模块声明它们提供的服务实现,以便其他模块可以使用这些服务。
provides <service-interface> with <implementation1-class>,<implementation2-class>...;
module testA {exports com.linging.service;provides com.linging.service.UserServicewith com.linging.service.impl.UserServiceImpl,com.linging.service.impl.UserServiceImpl2;
}
uses:
uses
关键字用于声明模块需要的服务。具体来说,uses
用于指定模块需要使用的服务接口或抽象类。这个服务通常是一个接口或抽象类,而不是实现类。
uses <service-interface>;
module testB {requires testA;uses com.linging.service.UserService;
}
ServiceLoader<UserService> serviceLoader = ServiceLoader.load(UserService.class);
for (UserService service : serviceLoader) {System.out.println(service.getUser());
}
2.JShell
交互式编程环境是一种让程序员能够即时输入代码并立即获得反馈的开发环境。每输入一行代码,系统就会立刻执行并显示结果,使得用户可以快速验证想法、进行简单计算等操作。尽管这种环境不太适合处理复杂的工程需求,但在快速验证和简单计算等场景下非常实用。尽管其他高级编程语言(比如Python)早就拥有了交互式编程环境,Java直到Java 9才正式推出了类似的工具。
下面就来一起学习下,这个Java中的交互式编程环境Jshell。
启动Jshell
C:\Users\Linging>jshell
| 欢迎使用 JShell -- 版本 9.0.4
| 要大致了解该版本, 请键入: /help intro
jshell>
执行计算
jshell> 1+1
$1 ==> 2jshell> 2*3
$2 ==> 6
定义变量
jshell> int a = 1, b = 2;
a ==> 1
b ==> 2jshell> a+b
$7 ==> 3
定义方法
jshell> public int sum(int a, int b){...> return a + b;...> }
| 已创建 方法 sum(int,int)jshell> sum(1,2)
$9 ==> 3
定义类
jshell> public class Calc{...> int a;...> int b;...> public Calc(){}...> public Calc(int a, int b){...> this.a = a;...> this.b = b;...> }...> public int add(){...> return a + b;...> }...> }
| 已创建 类 Calcjshell> Calc c = new Calc(1,2)
c ==> Calc@69ea3742jshell> c.add()
$12 ==> 3
帮助命令
jshell> /help
| 键入 Java 语言表达式, 语句或声明。
| 或者键入以下命令之一:
| /list [<名称或 id>|-all|-start]
| 列出您键入的源
| /edit <名称或 id>
| 编辑按名称或 id 引用的源条目
| /drop <名称或 id>
| 删除按名称或 id 引用的源条目
| /save [-all|-history|-start] <文件>
| 将片段源保存到文件。
| /open <file>
| 打开文件作为源输入
| /vars [<名称或 id>|-all|-start]
| 列出已声明变量及其值
| /methods [<名称或 id>|-all|-start]
| 列出已声明方法及其签名
| /types [<名称或 id>|-all|-start]
| 列出已声明的类型
| /imports
| 列出导入的项
| /exit
| 退出 jshell
| /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] ...
| 查看或更改评估上下文
| /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]...
| 重启 jshell
| /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]...
| 重置和重放相关历史记录 -- 当前历史记录或上一个历史记录 (-restore)
| /history
| 您键入的内容的历史记录
| /help [<command>|<subject>]
| 获取 jshell 的相关信息
| /set editor|start|feedback|mode|prompt|truncation|format ...
| 设置 jshell 配置信息
| /? [<command>|<subject>]
| 获取 jshell 的相关信息
| /!
| 重新运行上一个片段
| /<id>
| 按 id 重新运行片段
| /-<n>
| 重新运行前面的第 n 个片段
|
| 有关详细信息, 请键入 '/help', 后跟
| 命令或主题的名称。
| 例如 '/help /list' 或 '/help intro'。主题:
|
| intro
| jshell 工具的简介
| shortcuts
| 片段和命令输入提示, 信息访问以及
| 自动代码生成的按键说明
| context
| /env /reload 和 /reset 的评估上下文选项
查看定义的变量:/vars
jshell> /vars
| int $1 = 2
| int $2 = 6
| int a = 1
| int b = 2
| int $7 = 3
| int $9 = 3
| Calc c = Calc@69ea3742
| int $12 = 3
查看定义的方法:/methods
jshell> /methods
| int sum(int,int)
查看定义的类:/types
jshell> /types
| class Calc
列出输入源条目:/list
查看jshell之前输入的所有内容:
jshell> /list1 : 1+12 : 2*35 : int a = 1, b = 2;6 : int a = 1, b = 2;7 : a+b8 : public int sum(int a, int b){return a + b;}9 : sum(1,2)10 : public class Calc{int a;int b;public Calc(){}public Calc(int a, int b){this.a = a;this.b = b;}public int add(){return a + b;}}11 : Calc c = new Calc(1,2);12 : c.add()
左侧的数字为条目id,可以利用该id,进行编辑和删除操作。
编辑源条目:/edit
jshell> /edit 1
删除源条目:/drop
jshell> /list1 : 1+12 : 2*35 : int a = 1, b = 2;6 : int a = 1, b = 2;7 : a+b8 : public int sum(int a, int b){return a + b;}9 : sum(1,2)10 : public class Calc{int a;int b;public Calc(){}public Calc(int a, int b){this.a = a;this.b = b;}public int add(){return a + b;}}11 : Calc c = new Calc(1,2);12 : c.add()13 : 1+2;14 : 1+4;15 : 1+4;jshell> /drop 1
| 已删除 变量 $1jshell> /list2 : 2*35 : int a = 1, b = 2;6 : int a = 1, b = 2;7 : a+b8 : public int sum(int a, int b){return a + b;}9 : sum(1,2)10 : public class Calc{int a;int b;public Calc(){}public Calc(int a, int b){this.a = a;this.b = b;}public int add(){return a + b;}}11 : Calc c = new Calc(1,2);12 : c.add()13 : 1+2;14 : 1+4;15 : 1+4;
保存文件:/save
// 未指定路径,则保存在启动jshell的当前目录
jshell> /save me.txt
jshell> /save C:\Users\Linging\Desktop\jmod\my.txt
打开文件:/open
// 当切换了jshell环境后,可以通过打开之前保存的文件来快速还原之前的执行内容
jshell> /open my.txtjshell> /list1 : 1+1
重置jshell:/reset
jshell> /reset
| 正在重置状态。jshell> /list
查看引入的包:/imports
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
退出jshell:/exit
jshell> /exit
| 再见
3.集合工厂方法
在 Java 9 之前,要构建一个不可变集合往往需要经历若干繁琐的步骤,如初始化集合、添加元素以及对其进行封装处理。Java 9 通过引入专门的不可变集合(包括List、Set、Map)特性,旨在简化这一流程,提供了一个既简洁又安全的方法来创建不可变集合,确保了集合内容的不变性和线程安全性。
其内容包括:
- List.of():创建一个不可变的 List,可以传递任意数量(与其他工具类注意区分)的元素,注意里面不能有null。
- Set.of():创建一个不可变的 Set,可以传递任意数量(与其他工具类注意区分)的元素,注意里面不能有null。
- Map.of() 和 Map.ofEntries():用于创建一个不可变的 Map。Map.of() 可以直接传递键值对,而 Map.ofEntries() 可以通过 Map.entry(k, v) 创建条目,注意key和value都能为null。 以上方法创建的集合为不可变对象,不能添加、修改、删除。
List<Integer> list = List.of(1, 2, 3, 4, 5);Set<Integer> set = Set.of(1, 2, 3, 4, 5);Map<String, String> map1 = Map.of("name", "zhangsan", "age", "18");
Map<String, String> map2 = Map.ofEntries(Map.entry("name", "zhangsan"), Map.entry("age", "18"));
List.of和Arrays.asList比较类似,那他们之间除了长的不一样外,还有什么区别吗?
- Java 9中推出List.of创建的是不可变集合,而Arrays.asList是可变集合(长度不可变,值可变)
- List.of和Arrays.asList都不允许add和remove元素,但Arrays.asList可以调用set更改值,而List.of不可以,会报java.lang.UnsupportedOperationException异常
- List.of中不允许有null值,Arrays.asList中可以有null值
4.接口的改变
Java 8 支持接口的默认方法和静态方法 -----> Java 9 可定义 private 私有方法。
接口中私有方法的特点:
- 私有方法不能定义为抽象的
- 私有方法只能在接口内部使用,实现该接口的类或其他外部类无法调用这些方法
- 私有方法不会继承给接口的子接口,每个接口都必须自己定义自己的私有方法。
public interface UserService {// 普通抽象方法,需要被子类实现User getUser();// 私有方法private void helloPri(){System.out.println("===>执行私有方法");}//默认方法default void init(){System.out.println("===>执行默认方法");// 调用私有方法helloPri();}// 私有静态方法private static void stcPri(){System.out.println("===>执行私有静态方法");}// 静态方法static void stcInit(){System.out.println("===>执行静态方法");//调用静态私有方法stcPri();}
}
public class UserServiceImpl implements UserService {@Overridepublic User getUser() {User user = new User();user.setName("张三");user.setAge("18");System.out.println(user);return user;}
}
public static void main(String[] args) {UserService userService = new UserServiceImpl();// 调用抽象方法的实现userService.getUser();// 调用默认方法userService.init();// 调用静态方法UserService.stcInit();
}
5.Stream API
Java 9 对 Stream API 做了一些增强。
ofNullable
static <T> Stream<T> ofNullable(T t) 返回包含单个元素的顺序Stream ,如果非空,否则返回空Stream。
//当需要对可能为null的元素进行流操作时,使用ofNullable可以省去显式的空值判断,使代码更加简洁:
List<String> list = null;
long count = Stream.of(list).flatMap(Collection::stream).count();
System.out.println(count); // 抛出空指针异常List<String> list = null;
long count = Stream.ofNullable(list).flatMap(Collection::stream).count();
System.out.println(count); // 输出0,不会抛出异常
iterate
Stream类的iterate方法新增了一个重载版本,这个版本比原来的iterate方法更加灵活,因为它允许指定一个终止条件。
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
- seed 初始种子值。
- hasNext 用来判断何时结束流,如果 hasNext 返回 true,则next 函数就会继续生成下一个元素;一旦 hasNext 返回 false,序列生成将停止。
- next函数用来计算下一个元素值。
// jdk8
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);//jdk9 重载方法
Stream.iterate(1, i -> i <= 10, i -> i + 1).forEach(System.out::println);//输出结果
1
2
3
4
5
6
7
8
9
10
takeWhile
//takeWhile允许你根据给定的条件[截取]流中的元素,直到遇到第一个不满足条件的元素为止,并返回一个新的流,其中包含了前缀元素
Stream<T> takeWhile(Predicate<? super T> predicate)
Stream.of(1,2,3,4,5,6,7,8,9).takeWhile(n -> n < 5).forEach(System.out::println);
// 输出:
1
2
3
4
dropWhile
//dropWhile允许你根据给定的条件[跳过]流中的元素,直到遇到第一个不满足条件的元素为止,并返回一个新的流,其中包含了剩余的元素。
Stream<T> dropWhile(Predicate<? super T> predicate)
Stream.of(1,2,3,4,5,6,7,8,9,1).dropWhile(n -> n < 5).forEach(System.out::println);
// 输出:
5
6
7
8
9
1
6.Optional新增方法
Optional增加了三个有用的API。
stream()
:Optional现在可以转Stream。ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
:如果有值了怎么消费,没有值了怎么消费。or(Supplier<? extends Optional<? extends T>> supplier)
: 该方法接受一个 Supplier作为参数,如果当前Optional有值就返回当前Optional,当前Optional 为空时,将执行Supplier获取一个新的Optional 对象。可以用于提供默认值。
long count = Optional.of("hello").stream().count();
Optional.ofNullable(null).ifPresentOrElse(value -> {System.out.println("有值:" + value);
}, ()->{System.out.println("没有值...");
}); // 没有值...
String str = null;
Optional<String> optional = Optional.ofNullable(str).or(() -> {return Optional.ofNullable("默认值");
});
System.out.println(optional.get()); //默认值
7.Deprecated注解
@Deprecated 注解用于标记过时的 API,Java 9 对其进行了改进,增加了两个的属性:
- since :指明从哪个版本开始 API 被弃用。
- forRemoval:指出这个 API 是否计划在未来的版本中被移除。
比如: Thread类的destory()、stop()。
8.钻石操作符<>
在 Java 7 中引入的钻石操作符简化了泛型实例的创建,但它不能用于匿名内部类。由于这个限制,开发者不得不在使用匿名内部类时指定泛型参数,这增加了代码的冗余和复杂性。
static class InnerCls<T>{}public static void main(String[] args) {InnerCls<String> innerCls = new InnerCls<String>() {};
}
在 Java 9 中,钻石操作符得到了改进,允许与匿名内部类配合使用。现在,当我们实例化一个具有泛型参数的匿名内部类时,无需显式指定这些参数,因为 Java 编译器能够利用上下文信息自动推导出正确的类型。
static class InnerCls<T>{}public static void main(String[] args) {InnerCls<String> innerCls = new InnerCls<>() {};
}
9.异常处理
try-with-resources是JDK7中一个新的异常处理机制,它能够很容易得关闭在try-catch语句中使用得资源。try-with-resources语句确保了每个资源在语句结束时关闭。所有实现了java.lang.AutoCloseable
接口得对象,可以使用作为资源。
try-with-resources声明在JDK9已得到改进。如果你已经有一个资源时final或等效于final变量,你可以在try-with-resources语句中使用该变量,而无需在try-with-resources语句中声明一个新变量。
public static void main(String[] args) throws FileNotFoundException {//jdk8以前try (FileInputStream fileInputStream = new FileInputStream("");FileOutputStream fileOutputStream = new FileOutputStream("")) {} catch (IOException e) {e.printStackTrace();}//jdk9FileInputStream fis = new FileInputStream("");FileOutputStream fos = new FileOutputStream("");//多资源用分号隔开try (fis; fos) {} catch (IOException e) {e.printStackTrace();}}
10.String底层变更
在 JDK 9 中,String
类的底层实现发生了重大变化,从 char[]
改为 byte[]
,并新增了一个 coder
字段来表示编码方式。
编码方式:
- Latin-1 (ISO-8859-1):如果字符串中的字符都在 Latin-1 范围内(即 ASCII 字符),每个字符占用 1 个字节。
- UTF-16:如果字符串中包含超出 Latin-1 范围的字符(如中文等),则使用 UTF-16 编码,每个字符占用 2 个字节或 4 个字节。
code属性:
0
表示 Latin-1 编码。1
表示 UTF-16 编码。
大多数字符串只包含 Latin-1 范围内的字符,使用 byte[]
存储可以显著减少内存占用。例如,一个包含大量 ASCII 字符的字符串,内存占用可以减少一半。这不仅节省了内存,还减少了垃圾回收(GC)的次数,从而提高了性能。
length()计算长度:
- 如果
coder
为0
(Latin-1 编码),则length()
返回byte[]
数组的长度。 - 如果
coder
为1
(UTF-16 编码),则length()
返回byte[]
数组长度的一半(因为每个字符占用 2 个字节)。
public final class String implements Serializable, Comparable<String>, CharSequence {@Stableprivate final byte[] value;private final byte coder;private int hash;static final byte LATIN1 = 0;static final byte UTF16 = 1;// ....
}
11.不能使用下划线作为变量名
在早期版本的 Java 中,下划线(_)已用作标识符或创建 变量名称。从 Java 9 开始,下划线字符是一个保留关键字,不能用作标识符或变量名。如果我们使用单个下划线作为标识符,程序将无法编译并抛出编译时错误,因为现在它是一个 关键字,并且在 Java 9 或更高版本中不能用作变量名称。
public static void main(String[] args) {int _ = 2;
}
12.try-with-resources 优化
在Java 7 中引入了try-with-resources功能,保证了每个声明了的资源在语句结束的时候都会被关闭。
任何实现了java.lang.AutoCloseable接口的对象,和实现了java.io.Closeable接口的对象,都可以当做资源使用。
在Java 7中需要这样写:
try (BufferedInputStream bi = new BufferedInputStream(System.in);BufferedInputStream bi2 = new BufferedInputStream(System.in)) {// do something
} catch (IOException e) {e.printStackTrace();
}
而到了Java 9无需为 try-with-resource 临时声明变量,简化为:
BufferedInputStream bi = new BufferedInputStream(System.in);
BufferedInputStream bi2 = new BufferedInputStream(System.in);
try (bi;bi2) {// do something
} catch (IOException e) {e.printStackTrace();
}
13.CompletableFuture增强
CompletableFuture 是 Java 8 中引入用于处理异步编程的核心类,它引入了一种基于 Future 的编程模型,允许我们以更加直观的方式执行异步操作,并处理它们的结果或异常。
但是在实际使用过程中,发现 CompletableFuture 还有一些改进空间,所以 Java 9 对它做了一些增强,主要内容包括:
- 新的工厂方法
- 支持延迟执行和超时(timeout)机制
- 支持子类化
新的工厂方法:
completedFuture(U value)
:创建一个已经完成的CompletableFuture
实例failedFuture(Throwable ex)
:创建一个异常完成的CompletableFuture
实例。completedStage(U value)
:返回完成的CompletionStage
。failedStage(Throwable ex)
:返回异常完成的CompletionStage
。
支持超时机制:
如果执行超时, orTimeout() 方法直接抛出了一个异常,而 completeOnTimeout() 方法则是返回默认值
// 允许为 CompletableFuture 设置一个超时时间。如果在指定的超时时间内未完成,将抛出 TimeoutException 异常
public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
// 允许为 CompletableFuture 设置一个超时时间。如果在指定的超时时间内未完成,则返回默认值value
public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)
支持延迟执行:
CompletableFuture 类通过 delayedExecutor() 方法提供了对延迟执行的支持,该方法负责生成一个具有延后执行功能的 Executor,使得任务可以在未来指定的时间点才开始执行。
public static Executor delayedExecutor(long delay, TimeUnit unit)
支持子类化:
Java 9 为 CompletableFuture
做了一些改进,使得 CompletableFuture
可以被更简单地继承。
-
newIncompleteFuture()
:创建一个新的、不完整的CompletableFuture
实例。子类可以重写这个方法来返回CompletableFuture
的子类实例,允许在整个CompletableFuture
API 中自定义实例的行为。public <U> CompletableFuture<U> newIncompleteFuture()
-
defaultExecutor()
:提供CompletableFuture
操作的默认执行器。子类可以重写此方法以提供不同的默认执行器,这对于定制任务执行策略特别有用。public Executor defaultExecutor()
-
copy()
:创建一个与当前CompletableFuture
状态相同的新实例。子类可以重写此方法以确保复制的实例是特定子类的实例,而不仅仅是CompletableFuture
。public CompletableFuture copy()
public class MyCompletableFuture<U> extends CompletableFuture<U> {@Overridepublic Executor defaultExecutor() {return Executors.newSingleThreadExecutor();}@Overridepublic CompletableFuture<U> newIncompleteFuture() {return new MyCompletableFuture<>();}@Overridepublic CompletableFuture<U> copy() {return new MyCompletableFuture<>();}
}public static void main(String[] args) throws Exception {MyCompletableFuture<String> future = new MyCompletableFuture<>();future.completeAsync(() -> {System.out.println(Thread.currentThread());return "hello world";}).thenAccept(System.out::println);future.join();
}
14.垃圾收集器
- G1 垃圾收集器成为默认垃圾收集器
- 背景:从 JDK 9 开始,G1(Garbage-First)垃圾收集器成为 32 位和 64 位服务器配置的默认垃圾收集器。
- 动机:限制 GC 暂停时间通常比最大化吞吐量更重要。G1 作为一个低暂停时间的收集器,相比面向吞吐量的并行 GC,能够为大多数用户提供更好的整体体验。
- 改进:
- 并行全GC:G1 在 JDK 9 中引入了并行化的全GC,多个线程可以同时工作,加快垃圾回收速度,减少 GC 暂停时间。
- 全GC的预测:G1 能够预测何时可能发生全GC,并提前进行准备工作,减少全GC的停顿时间。
- 改进的内存回收效率:优化了内存回收效率,尤其是在处理大对象和字符串时。
- 更好的自适应调整:改进了并发标记周期,更准确地调整 Young GC 和 Mixed GC 的频率和持续时间。
- 降低的内存占用:减少了一些内部数据结构的内存占用,降低了整体内存消耗。
- 改进的 G1 日志记录:提供了更多关于垃圾回收过程的信息,便于性能调优和问题诊断。
- CMS 垃圾收集器被标记为过时
- 背景:CMS(Concurrent Mark-Sweep)垃圾收集器在 JDK 9 中被标记为过时,计划在未来的主要版本中停止支持。
- 动机:减少 GC 代码库的维护负担,并加速新垃圾收集器的开发。G1 垃圾收集器旨在长期替代大多数 CMS 的使用场景。
- 移除JDK 8中已弃用的垃圾收集器(GC)组合。
这意味着以下GC组合不再存在:
- DefNew + CMS DefNew CMS
- DefNew:这是
Serial
收集器的新生代版本,采用复制算法,单线程执行。它会在垃圾回收时暂停所有用户线程(STW),适合单核处理器或对响应时间要求不高的场景。 - CMS:全称为 Concurrent Mark Sweep,是老年代的垃圾收集器,采用标记-清除算法。它可以在垃圾回收过程中与用户线程并发执行,从而减少停顿时间,适合对延迟敏感的应用。
- DefNew:这是
- ParNew + SerialOld ParNew SerialOld
- ParNew:这是
Serial
收集器的多线程版本,用于新生代的垃圾回收。它使用复制算法,并且可以利用多核处理器来提高回收效率。 - SerialOld:这是
Serial
收集器的老年代版本,采用标记-整理算法,单线程执行。
- ParNew:这是
- Incremental CMS 增量CMS
- Incremental CMS 是 Concurrent Mark Sweep (CMS) 垃圾回收器的一个变种,旨在减少垃圾回收过程中对应用程序性能的影响。它通过让垃圾回收线程和用户线程交替运行,尽量减少垃圾回收线程独占资源的时间。
相关文章:
JDK9新特性
文章目录 新特性:1.模块化系统使用模块化module-info.java:exports:opens:requires:provides:uses: 2.JShell启动Jshell执行计算定义变量定义方法定义类帮助命令查看定义的变量:/var…...
性能测试中的数据库连接池优化
目录 一、配置连接池参数 二、配置连接池数量 三、监控连接池 数据库连接池的意义是让连接复用,通过建立一个数据库连接池(缓冲区)以及一套连接的使用,分配,管理策略,使得该连接池中的连接可以得到高效&…...
1. 初识spark
背景: 作为一名开发人员,用内存处理数据是每天都在做的事情。内存处理数据最大的优势就是方便,快捷,可以很快得到结果,但是内存总是有瓶颈的,不管你运行代码的机器有多大的内存,总是有更大规模…...
专业学习|一文了解并实操自适应大邻域搜索(讲解代码)
一、自适应大邻域搜索概念介绍 自适应大邻域搜索(Adaptive Large Neighborhood Search,ALNS)是一种用于解决组合优化问题的元启发式算法。以下是关于它的详细介绍: -自适应大领域搜索的核心思想是:破坏解、修复解、动…...
Redis --- 使用zset处理排行榜和计数问题
在处理计数业务时,我们一般会使用一个数据结构,既是集合又可以保证唯一性,所以我们会选择Redis中的set集合: 业务逻辑: 用户点击点赞按钮,需要再set集合内判断是否已点赞,未点赞则需要将点赞数1…...
排序算法——快速排序
代码仓库: 1037827920/AlgorithmZoo 快速排序 算法步骤 选择基准元素,从数组中选择一个元素作为基准,通常选择方式有: 第一个元素最后一个元素中间元素随机选择 分区操作,将数组元素根据基准分为两部分,…...
有用的sql链接
『SQL』常考面试题(2——窗口函数)_sql的窗口函数面试题-CSDN博客 史上最强sql计算用户次日留存率详解(通用版)及相关常用函数 -2020.06.10 - 知乎 (zhihu.com) 1280. 学生们参加各科测试的次数 - 力扣(LeetCode&…...
手写MVVM框架-构建虚拟dom树
MVVM的核心之一就是虚拟dom树,我们这一章节就先构建一个虚拟dom树 首先我们需要创建一个VNode的类 // 当前类的位置是src/vnode/index.js export default class VNode{constructor(tag, // 标签名称(英文大写)ele, // 对应真实节点children,…...
C++单例模式
单例模式是一种设计模式,它保证一个类只有一个对象。因此单例模式要私有化构造函数,禁用拷贝构造以及赋值重载。同时还要提供一个静态成员函数获取单例对象。 单例模式有两种实现方式:饿汉模式和懒汉模式 饿汉模式:创建静态单例…...
SQL入门到精通 理论+实战 -- 在 MySQL 中学习SQL语言
目录 一、环境准备 1、MySQL 8.0 和 Navicat 下载安装 2、准备好的表和数据文件: 二、SQL语言简述 1、数据库基础概念 2、什么是SQL 3、SQL的分类 4、SQL通用语法 三、DDL(Data Definition Language):数据定义语言 1、操…...
RabbitMQ 可靠性投递
文章目录 前言一、RabbitMQ自带机制1、生产者发送消息注意1.1、事务(Transactions)1.2、发布确认(Publisher Confirms)1.2.1、同步1.2.2、异步 2、消息路由机制2.1、使用备份交换机(Alternate Exchanges)2.…...
Java常见的技术场景面试题
一、单点登录这块怎么实现的? 单点登录概述 单点登录:Single Sign On(简称SSO),只需要登录一次,就可以访问所有信任的应用系统 在以前的时候,一般我们就单系统,所有的功能都在同一个系统上。…...
使用 Postman 进行 API 测试:从入门到精通
使用 Postman 进行 API 测试:从入门到精通 使用 Postman 进行 API 测试:从入门到精通一、什么是 API 测试?二、Postman 简介三、环境搭建四、API 测试流程1. 收集 API 文档2. 发送基本请求示例:发送 GET 请求示例代码(…...
用python实现进度条
前言 在Python中,可以使用多种方式实现进度条。以下是几种常见的进度条格式的实现方法: 1. 使用 tqdm 库 tqdm 是一个非常流行的库,可以轻松地在循环中显示进度条。 from tqdm import tqdm import time# 示例:简单的进度条 fo…...
android 自定义通话录音
在 Android 开发中,实现通话录音功能通常涉及到对系统通话的拦截和录音。由于通话录音涉及到用户隐私和安全性,Android 系统对此有严格的限制和要求。在 Android 10(API 级别 29)及以上版本中,直接访问通话录音功能变得…...
WebSocket——环境搭建与多环境配置
一、前言:为什么要使用多环境配置? 在开发过程中,我们通常会遇到多个不同的环境,比如开发环境(Dev)、测试环境(Test)、生产环境(Prod)等。每个环境的配置和需…...
【自动化办公】批量图片PDF自定义指定多个区域识别重命名,批量识别铁路货物运单区域内容改名,基于WPF和飞桨ocr深度学习模型的解决方案
项目背景介绍 铁路货运企业需要对物流单进行长期存档,以便后续查询和审计。不同的物流单可能包含不同的关键信息,通过自定义指定多个区域进行识别重命名,可以使存档的图片文件名具有统一的规范和明确的含义。比如,将包含货物运单…...
在线教程丨YOLO系列10年更新11个版本,最新模型在目标检测多项任务中达SOTA
YOLO (You Only Look Once) 是计算机视觉领域中最具影响力的实时目标检测算法之一,以其高精度与高效性深受业界青睐,广泛应用于自动驾驶、安防监控、医疗影像等领域。 该模型最早于 2015 年由华盛顿大学研究生 Joseph Redmon 发布,开创了将目…...
c++可变参数详解
目录 引言 库的基本功能 va_start 宏: va_arg 宏 va_end 宏 va_copy 宏 使用 处理可变参数代码 C11可变参数模板 基本概念 sizeof... 运算符 包扩展 引言 在C编程中,处理不确定数量的参数是一个常见的需求。为了支持这种需求,C标准库提供了 &…...
Ubuntu安装VMware17
安装 下载本文的附件,之后执行 sudo chmod x VMware-Workstation-Full-17.5.2-23775571.x86_64.bundle sudo ./VMware-Workstation-Full-17.5.2-23775571.x86_64.bundle安装注意事项: 跳过账户登录的办法:断开网络 可能出现的问题以及解决…...
在Debian 12上安装VNC服务器
不知道什么标题 可以看到这个文章是通过豆包从国外网站copy的,先这样写着好了,具体的我有时间再补充,基本内容都在这里了。 在Debian 12上安装VNC服务器 简介 VNC(Virtual Network Computing,虚拟网络计算…...
设计模式Python版 外观模式
文章目录 前言一、外观模式二、外观模式示例三、抽象外观类四、抽象外观类示例 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式&am…...
Selenium 浏览器操作与使用技巧——详细解析(Java版)
目录 一、浏览器及窗口操作 二、键盘与鼠标操作 三、勾选复选框 四、多层框架/窗口定位 五、操作下拉框 六、上传文件操作 七、处理弹窗与 alert 八、处理动态元素 九、使用 Selenium 进行网站监控 前言 Selenium 是一款非常强大的 Web 自动化测试工具,能够…...
论文解读:《基于TinyML毫米波雷达的座舱检测、定位与分类》
摘要 本文提出了一种实时的座舱检测、定位和分类解决方案,采用毫米波(mmWave)雷达系统芯片(SoC),CapterahCAL60S344-AE,支持微型机器学习(TinyML)。提出了波束距离-多普勒…...
e2studio开发RA2E1(5)----GPIO输入检测
e2studio开发RA2E1.5--GPIO输入检测 概述视频教学样品申请硬件准备参考程序源码下载新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置GPIO口配置按键口配置按键口&Led配置R_IOPORT_PortRead()函数原型R_IOPORT_PinRead()函数原型代码 概述 本篇文章主要介绍如何…...
数据结构:队列篇
图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…...
idea中git的简单使用
提交,推送直接合并 合到哪个分支就到先切到哪个分支...
Java中的object类
1.Object类是什么? 🟪Object 是 Java 类库中的一个特殊类,也是所有类的父类(超类),位于类继承层次结构的顶端。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。 🟦Java里面除了Object类,所有的…...
html2canvas绘制页面并生成图像 下载
1. 简介 html2canvas是一个开源的JavaScript库,它允许开发者在用户的浏览器中直接将HTML元素渲染为画布(Canvas),并生成图像。以下是对html2canvas的详细介绍: 2. 主要功能 html2canvas的主要功能是将网页中的HTML元…...
Certum OV企业型通配符SSL
随着网络攻击手段的不断演变,仅仅依靠HTTP协议已无法满足现代企业对数据安全的需求。SSL证书,特别是经过严格验证的组织验证型SSL证书,成为了保护网站数据传输安全、提升用户信任度的标配。 一、Certum OV企业型通配符SSL概述 Certum&#…...
2024年Web前端最新Java进阶(五十五)-Java Lambda表达式入门_eclipse lambda(1),面试必备
对象篇 模块化编程-自研模块加载器 开源分享:【大厂前端面试题解析核心总结学习笔记真实项目实战最新讲解视频】 Arrays.sort(players, sortByName); // 1.3 也可以采用如下形式: Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2))); ??其…...
JVM 四虚拟机栈
虚拟机栈出现的背景 由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多…...
V103开发笔记1-20250113
2025-01-13 一、应用方向分析 应用项目: PCBFLY无人机项目(包括飞控和手持遥控器); 分析移植项目,应用外设资源包括: GPIO, PWM,USART,GPIO模拟I2C/SPI, ADC,DMA,USB等; 二、移植项目的基本…...
Page Assist - 本地Deepseek模型 Web UI 的安装和使用
Page Assist Page Assist是一个开源的Chrome扩展程序,为本地AI模型提供一个直观的交互界面。通过它可以在任何网页上打开侧边栏或Web UI,与自己的AI模型进行对话,获取智能辅助。这种设计不仅方便了用户随时调用AI的能力,还保护了…...
Cookie及Session---笔记
目录 Cookiecookie简介cookiesession的认证方式tpshop完整登录实现-cookie Sessionsession简介session自动管理cookietpshop完整登录实现-sessioncookie和session的区别获取响应结果指定内容 Cookie cookie简介 工程师针对HTTP协议是无连接无状态特性所设计的一种技术&#x…...
【Block总结】DASI,多维特征融合
论文信息 HCF-Net(Hierarchical Context Fusion Network)是一种新提出的深度学习模型,专门用于红外小目标检测。该论文于2024年3月16日发布,作者包括Shibiao Xu、ShuChen Zheng等,主要研究机构为北京邮电大学。该模型…...
LabVIEW的智能电源远程监控系统开发
在工业自动化与测试领域,电源设备的精准控制与远程管理是保障系统稳定运行的核心需求。传统电源管理依赖本地手动操作,存在响应滞后、参数调节效率低、无法实时监控等问题。通过集成工业物联网(IIoT)技术,实现电源设备…...
4.PPT:日月潭景点介绍【18】
目录 NO1、2、3、4 NO5、6、7、8 NO9、10、11、12 表居中或者水平/垂直居中单元格内容居中或者水平/垂直居中 NO1、2、3、4 新建一个空白演示文稿,命名为“PPT.pptx”(“.pptx”为扩展名)新建幻灯片 开始→版式“PPT_素材.doc…...
《迪拜AI展:探寻中东人工智能发展的璀璨新篇》
迪拜:AI 浪潮下的闪耀明珠 迪拜,这座位于阿拉伯半岛东部、波斯湾东南岸的城市,犹如一颗璀璨的明珠,在中东地区散发着独特的魅力。它是阿拉伯联合酋长国的第二大城市,也是迪拜酋长国的首府 ,凭借优越的地理位…...
axios如何利用promise无痛刷新token
目录 需求 需求解析 实现思路 方法一: 方法二: 两种方法对比 实现 封装axios基本骨架 instance.interceptors.response.use拦截实现 问题和优化 如何防止多次刷新token 同时发起两个或以上的请求时,其他接口如何重试 最后完整代…...
R语言 | 使用 ComplexHeatmap 绘制热图,分区并给对角线分区加黑边框
目的:画热图,分区,给对角线分区添加黑色边框 建议直接看0和4。 0. 准备数据 # 安装并加载必要的包 #install.packages("ComplexHeatmap") # 如果尚未安装 library(ComplexHeatmap)# 使用 iris 数据集 #data(iris)# 选择数值列&a…...
TensorFlow 简单的二分类神经网络的训练和应用流程
展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括: 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中,数据准备是通过两个 Numpy 数…...
蓝桥杯备考:模拟算法之字符串展开
P1098 [NOIP 2007 提高组] 字符串的展开 - 洛谷 | 计算机科学教育新生态 #include <iostream> #include <cctype> #include <algorithm> using namespace std; int p1,p2,p3; string s,ret; void add(char left,char right) {string tmp;for(char ch left1;…...
[创业之路-282]:《产品开发管理-方法.流程.工具 》-1- 优秀研发体系的特征、IPD关注的4个关键要素、IPD体系的7个特点
目录 一、优秀研发体系的特征 二、IPD关注的4个关键要素 1. 组织管理 2. 市场管理 3. 流程管理 4. 产品管理 三、IPD体系的7个特点 1、产品开发是投资行为: 2、基于市场的产品研发: 3、平台化开发,大平台,小产品&#x…...
Node.js 与 PostgreSQL 集成:深入 pg 模块的应用与实践
title: Node.js 与 PostgreSQL 集成:深入 pg 模块的应用与实践 date: 2025/2/5 updated: 2025/2/5 author: cmdragon excerpt: 随着 JavaScript 在服务器端编程中的兴起,Node.js 已成为构建高性能网络应用程序的重要平台。PostgreSQL 则以其强大的特性以及对复杂数据结构的…...
【Uniapp-Vue3】从uniCloud中获取数据
需要先获取数据库对象: let db uniCloud.database(); 获取数据库中数据的方法: db.collection("数据表名称").get(); 所以就可以得到下面的这个模板: let 函数名 async () > { let res await db.collection("数据表名称…...
LeetCode 0090.子集 II:二进制枚举 / 回溯
【LetMeFly】90.子集 II:二进制枚举 / 回溯 力扣题目链接:https://leetcode.cn/problems/subsets-ii/ 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。 解集 不能 …...
Pytest+selenium UI自动化测试实战实例
今天来说说pytest吧,经过几周的时间学习,有收获也有疑惑,总之最后还是搞个小项目出来证明自己的努力不没有白费。 环境准备 1 确保您已经安装了python3.x 2 配置python3pycharmselenium2开发环境 3 安装pytest库pip install p…...
黑马点评 - 商铺类型缓存练习题(Redis List实现)
首先明确返回值是一个 List<ShopType> 类型那么我们修改此函数并在 TypeService 中声明 queryTypeList 方法,并在其实现类中实现此方法 GetMapping("list")public Result queryTypeList() {return typeService.queryTypeList();}实现此方法首先需要…...
C++ 创建和配置dll与lib库
C简明教程(13)创建和配置dll与lib库_怎样生成lib库和dll库-CSDN博客 C 动态库与静态库详解 一、为什么要引入库的概念 在 C 编程中,随着项目规模的不断扩大,代码量也会急剧增加。如果将所有代码都写在一个源文件中,…...