Java8 新特性
1. 接口新特性
Java 接口存在的问题是当接口修改时,实现类也必须修改,为了解决接口的修改与现有实现类不兼容的问题,接口中的方法可以用 default 或者 static 修饰。
default 修饰的方法属于实例方法,有自己的方法体,它可以用 this 调用,可以被子类继承、重写,当类同时实现具有签名相同的default方法的两个不同接口时,这个方法必须要被重写。
static 修饰的方法,它也有自己的方法体,它的使用和类的静态方法一样,它不能被子类继承、重写,只能使用 interfaceName.methodName() 的方式进行调用。
1.1 Java8 中的接口和抽象类有什么区别?
主要区别是:
- 接口可以多个实现,抽象类只能单继承;
- 接口的方法是 public abstract 修饰的,变量是 public static final 修饰的。抽象类 可以使用其他修饰符。
接口新增 default 和 static 关键字是为了解决接口修改与现有的实现不兼容的问题,不是为了替代抽象类,因此接口和抽象类在使用上还是不变。
2. 函数式接口
函数式接口也称为单抽象方法接口,也就是函数式接口有且只有一个抽象方法,但是可以有多个非抽象方法。
函数式接口可以没有 @FunctionalInterface 注解, @FunctionalInterface 注解只是在编译期起到强制规范定义的作用。
3. Lambda 表达式
Lambda 表达式可以使代码变得更加简洁紧凑,让 Java 能支持简单的函数式编程。 Lambda 表达式是一个匿名函数,Java8 允许把函数作为参数传递进方法中。
Lambda 带来的便利有:
- 代替匿名内部类:这些可以代替的匿名内部类要求必须是函数式接口,也就是说只有一个抽象方法需要被重写。
- 集合迭代:对于集合可以使用 Lambda 表达式进行迭代。
- 方法引用:可以使用
::关键字来传递方法或者构造函数的引用。类似Person p = Person::new的写法,本质是将构造函数的引用传递给了方法。
4. Stream
Stream 类似之前的流,它不存储数据,它可以检索和逻辑处理集合数据,包括:筛选、排序、统计、计数等。它的数据源可以是 Collection、Array 等。
Stream 分为串行流,和 ParallelStream 并行流。
Stream 的延迟执行,在执行返回 Stream 类型的方法时,不会立即执行,而会等一个返回非 Stream 类型的方法后才会执行。 下面的方法会先打印:"execute count",然才会循环打印 "filter:" + e 的内容。
public static void main(String[] args) {
List<Integer> list = List.of(1,2);
Stream<Integer> filter = list.stream().filter(e -> {
System.out.println("filter:" + e);
return true;
});
System.out.println("execute count");
long count = filter.count();
}
5. Optional
Optional 主要用来处理判断空和 null 的问题。
比如:Optional.ofNullable(zoo).map(z -> z.getDog()).map(d -> d.getAge()).ifPresent(age -> System.out.println(age)); 我们无需判断 zoo、z、d 变量是否为空或者 null。
我们可以使用 Optional.empty()、 Optional.of(T value)、Optional.ofNullable(T value)等方法创建 Optional 对象。
Optional 的 flatMap() 函数可以把 map() 函数的结果展开。即对于 List<List<String>> 通过 map() 变化后,得到的是 List<List<String>>,通过 flatMap() 变换后,可以得到 List<String>。
日期-时间(Date-Time) api
新的 Date-Time api 解决了 Date 类的大部分痛点:
- 非线程安全
- 时区处理麻烦
- 各种格式化、时间计算繁琐
- 设计缺陷:Date 类同时包含日期和时间,并且还有一个 java.sql.Date 类,容易混淆。
java.time 的主要类:
LocalDateTime: 日期 + 时间,格式为:yyyy-MM-ddTHH:mm:ss.SSS
LocalDate: 日期,格式为:yyyy-MM-dd
LocalTime: 时间,格式为:HH:mm:ss
对于上面三个类的格式化:
LocalDate 和 LocalTime 的 toString() 方法提供了默认的格式 yyyy-MM-dd 和 HH:mm:ss。
LocalDateTime 可以使用以下方法进行格式化:
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateTimeStr = dateTime.format(formatter);
字符串/数字 转日期格式:
LocalDateTime、LocalDate、LocalTime 可以使用对应类的 of 静态方法将数字转为日期。也可以使用对应的 parse 方法将符合格式的字符串转为对应的类型,例子:LocalDate.of(2021, 1, 26),LocalDateTime.parse("2021-01-26 12:12:22")。
jdbc 的 时间-日期 与 Java8 的 日期-时间 对应:
Date ---> LocalDate
Time ---> LocalTime
Timestamp ---> LocalDateTime
而之前统统对应 Date,也只有 Date。
时区问题:
Java8 引入了 ZonedDateTime 来表示带有时区的时间。
//当前时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("当前时区时间: " + zonedDateTime);
//东京时间
ZoneId zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
ZonedDateTime tokyoTime = zonedDateTime.withZoneSameInstant(zoneId);
System.out.println("东京时间: " + tokyoTime);
// ZonedDateTime 转 LocalDateTime
LocalDateTime localDateTime = tokyoTime.toLocalDateTime();
System.out.println("东京时间转当地时间: " + localDateTime);
//LocalDateTime 转 ZonedDateTime
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.systemDefault());
System.out.println("本地时区时间: " + localZoned);
//打印结果
// 当前时区时间: 2021-01-27T14:43:58.735+08:00[Asia/Shanghai]
// 东京时间: 2021-01-27T15:43:58.735+09:00[Asia/Tokyo]
// 东京时间转当地时间: 2021-01-27T15:43:58.735
// 当地时区时间: 2021-01-27T15:53:35.618+08:00[Asia/Shanghai]
Java9 新特性
1. JShell
提供了类似 Python 的实时命令行交互工具。
JShell 的一些特点:
- 它可以处理简单的小逻辑、验证简单的小问题,它比 IDE 更有效率。
- 代码语句输入完成后,JShell 可以立即返回执行结果,而不再需要编辑器、编译器、解释器。
- 支持重复变量声明,后面声明的变量会覆盖前面声明的变量。
- 支持独立的表达式计算,比如: 1 + 1。
2. 模块化系统
一个模块可以看作是:一组唯一命名的、可重用的包、资源和模块描述文件(module-info.java)
任意一个 jar 文件,加上一个模块描述文件,就可以升级为一个模块。
引入模块系统之后, jdk 被重新组织成了 94 个模块。 Java 应用可以通过新增的 jlink 工具创建出只包含所依赖的 jdk 模块来自定义运行时镜像,从而极大的减少 Java 运行时环境的大小。
module my.module {
//exports 公开指定包的所有公共成员
exports com.my.package.name;
}
module my.module {
//exports…to 限制访问的成员范围
export com.my.package.name to com.specific.package;
}
其他特性:
- G1 称为默认的垃圾收集器。
- 提供了快速创建不可变集合的静态工厂方法:
List.of()、Set.of()、Map.ofEntries()。 - String 存储结构优化,由
char[]变成了byte[]。 - 接口中允许使用私有方法。
- try-with-resources 增强,可以在 try-with-resources 语句中使用 effectively-final 变量(初始化后没有更改的变量),例如:
var w = new PrintWriter("test.txt"); try (w) {} - 提供进程相关的 api 可以获取当前正在运行的 jvm 的进程。
String 存储结构优化有哪些好处?
- 节约内存空间,由
char[]变成了byte[]之后,对于 ASCII 字符可以节约一半的内存,因为 ASCII 字符只用一个 byte 但是 char 是 2 个 byte。 - 字符串编码更加灵活,之前用 char 数组存的时候,需要将每个字符编码成 utf-16 ,这对于使用不同字符集的语言来说不太灵活,byte 数组可以支持更多的字符编码格式。
- 提高字符串操作效率,在字符串拷贝、序列化等场景下,可以避免从 char 数组转换为 byte 数组,从而提高运行效率。
Java10 新特性
1. 提供了局部变量类型推断
提供了 var 关键字,对于类型明确的局部变量可以定义 var 类型。 但是 var 关键字不能用于 null 值、Lambda 表达式、数组类型。
var count=null; //编译不通过,不能声明为 null
var r = () -> Math.random();//编译不通过,不能声明为 Lambda表达式
var array = {1,2,3};//编译不通过,不能声明数组
其他特性:
- 集合增强:对于 List 、 Set 、 Map 提供了
copyOf()对于给定的集合,返回一个不可变的拷贝。 - Collectors 中提供了将流转换为不可变集合的静态方法,例如:
list.stream().collect(Collectors.toUnmodifiableList());
Java11 新特性
1. 对 HTTP Client 进行标准化
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.baidu.com"))
.GET()
.build();
var client = HttpClient.newHttpClient();
// 同步
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
// 异步
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(e -> {
// 响应结果
System.out.println(e);
});
Java12-13 新特性
String 字符串增强
- String 字符可以通过
indent()方法进行缩进。例如:"Java".indent(4),会在字符串 Java 前面添加四个空格,如果是 -4 ,就是减少 4 个空格。 - String 字符可以通过
transform()进行拼接。例如:var str = "world".transform(e -> "hello " + e),str = "hello world"
Files 增强
Files 类添加了 mismatch(Path path, Path path2) 方法来对比两个文件。方法返回第一个不匹配的字符的位置。如果文件相同,返回 -1L。
Java14-15 新特性
空指针异常精准提示
使用 jvm 参数 -XX:+ShowCodeDetailsInExceptionMessages 开启更为精准的空指针异常调用信息。
switch 增强
switch 语法引入了类似 Lambda 语法,同时引入了 yield 关键字用来返回值,这样 switch 块的返回值就能赋值给变量了。
String result = switch (test) {
case null -> "null";
case "a", "b", "c" -> "a";
case "d", "e" -> "b";
default -> {
if (test.isEmpty()) {
yield "c";
} else {
yield "d";
}
}
};
文本块
对于大量文本,支持文本块特性,同时引入了两个转义字符,\:表示行尾不换行;\s:表示单个空格。
String str = """
第一行与第二行不换行。 \
\s但是前面加了一个空格
""";
Java16 新特性
instanceof 模式匹配增强
在之前使用 instanceof 进行模式匹配后,还需进行强制类型转换。增强后的模式匹配成功后,可以直接当做新的类型使用。
if (o instanceof String s) {
// 此时 s 已经被强转为字符串类型了。
}
数据类关键字 record
record 关键字用来简化数据类的定义。用 record 声明了数据类之后,就自动获得了它的属性访问方法、以及 toString() , hashCode() 、 equals() 方法。
它类似 lombok 插件,并同时使用了 @Getter, @ToString, @EqualsAndHashCode 注解。
它的成员变量都是 final 类型的,它的构造函数就是声明时的函数。
public static void main(String[] args) {
//使用这个数据类,并访问成员变量
Rectangle rectangle = new Rectangle(1, 2);
int length = rectangle.length();
String str = rectangle.toString();
}
// 用 record 声明一个数据类
record Rectangle(int length, int width) { }