简介
JDK9新特性 ^3
模块系统(Jigsaw)
- 在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小
- Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来
- 示例
1 | // jdk9_module1 下 module-info.java |
JShell交互式编程环境
- 增加了REPL(Read-Eval-Print Loop)工具
jshell
,在%JAVA_HOME%/lib目录
接口私有方法
1 | interface Logging { |
VarHandle变量句柄
1 | public class T1_VarHandle { |
JDK8新特性 ^1
Lambda表达式
- 原理 ^4
- 在类编译时,会生成一个私有静态方法+一个内部类
- 在内部类中实现了函数式接口,在实现接口的方法中,会调用上述编译器生成的静态方法
- 在使用lambda表达式的地方,通过传递内部类实例,来调用函数式接口方法
- 无法使用continue/break语句,只能使用return(相当于continue)语句
- 作用域:可以直接访问标记了final的外层局部变量,或者成员变量以及静态变量
- 变量捕获和非捕获
- Lambda表达式中的异常无法通过外层方法抛出
1 | list.forEach(item -> { |
方法引用(::)
方法引用
或者说双冒号运算
对应的参数类型是Function<T, R>
,T表示传入类型,R表示返回类型。基于Lambda表达式实现- 比如表达式
person -> person.getAge();
传入参数是person,返回值是person.getAge(),那么方法引用Person::getAge
就对应着Function<Person,Integer>
类型 - 示例
- Class::new
- Class::static_method
- Class::method
- instance::method
使用案例
1
2
3
4
5
6
7
8private String getUserInfo(User user, Function<User, Object> func) {
Object value = func.apply(user); // 如果参数为 Function<User, String> 则返回类型为 String
return value;
}
// 调用
Object username = getUserInfo(user, User::getUsername);
Object password = getUserInfo(user, User::getPassword);
接口默认方法
1 | interface Formula { |
函数式接口(方法回调)
- 可以是内部类
- 每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。默认方法不算抽象方法,所以可以给函数式接口添加默认方法
- 增加
@FunctionalInterface
注解是为了告诉编译器此接口只能有一个抽象方法
1 | // 如果没有指定,下面的代码也是对的,只不过编译器不会检查 |
Streams
- Java 8 中的
Stream
是对集合(Collection)对象功能的增强。java.util.stream
- Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如”过滤掉长度大于 10 的字符串”、”获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换
- Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返
- 而和迭代器又不同的是,Stream 可以并行化操作(依赖于 Java7 中引入的 Fork/Join 框架),迭代器只能命令式地、串行化操作
- 常用API
Intermediate
(中间操作)- 一个流可以后面跟随零个或多个 intermediate 操作
filter
(子句需返回true|false)、map
(字句返回值作为后续条目)、peek
(全部循环,无需返回值,可用于打印和修改条目)、distinct、sorted、limit、skip、parallel、sequential unordered- mapToInt、flatMap 等
Terminal
(最终遍历)- 一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用”光”了,无法再被操作(此时进行遍历)
- collect、forEach、reduce、anyMatch(循环判断每一条目,只有有一个符合就整体返回true)、allMatch、findFirst
- toArray、forEachOrdered、noneMatch、findAny、iterator、min、max、count
Short-circuiting
(分流/过滤)- 对于一个无限大的Stream时,需要获取返回一个有限的新Stream或快速计算出值时需要进行过滤
- anyMatch、allMatch、noneMatch、findFirst、findAny、limit
- 简单示例
1 | // ## 构造流 |
Date/Time API(JSR 310)
- 参考java-base.md#时间
- 使用 LocalDateTime 替代 Date。JDK 中的 Date 的缺点:打印Date可读性差,SimpleDateFormat格式化时线程不安全等
- LocalDate、LocalTime、LocalDateTime(本地日期时间,不含时区,只是说日期格式展示本地化)、Instant(无时区信息,瞬时实例)、ZonedDateTime(含时区信息) 为不可变对象,修改这些对象对象会返回一个副本,即生成并返回一个新对象
1 | // Clock 类。可以替代System.currentTimeMillis()和TimeZone.getDefault() |
Nashorn JavaScript引擎
- Java 8提供了新的Nashorn JavaScript引擎,使得我们可以在JVM上开发和运行JS应用。Nashorn JavaScript引擎是javax.script.ScriptEngine的另一个实现版本,这类Script引擎遵循相同的规则,允许Java和JavaScript交互使用
- 参考下文javax.script(js-java))
- Nashorn引擎命令行工具:
jjs
,可以接受js源码并执行,如jjs func.js
1 | ScriptEngineManager manager = new ScriptEngineManager(); |
Base64
- 对Base64编码的支持已经被加入到Java 8官方库中,这样不需要使用第三方库就可以进行Base64编码
1 | Base64.getEncoder().encodeToString(str); // 编码 |
@Repeatable重复注解
- 可让在一个类上重复使用同一注解(此注解被@Repeatable注解过)
并行数组
- 最重要的方法是
Arrays.parallelSort()
,可以显著加快多核机器上的数组排序
并发性
- LongAdder 使用了分段锁
- DoubleAdder
Metaspace代替持久代PermGen space
其他
1 | // 类似get,区别在于如果map中没有对应key则返回执行函数,将此函数返回值put到此key上,并返回 |
JDK7新特性
try-with-resources
JDK6
javax.script(js-java)
- 从 JDK 1.8 开始,Nashorn取代Rhino(JDK 1.6, JDK1.7) 成为 Java 的嵌入式 JavaScript 引擎。Nashorn 完全支持 ECMAScript 5.1 规范以及一些扩展
- Nashorn JavaScript Engine 在 Java 11 标记为forRemoval,在 Java 15 已经不可用了(manager.getEngineByName(“javascript”)返回值为null)。可同引入依赖解决
- 参考文章
- Java 8 Nashorn 指南:https://zhuanlan.zhihu.com/p/33257346
- Java和Js之间相互调用,无需安装依赖,相似的如JEXL执行字符串JAVA代码
1 | <dependency> |
- 案例
1 |
|
- Nashorn案例
1 | // 进行Debug(js必须是文件才能在idea中进行调试): https://www.cnblogs.com/shirui/p/9430804.html |
未知
package-info.java
- 是一个Java文件,可以放到任意Java源码包执行。不过里面的内容有特定的要求,其主要目的是为了提供包级别相关的操作,比如包级别的注解、注释及公共变量
- 提供包级别的注解
1 | // 创建包注解 |
- 提供包级别的变量
1 | // package-info.java文件内容 |
- 提供包级别的注释
- 使用JavaDoc的时候,通过在package-info.java添加注释,生成JavaDoc实现对应包的注释说明
SPI
SPI
(Service Provider Interface) 是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。从使用人员上来说,SPI 被框架扩展人员使用 ^5- 核心类
java.util.ServiceLoader
- 使用
- 主项目中定义接口/抽象类
cn.aezo.test.IService
- 插件开发者,在插件项目resources目录下新建
META-INF/services
目录,然后在这个目录下新建cn.aezo.test.IService
文件(此文件最终以jar包等形式放到classpath下),并在这个文件中写入实现的类名cn.aezo.test.IServiceImpl
(多个可换行写入) - 将插件jar放到主项目的classpath目录,主项目通过
ServiceLoader<IService> service = ServiceLoader.load(IService.class);
就可以加载实现类了
- 主项目中定义接口/抽象类
- dubbo作为一个高度可扩展的rpc框架,也依赖于java的SPI,并且dubbo对java原生的spi机制作出了一定的扩展,使得其功能更加强大
- Pf4j插件框架也是基于java的SPI实现
参考文章