目录
1. Java 8
2014年发布,带来Lambda表达式,Stream API,Date/Time API等新特性。
Java 8 新增了许多新的特性,这里主要讲解以下几个:
1.1. Lambda表达式和函数式接口
Lambda 表达式允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。
示例:
// Lambda 表达式示例
(a, b) -> a + b
函数式接口:只包含一个抽象方法的接口,称为函数式接口。Lambda 表达式的目标类型是这样的一个函数式接口。
示例:
@FunctionalInterface
public interface MyFunc {
void method();
}
// 使用 Lambda 表达式实现
MyFunc f = () -> System.out.println("Hello");
1.2. 方法引用
使用双冒号 :: 操作符,可以直接引用已有 Java 类或对象(实例)的方法或构造器。与 Lambda 表达式相比,方法引用可以使语义变得更加清晰。
示例:
// Lambda 表达式
(s) -> Integer.parseInt(s)
// 方法引用
Integer::parseInt
1.3. Stream API
Stream API可以让你以声明性方式处理数据。使用Stream API可以更简洁地表达各种数据处理场景,如:过滤、排序、映射、归约等。
Stream API 常用方法:
- forEach:迭代流中的每个数据
- filter:过滤流中的元素
- map:将流中的元素映射到另一个流中
- reduce:将流中的元素组合起来
- count:返回流中元素的总个数
- sorted:对流进行排序
- distinct:去除重复元素
- limit:限制流的大小
- skip:跳过流的前n个元素
- parallel:使流并行执行
- collect:将流转换成集合或聚合元素
示例:
List<String> list = Arrays.asList("a", "b", "c", "d");
// 迭代
list.stream().forEach(System.out::println);
// 过滤
list.stream().filter(s -> s.equals("a")).forEach(System.out::println);
// 映射
list.stream().map(String::toUpperCase).forEach(System.out::println);
// reduce 求和
Integer sum = list.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum); // 10
// count 个数
long count = list.stream().count();
System.out.println(count); // 4
// distinct 去重复
list.stream().distinct().forEach(System.out::println);
// limit 取前3个
list.stream().limit(3).forEach(System.out::println);
// skip 跳过2个
list.stream().skip(2).forEach(System.out::println);
// 并行stream
list.parallelStream().forEach(System.out::println);
// collect 收集到List
List<String> list2 = list.stream().collect(Collectors.toList());
System.out.println(list2);
Stream API 提供了一种声明式的方式来处理集合中的元素,大大提高了Java集合处理的效率和简洁度。
1.4. 接口默认方法
Java 8 在接口中引入了默认方法。默认方法使 interfaces 有了更大的灵活性。
示例:
interface MyInterface {
default void method1() { ... }
}
1.5. Optional类
Java 8 的 Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 类常用方法:
- of(T t):创建一个 Optional 实例,t必须非空;
- empty():创建一个空的 Optional 实例;
- isPresent():判断是否包含值;
- get():获取Optional包装的实际值;
- orElse(T t):获取Optional的值,如果为空则返回t;
- orElseGet(Supplier s):获取Optional的值,如果为空则调用s获取默认值;
- orElseThrow(Supplier<? extends X> exceptionSupplier):获取Optional的值,如果为空则抛出exceptionSupplier创建的异常。
Optional 的目的就是为null值提供更好的代替方案,用于异常处理,避免空指针异常,使代码更加精简和健壮。
示例:
// 创建 Optional 实例
Optional<String> op1 = Optional.of("abc");
Optional<String> op2 = Optional.empty();
// isPresent 方法判断Optional是否包含值
op1.isPresent(); // true
op2.isPresent(); // false
// get 方法获取Optional的值
String s = op1.get(); // s = "abc"
// 空指针处理
String s1 = op2.orElse("default"); // s1 = "default"
String s2 = op2.orElseGet(() -> "default"); // s2 = "default"
op2.orElseThrow(NullPointerException::new);
// throws NullPointerException
1.6. Nashorn引擎
Nashorn 是 Java 8 中嵌入式的 JavaScript 引擎。它允许在 Java 应用程序中执行 JavaScript 代码。
Nashorn 可以直接从 Java 代码调用 JavaScript 函数,也可以直接从 JavaScript 代码调用 Java 函数。
示例:
1). 从 Java 调用 JavaScript 函数:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
String script = "function sayHello(name) { print('Hello, ' + name + '!'); }";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("sayHello", "Java");
// 输出: Hello, Java!
2). 从 JavaScript 调用 Java 方法
public class NashornCallJava {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
NashornCallJava call = new NashornCallJava();
engine.put("call", call); // 将 Java 对象暴露给 JS 环境
String script = "call.sayHello('JavaScript');";
engine.eval(script);
// 输出: Hello, JavaScript!
3). Java 和 JS 交互
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("call", this); // 暴露当前类到 JS 环境
String script = "function invokeJava(name) { call.sayHello(name); }";
engine.eval(script);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("invokeJava", "Nashorn");
// 输出: Hello, Nashorn!
通过 Nashorn,Java 和 JavaScript 可以实现更深层次的交互和组合。
1.7. 日期时间 API
Java 8 中的日期时间 API 位于 java.time 包下,主要有以下几个类:
- LocalDate:表示日期,格式 YYYY-MM-DD,可以使用 now() 获取当前日期。
- LocalTime:表示时间,格式 HH-mm-ss,可以使用 now() 获取当前时间。
- LocalDateTime:表示日期时间,结合了日期和时间,格式 YYYY-MM-DDTHH-mm-ss。
- Instant:表示时间戳,从 1970 年 1 月 1 日 0 点开始的秒数。
- Period:表示日期间隔,以年月日衡量。
- Duration:表示时间间隔,以秒和纳秒衡量。
- ZonedDateTime:带时区的日期时间类。
- ZoneId:时区类,其 ID 遵循 RFC 6549。
日期时间的简单示例:
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("今天是: " + today);
// 获取当前时间
LocalTime time = LocalTime.now();
System.out.println("现在时间: " + time);
// 获取当前日期时间
LocalDateTime dt = LocalDateTime.now();
System.out.println("现在是: " + dt);
// 时间戳
Instant timestamp = Instant.now();
System.out.println("时间戳: " + timestamp);
// 增加时间
LocalDateTime dt2 = dt.plusYears(2);
System.out.println("两年后: " + dt2);
Period p = Period.of(1, 2, 3); // 1年2个月3天
LocalDate d2 = today.plus(p);
System.out.println("一年两月三天后: " + d2);
Duration d = Duration.ofSeconds(60); // 1分钟
LocalTime t2 = time.plus(d);
System.out.println("一分钟后: " + t2);
// 时区转换
ZoneId zone1 = ZoneId.of("UTC");
ZoneId zone2 = ZoneId.of("Asia/Shanghai");
LocalDateTime dt3 = LocalDateTime.now(zone1);
ZonedDateTime zdt1 = dt3.atZone(zone1);
ZonedDateTime zdt2 = dt3.atZone(zone2);
System.out.println("UTC 时间: " + zdt1);
System.out.println("上海时间: " + zdt2);
以上只是 Java8 部分新特性的简单介绍,如果还有其他你感兴趣的特性请评论回复,我再来详细解读
2. Java 9
2017年发布,带来Jigsaw(模块化),JShell(REPL),与JDK结构改变。
3. Java 10
2018年发布,带来VAR关键字,G1 GC的改进,Thread本地存储API等新特性。
4. Java 11
2018年发布,是第一个长期支持版本(LTS)。带来ZGC, Epsilon GC, Flight Recorder等新特性。
5. Java 12
2019年发布,主要是小改进和bug修复。
6. Java 13
2019年发布,主要新特性有Switch表达式,纤程API等。
7. Java 14
2020年发布,主要新特性为Pattern Matching for instanceof,Record类等。
8. Java 15
2020年发布,主要新特性为彻底取消Untethered 事件, Sealed类等。
9. Java 16
2021年3月发布,主要新特性为JDK Flight Recorder改进,松散输出格式,Vector API。