一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
前言
今天本来想写一下ES搜索的文章来着,但是在使用Spring Boot的部分配置项的时候发现配置过期了。所以暂时先不写ES了。相信很多同学用的java版本都是1.8版本的。现在已经出来18版本了所以我就把一些常用的JAVA9的特性总结一下
- 注意:以下代码有个Sout.show方法,源码如下
public class Sout {
public static void show(Object object) {
System.out.println(object);
}
public static void show(String format, Object... args) {
System.out.println(String.format(format, args));
}
}
新特性
java模块系统
java 9 最大的变化之一是引入了模块系统(Jigsaw 项目)。
-
模块的定义:代码和数据的封装体。
-
模块的具体实现:模块的代码被组织成多个包,每个包中包含Java类和接口;模块的数据则包括资源文件和其他静态信息。 Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。 示例如下:
-
个人理解:
- 同:多模块开发模式、微服务来发模式、DDD开发模式 java9的模块系统我在使用的时候感觉和上边三种的开发模式差不多,与其说差不多倒不如说可以一起使用。我们在java8采用微服务开发的时候,在经典的订单、商品、库存三个服务下可以各自添加自己的module-info文件。定义开放的方法等信息
- 异 前段时间有个比较出名的面试题很活跃:大体的意思是虽然我的变量、方法是private修饰的,但是还是可以通过反射来获取到。我们不讨论这个面试题,但是我们可以用module-info来限制某些方法属于包内可用,包外不可用,即可解决这些反射和访问权限控制粒度不足的问题。
-
使用方法
module 本模块的名称{ exports 对外暴露的包路径; requires 需要依赖的其他模块名称; }如果写js es6比较多的同学可能会感到莫名的熟悉感,这个和export语法简直不要太相同
新的集合工厂方法
Java9提供了以下新的静态方法来创建集合,终于不用Array.asList了(虽然真的挺好用),下面这些方法可以更简洁的来创建集合
Set<Integer> set = Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
List<String> list = List.of("1", "2", "3", "4", "5");
Map<String, Object> map = Map.of("name", "lmh", "age", 20);
// map的属性如果超过10,需要使用下边这个方法
Map<String, Object> bigMap = Map.ofEntries(new AbstractMap.SimpleEntry<>("A1", "Apple"),
new AbstractMap.SimpleEntry<>("A2", "Apple"), new AbstractMap.SimpleEntry<>("A3", "Apple"),
new AbstractMap.SimpleEntry<>("A4", "Apple"), new AbstractMap.SimpleEntry<>("A5", "Apple"),
new AbstractMap.SimpleEntry<>("A6", "Apple"), new AbstractMap.SimpleEntry<>("A7", "Apple"),
new AbstractMap.SimpleEntry<>("A8", "Apple"), new AbstractMap.SimpleEntry<>("A9", "Apple"),
new AbstractMap.SimpleEntry<>("A10", "Apple"), new AbstractMap.SimpleEntry<>("A11", "Apple"),
new AbstractMap.SimpleEntry<>("A12", "Apple"), new AbstractMap.SimpleEntry<>("A13", "Apple"),
new AbstractMap.SimpleEntry<>("A14", "Apple"), new AbstractMap.SimpleEntry<>("A15", "Apple"));
Sout.show(set);
Sout.show(list);
Sout.show(map);
Sout.show(bigMap);
私有接口方法
Java 9 不仅像 Java 8 一样支持接口默认方法,同时还支持私有方法。 在 Java 9 中,一个接口中能定义如下几种变量/方法:
- 常量
- 抽象方法
- 默认方法
- 静态方法
- 私有方法
- 私有静态方法 例如下边这个代码的示例
class MysqlFactory implements SqlLog {
@Override
public void getEnv() {
System.out.println("mysql");
}
}
class OracleFactory implements SqlLog {
@Override
public void getEnv() {
System.out.println("oracle");
}
}
/**
* 在java9中,一个接口能定义如下几种变量/方法 1:常量 2:抽象方法 3:默认方法 4:静态方法 5:私有方法 6:私有静态方法
*/
interface SqlLog {
/**
* 常量
*/
static String PROJECT_NAME = "ipm";
/**
* 抽象方法
*/
void getEnv();
/**
* 默认方法
*
* @param msg
*/
default void logInfo(String msg) {
log(msg, "INFO");
}
/**
* 静态方法
*
* @param pass
*/
static void setDbPass(String pass) {
System.out.println("密码为:" + pass);
}
String ORACLE = "Oracle_Db";
String MYSQL = "mysql_Db";
/**
* 私有方法
*
* @param msg
* @param prefix
*/
private void log(String msg, String prefix) {
getConnection();
System.out.println(prefix + msg);
closeConnect();
}
/**
* 私有静态方法
*/
private static void getConnection() {
System.out.println("Open Db Conn");
}
/**
* 私有静态方法
*/
private static void closeConnect() {
System.out.println("Close Db Conn");
}
}
不得不说interface越升级越复杂了,感觉离开始的的设计初衷越走越远了,这里大家安装实际情况使用吧
改进的进程 API
Java 9 向 Process API 添加了一个名为 ProcessHandle 的接口来增强 java.lang.Process 类。 ProcessHandle 接口的实例标识一个本地进程,它允许查询进程状态并管理进程。 ProcessHandle 接口中声明的 onExit() 方法可用于在某个进程终止时触发某些操作。
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
String np = "Not Present";
Process p = pb.start();
ProcessHandle.Info info = p.info();
CompletableFuture<Process> cf = p.onExit();
Sout.show("Process ID : %s", p.pid());
Sout.show("Command name : %s", info.command().orElse(np));
Sout.show("Command line : %s", info.commandLine().orElse(np));
Sout.show("Start time: %s",
info.startInstant().map(i -> i.atZone(ZoneId.systemDefault()).toLocalDateTime().toString()).orElse(np));
Sout.show("Arguments : %s",
info.arguments().map(a -> Stream.of(a).collect(Collectors.joining(" "))).orElse(np));
Sout.show("User : %s", info.user().orElse(np));
var doneFlag = false;
do {
doneFlag = cf.isDone();
} while (!doneFlag);
Sout.show(doneFlag);
这个算是一个优化吧,不过对于平常的工作中,对于进程的使用不是很多。吐槽一句,看进程的优化不如去好好看一下多线程及线程池呢。大家也可以在升级jdk17(长时间支持的版本)之后加上这个进程补丁
stream 新方法
- takeWhile 方法 takeWhile() 方法使用一个断言作为参数,返回给定 Stream 的子集直到断言语句第一次返回 true。如果没有满足断言条件为true的元素,将返回一个空的 Stream。 takeWhile() 方法在有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素;在无序的 Stream 中,takeWhile 返回从开头开始的符合 Predicate 要求的元素的子集。
- dropWhile 方法 dropWhile 方法和 takeWhile 作用相反的,使用一个断言作为参数,直到断言语句第一次返回 false 才返回给定 Stream 的子集。
- iterate方法 方法允许使用初始种子值创建顺序(可能是无限)流,并迭代应用指定的下一个方法。 当指定的 hasNext 的 predicate 返回 false 时,迭代停止。
- ofNullable方法 ofNullable 方法可以预防 NullPointerExceptions 异常, 可以通过检查流来避免 null 值。 如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。
List<String> strList = List.of("1", "2", "3", "4", "5", "", "6", "7", "8", "9");
strList.stream().takeWhile(item -> !item.isEmpty()).forEach(Sout::show);
Sout.show("123123");
strList.stream().dropWhile(item -> !item.isEmpty()).forEach(Sout::show);
Sout.show("456456");
IntStream.iterate(3, x -> x < 10, x -> x + 3).forEach(Sout::show);
Sout.show("789789");
long count = Stream.ofNullable(100).count();
Sout.show(count);
count = Stream.ofNullable(null).count();
Sout.show(count);
大家可以多自己实践一下,感觉比较实用
try-with-resources
try-with-resources 声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。
class AutoClose implements AutoCloseable {
@Override
public void close() throws IOException {
Sout.show("触发关闭");
}
public String toUpper(String str) {
return str.toUpperCase();
}
}
/**
* java9 优化为不必声明新变量即可接收参数,但是参数必须为final或隐式final
*/
@Test
public void demo2() {
String str = "abc";
AutoClose ac = new AutoClose();
try (ac) {
str = ac.toUpper(str);
Sout.show("执行业务逻辑中");
} catch (Exception e) {
e.printStackTrace();
}
Sout.show(str);
}
这里感觉要在升级之后和同事做技术分享,否则会出现非final或非隐式final一直未关闭的情况
@Deprecated改进
Java 9 中注解增加了两个新元素:since 和 forRemoval
-
since: 元素指定已注解的API元素已被弃用的版本
-
forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API
/** * 2021-07-14之后过时,且将在未来的版本会删除 */ @Deprecated(since = "2021-07-14", forRemoval = true) class DeprecatedAnnoOne { } class DeprecatedAnnoTwo { }
一般写在注释上的说明可以放在这里了,起提醒的作用比较多一点
钻石操作符
钻石操作符是在 java 7 中引入的,可以让代码更易读,但它不能用于匿名的内部类。 在 java 9 中, 它可以与匿名的内部类一起使用,从而提高代码的可读性
public class DiamondOperatorTest {
/**
* 1.9之前的写法
*/
@Test
public void demo1() {
BaseDiamondOperatorHandler<Integer> intHandle = new BaseDiamondOperatorHandler<Integer>(1) {
@Override
void handle() {
Sout.show(content);
}
};
BaseDiamondOperatorHandler<Double> doubleHandle = new BaseDiamondOperatorHandler<Double>(2.22) {
@Override
void handle() {
Sout.show(content);
}
};
BaseDiamondOperatorHandler<String> strHandle = new BaseDiamondOperatorHandler<String>("Hello") {
@Override
void handle() {
Sout.show(content);
}
};
intHandle.handle();
doubleHandle.handle();
strHandle.handle();
}
/**
* 1.9及之后的写法
*/
@Test
public void demo2() {
BaseDiamondOperatorHandler<?> intHandle = new BaseDiamondOperatorHandler<>(1) {
@Override
void handle() {
Sout.show(content);
}
};
BaseDiamondOperatorHandler<?> doubleHandle = new BaseDiamondOperatorHandler<>(2.22) {
@Override
void handle() {
Sout.show(content);
}
};
BaseDiamondOperatorHandler<?> strHandle = new BaseDiamondOperatorHandler<>("Hello") {
@Override
void handle() {
Sout.show(content);
}
};
intHandle.handle();
doubleHandle.handle();
strHandle.handle();
}
}
abstract class BaseDiamondOperatorHandler<T> {
public T content;
public BaseDiamondOperatorHandler(T content) {
this.content = content;
}
/**
* handle处理
*/
abstract void handle();
}
这里感觉是类型推导的前身,在java9有这个特性之后java10马上出了局部变量类型推导
Optional 类
在 java 9 中, 添加了三个方法来改进optional的功能:
- stream() stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的Stream,否则返回一个空的 Stream(Stream.empty())
Sout.show(Optional.empty().stream().count());
- ifPresentOrElse() ifPresentOrElse 方法的用途是,如果一个 Optional 包含值,则对其包含的值调用函数 action,即action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse还有第二个参数 emptyAction —— 如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即emptyAction.run()。如果值存在,返回 Optional 指定的值,否则返回一个预设的值。
Optional.of(10086).ifPresentOrElse(Sout::show, () -> Sout.show("not present"));
- or() or如果值存在,返回 Optional 指定的值,否则返回一个预设的值。
Supplier<Optional<String>> supplierOptional = () -> Optional.of("Not Present");
Optional.empty().or(supplierOptional).ifPresent(item->Sout.show(item));
结语
java9的大部分新特性总计如上,由于这些代码我编写的比较早,一些知识点也是参考的别人的技术文档,所以可能有遗漏和不足。欢迎留言交流
- 下期预告:spring boot初步集成es8.x版本进行增删查操作。欢迎关注!