第一天:Java 8核心特性复习
Java 8是一个里程碑式的版本,于2014年3月发布,引入了许多革命性的特性,这些特性深刻地改变了Java编程范式。今天我们将全面复习Java 8的核心特性。
1. Lambda表达式
Lambda表达式是Java 8最重要的特性之一,它允许我们将行为(函数)作为参数传递,使代码更加简洁、可读性更强。
基本语法
// 无参数,无返回值
Runnable r1 = () -> System.out.println("Hello Lambda!");
// 有一个参数,无返回值(参数类型可以省略)
Consumer<String> c1 = (s) -> System.out.println(s);
Consumer<String> c2 = s -> System.out.println(s); // 单个参数可以省略括号
// 多个参数,有返回值
Comparator<Integer> cmp = (x, y) -> x - y;
// 代码块形式
BinaryOperator<Integer> add = (x, y) -> {
System.out.println("Adding " + x + " and " + y);
return x + y;
};
+----------------+ +----------------+
| 匿名内部类方式 | | Lambda表达式方式 |
+----------------+ +----------------+
| | | |
| new Runnable() | | () -> |
| { | --> | System.out. |
| public void | | println("Hi"); |
| run() { | | |
| System.out.| | |
| println( | | |
| "Hi"); | | |
| } | | |
| } | | |
+----------------+ +----------------+
函数式接口
Lambda表达式需要配合函数式接口使用。函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface
注解标记。
Java 8提供了许多内置的函数式接口:
// 常用函数式接口示例
Function<String, Integer> strLength = s -> s.length(); // 接收一个参数并返回一个结果
Predicate<Integer> isEven = n -> n % 2 == 0; // 接收一个参数并返回布尔值
Consumer<String> printer = s -> System.out.println(s); // 接收一个参数但不返回结果
Supplier<Double> random = () -> Math.random(); // 不接收参数但返回一个结果
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b; // 接收两个参数并返回一个结果
+-------------------+----------------+------------------+
| 函数式接口 | 函数描述符 | 示例 |
+-------------------+----------------+------------------+
| Predicate<T> | T -> boolean | 检查苹果是否是红色 |
| Consumer<T> | T -> void | 打印一个数字 |
| Function<T,R> | T -> R | 获取苹果的重量 |
| Supplier<T> | () -> T | 创建一个苹果对象 |
| BiFunction<T,U,R> | (T,U) -> R | 两个数字相加 |
| ... | ... | ... |
+-------------------+----------------+------------------+
变量捕获
Lambda表达式可以捕获外部变量,但这些变量必须是effectively final的(即使不声明为final,也不能在Lambda中修改)。
int factor = 2;
Function<Integer, Integer> multiplier = n -> n * factor; // 捕获外部变量factor
// factor = 3; // 错误:Lambda表达式中使用的变量必须是effectively final的
+------------------+ +------------------+
| Lambda表达式外部 | | Lambda表达式内部 |
+------------------+ +------------------+
| int factor = 2; | --> | n -> n * factor |
+------------------+ +------------------+
| factor不能被修改 |
+------------------+
2. 方法引用
方法引用是Lambda表达式的一种简化形式,当Lambda表达式的内容仅仅是调用一个已有的方法时,可以使用方法引用。
四种类型的方法引用
// 1. 静态方法引用:ClassName::staticMethodName
Function<String, Integer> parseInt = Integer::parseInt;
// 2. 实例方法引用:instance::methodName
String str = "Hello";
Supplier<Integer> length = str::length;
// 3. 对象方法引用:ClassName::methodName
Function<String, Integer> strLength = String::length;
// 4. 构造方法引用:ClassName::new
Supplier<List<String>> listSupplier = ArrayList::new;
+----------------------+-------------------------+---------------------------+
| 方法引用类型 | 语法 | 对应的Lambda表达式 |
+----------------------+-------------------------+---------------------------+
| 静态方法引用 | ClassName::staticMethod | (args) -> ClassName. |
| | | staticMethod(args) |
+----------------------+-------------------------+---------------------------+
| 实例方法引用 | instance::method | (args) -> instance. |
| | | method(args) |
+----------------------+-------------------------+---------------------------+
| 对象方法引用 | ClassName::method | (instance, args) -> |
| | | instance.method(args) |
+----------------------+-------------------------+---------------------------+
| 构造方法引用 | ClassName::new | (args) -> new ClassName |
| | | (args) |
+----------------------+-------------------------+---------------------------+
3. Stream API
Stream API提供了一种函数式编程的方式来处理集合,使代码更加简洁、可读性更强。
创建Stream
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> streamFromList = list.stream();
// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);
// 使用Stream.of
Stream<String> streamOf = Stream.of("a", "b", "c");
// 创建无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
Stream<Double> randomStream = Stream.generate(Math::random);
+-------------+ +-------------+ +-------------+ +-------------+
| 数据源 | | 中间操作 | | 中间操作 | | 终端操作 |
| (Collection)| | (filter) | | (map) | | (collect) |
+-------------+ +-------------+ +-------------+ +-------------+
| | | |
v v v v
[1,2,3,4,5] --> [2,4] --> [4,16] --> [4,16]
中间操作
中间操作返回一个新的Stream,可以链式调用。
List<String> names = Arrays.asList("John", "Jane", "Jack", "Joe");
names.stream()
.filter(name -> name.startsWith("J")) // 过滤
.map(String::toUpperCase) // 转换
.sorted() // 排序
.distinct() // 去重
.limit(2) // 限制数量
.forEach(System.out::println); // 终端操作
+-------------+ +-------------+ +-------------+ +-------------+
| filter() | | map() | | sorted() | | limit() |
+-------------+ +-------------+ +-------------+ +-------------+
| 筛选满足 | | 将元素转换 | | 对元素 | | 截取前N个 |
| 条件的元素 | | 为新元素 | | 进行排序 | | 元素 |
+-------------+ +-------------+ +-------------+ +-------------+
终端操作
终端操作会消费Stream并产生结果。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 收集结果
List<Integer> doubledList = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
// 归约操作
int sum = numbers.stream().reduce(0, Integer::sum);
// 查找操作
Optional<Integer> first = numbers.stream().findFirst();
// 匹配操作
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0);
boolean noneEven = numbers.stream().noneMatch(n -> n % 2 == 0);
// 统计操作
long count = numbers.stream().count();
+-------------+ +-------------+ +-------------+
| 终端操作 | | 返回类型 | | 示例 |
+-------------+ +-------------+ +-------------+
| forEach | | void | | 遍历每个元素 |
| collect | | 集合 | | 收集到List |
| reduce | | Optional/T | | 求和、最大值 |
| count | | long | | 计算元素个数 |
| findFirst | | Optional<T> | | 查找第一个 |
| anyMatch | | boolean | | 任一匹配 |
| allMatch | | boolean | | 全部匹配 |
| noneMatch | | boolean | | 全不匹配 |
+-------------+ +-------------+ +-------------+
Collectors类
Collectors类提供了许多有用的收集器,用于将Stream转换为集合或其他形式。
// 收集为List
List<String> list = stream.collect(Collectors.toList());
// 收集为Set
Set<String> set = stream.collect(Collectors.toSet());
// 收集为Map
Map<String, Integer> map = stream.collect(Collectors.toMap(s -> s, String::length));
// 连接字符串
String joined = stream.collect(Collectors.joining(", "));
// 分组
Map<Integer, List<String>> groupedByLength = stream.collect(Collectors.groupingBy(String::length));
// 分区
Map<Boolean, List<Integer>> partitioned = stream.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// 统计
IntSummaryStatistics stats = stream.collect(Collectors.summarizingInt(String::length));
+------------------+ +------------------+
| Collectors方法 | | 返回结果 |
+------------------+ +------------------+
| toList() | | ArrayList |
| toSet() | | HashSet |
| toMap(k,v) | | HashMap |
| joining() | | String |
| groupingBy() | | Map<K,List<T>> |
| partitioningBy() | | Map<Boolean, |
| | | List<T>> |
| summarizingXxx() | | XxxSummary |
| | | Statistics |
+------------------+ +------------------+
并行流
Stream API支持并行处理,可以充分利用多核CPU的优势。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 串行流
long startTime1 = System.currentTimeMillis();
numbers.stream().map(n -> performHeavyComputation(n)).collect(Collectors.toList());
long endTime1 = System.currentTimeMillis();
System.out.println("串行流耗时: " + (endTime1 - startTime1) + "ms");
// 并行流
long startTime2 = System.currentTimeMillis();
numbers.parallelStream().map(n -> performHeavyComputation(n)).collect(Collectors.toList());
long endTime2 = System.currentTimeMillis();
System.out.println("并行流耗时: " + (endTime2 - startTime2) + "ms");
串行流处理:
[1] -> [2] -> [3] -> [4] -> [5] -> [结果]
并行流处理:
[1] -> [2] -+
|
[3] -> [4] -+-> [合并] -> [结果]
|
[5] -------+
4. Optional类
Optional类是一个容器对象,可以包含也可以不包含非空值,用于避免空指针异常。
// 创建Optional对象
Optional<String> optional1 = Optional.of("Hello"); // 不能为null
Optional<String> optional2 = Optional.ofNullable(null); // 可以为null
Optional<String> optional3 = Optional.empty(); // 空Optional
// 检查值是否存在
boolean isPresent = optional1.isPresent();
boolean isEmpty = optional1.isEmpty(); // Java 11+
// 获取值
String value1 = optional1.get(); // 如果为空会抛出NoSuchElementException
String value2 = optional2.orElse("Default"); // 如果为空返回默认值
String value3 = optional2.orElseGet(() -> "Computed Default"); // 如果为空通过Supplier获取默认值
String value4 = optional2.orElseThrow(() -> new RuntimeException("Value not present")); // 如果为空抛出异常
// 条件操作
optional1.ifPresent(s -> System.out.println(s)); // 如果存在值则执行Consumer
optional1.ifPresentOrElse(
s -> System.out.println(s),
() -> System.out.println("Empty") // Java 9+
);
// 转换操作
Optional<Integer> mapped = optional1.map(String::length);
Optional<Optional<Integer>> nested = optional1.map(s -> Optional.of(s.length()));
Optional<Integer> flatMapped = optional1.flatMap(s -> Optional.of(s.length()));
// 过滤操作
Optional<String> filtered = optional1.filter(s -> s.length() > 3);
+------------------+ +------------------+ +------------------+
| 传统null检查 | | Optional方式 | | 结果 |
+------------------+ +------------------+ +------------------+
| if(obj != null) | | optional | | 执行操作或 |
| { | | .ifPresent( | | 跳过 |
| doSomething(); | | this::doSomething| | |
| } | | ); | | |
+------------------+ +------------------+ +------------------+
| return obj != null| | return optional | | 返回实际值或 |
| ? obj : default; | | .orElse(default);| | 默认值 |
+------------------+ +------------------+ +------------------+
| if(obj == null) | | optional | | 值不存在时 |
| { | | .orElseThrow( | | 抛出异常 |
| throw new Ex...| | () -> new Ex... | | |
| } | | ); | | |
+------------------+ +------------------+ +------------------+
5. 接口默认方法和静态方法
Java 8允许在接口中定义默认方法和静态方法,这些方法有默认实现,实现类可以选择覆盖或使用默认实现。
interface Vehicle {
// 抽象方法
void start();
// 默认方法
default void honk() {
System.out.println("Beep beep!");
}
// 静态方法
static Vehicle create() {
return new Car();
}
}
class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car started");
}
// 可以选择覆盖默认方法
@Override
public void honk() {
System.out.println("Car honk: Beep beep!");
}
}
+------------------+ +------------------+
| Java 8之前的接口 | | Java 8的接口 |
+------------------+ +------------------+
| 只能包含: | | 可以包含: |
| - 常量 | | - 常量 |
| - 抽象方法 | | - 抽象方法 |
| | | - 默认方法 |
| | | - 静态方法 |
+------------------+ +------------------+
多重继承问题
当一个类实现多个接口,而这些接口有相同的默认方法时,会出现多重继承问题。
interface A {
default void foo() {
System.out.println("A's foo");
}
}
interface B {
default void foo() {
System.out.println("B's foo");
}
}
// 编译错误:类C继承了接口A和B的相同方法foo()
class C implements A, B {
// 必须覆盖foo方法解决冲突
@Override
public void foo() {
// 可以选择调用其中一个接口的默认实现
A.super.foo();
// 或者
// B.super.foo();
// 或者提供自己的实现
}
}
+------------------+ +------------------+
| 接口A | | 接口B |
+------------------+ +------------------+
| default void foo()| | default void foo()|
+------------------+ +------------------+
^ ^
| |
+-----------------------+
|
v
+------------------+
| 类C |
+------------------+
| @Override |
| void foo() { |
| A.super.foo(); |
| } |
+------------------+
6. 新的日期时间API
Java 8引入了新的日期时间API,位于java.time
包下,解决了旧API的许多问题。
核心类
// 本地日期和时间
LocalDate date = LocalDate.now(); // 当前日期
LocalTime time = LocalTime.now(); // 当前时间
LocalDateTime dateTime = LocalDateTime.now(); // 当前日期和时间
// 创建特定的日期和时间
LocalDate specificDate = LocalDate.of(2023, 8, 17);
LocalTime specificTime = LocalTime.of(14, 30, 15);
LocalDateTime specificDateTime = LocalDateTime.of(2023, 8, 17, 14, 30, 15);
// 日期时间操作
LocalDate tomorrow = date.plusDays(1);
LocalDate lastMonth = date.minusMonths(1);
LocalDate firstDayOfMonth = date.withDayOfMonth(1);
// 时区
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime shanghaiTime = ZonedDateTime.now(zoneId);
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
// 时间间隔
Period period = Period.between(LocalDate.of(2020, 1, 1), LocalDate.of(2023, 8, 17));
Duration duration = Duration.between(LocalTime.of(14, 0), LocalTime.of(15, 30));
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = dateTime.format(formatter);
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-08-17 14:30:15", formatter);
+------------------+ +------------------+ +------------------+
| 日期时间类型 | | 时区处理 | | 时间间隔 |
+------------------+ +------------------+ +------------------+
| LocalDate | | ZoneId | | Period |
| LocalTime | | ZoneOffset | | Duration |
| LocalDateTime | | ZonedDateTime | | ChronoUnit |
| Instant | | OffsetDateTime | | |
+------------------+ +------------------+ +------------------+
时间戳和Instant
// 获取当前时间戳
Instant now = Instant.now();
// 从毫秒创建Instant
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
// 在Instant上添加时间
Instant later = instant.plus(Duration.ofHours(2));
// 比较两个Instant
boolean isBefore = instant.isBefore(later);
// 转换为LocalDateTime
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
时钟
// 获取系统默认时钟
Clock clock = Clock.systemDefaultZone();
// 获取特定时区的时钟
Clock parisClock = Clock.system(ZoneId.of("Europe/Paris"));
// 固定时钟(用于测试)
Clock fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
// 从时钟获取当前时间
Instant instant = clock.instant();
LocalDateTime dateTime = LocalDateTime.now(clock);
+------------------+ +------------------+
| 旧的日期时间API | | Java 8日期时间API|
+------------------+ +------------------+
| java.util.Date | | java.time.Local |
| java.util.Calendar| | Date/Time/ |
| java.text.Simple | | DateTime |
| DateFormat | | |
+------------------+ +------------------+
| 可变的 | | 不可变的 |
| 非线程安全 | | 线程安全 |
| 设计不一致 | | 设计一致 |
| 处理困难 | | API丰富易用 |
+------------------+ +------------------+
7. CompletableFuture
CompletableFuture提供了一种异步编程的方式,可以组合多个异步操作,处理异常,以及控制异步操作的执行。
创建和执行异步任务
// 创建CompletableFuture
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello";
});
// 不返回结果的异步任务
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
System.out.println("Running async task");
});
// 指定Executor
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Hello", executor);
转换和组合
// 转换结果
CompletableFuture<Integer> future4 = future1.thenApply(s -> s.length());
// 消费结果
CompletableFuture<Void> future5 = future1.thenAccept(s -> System.out.println(s));
// 执行下一个操作(不使用上一个结果)
CompletableFuture<Void> future6 = future1.thenRun(() -> System.out.println("Done"));
// 组合两个CompletableFuture
CompletableFuture<String> future7 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> combinedFuture = future1.thenCombine(future7, (s1, s2) -> s1 + " " + s2);
// 组合多个CompletableFuture(所有完成后执行)
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future4, future7);
// 组合多个CompletableFuture(任意一个完成后执行)
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future4, future7);
7. CompletableFuture(续)
异常处理
// 处理异常
CompletableFuture<String> failedFuture = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Computation failed");
return "Success";
});
CompletableFuture<String> recoveredFuture = failedFuture.exceptionally(ex -> "Recovered: " + ex.getMessage());
// 处理正常结果和异常
CompletableFuture<String> handledFuture = failedFuture.handle((result, ex) -> {
if (ex != null) {
return "Handled: " + ex.getMessage();
} else {
return result;
}
});
+------------------+ +------------------+
| 传统异步编程 | | CompletableFuture|
+------------------+ +------------------+
| 回调地狱 | | 链式调用 |
| 异常处理困难 | | 内置异常处理 |
| 组合操作复杂 | | 丰富的组合API |
| 手动线程管理 | | 自动线程管理 |
+------------------+ +------------------+
等待完成
// 阻塞等待结果
String result = future1.get(); // 可能抛出异常
String resultWithTimeout = future1.get(1, TimeUnit.SECONDS); // 带超时的等待
// 非阻塞检查
boolean isDone = future1.isDone();
boolean isCancelled = future1.isCancelled();
boolean isCompletedExceptionally = future1.isCompletedExceptionally();
// 强制完成
future1.complete("Forced result");
// 强制异常完成
future1.completeExceptionally(new RuntimeException("Forced failure"));
// 取消任务
future1.cancel(true);
8. Base64编码
Java 8内置了Base64编码的支持。
// 基本编码
String text = "Hello, World!";
String encoded = Base64.getEncoder().encodeToString(text.getBytes());
byte[] decoded = Base64.getDecoder().decode(encoded);
// URL安全编码
String urlEncoded = Base64.getUrlEncoder().encodeToString(text.getBytes());
byte[] urlDecoded = Base64.getUrlDecoder().decode(urlEncoded);
// MIME编码
String mimeEncoded = Base64.getMimeEncoder().encodeToString(text.getBytes());
byte[] mimeDecoded = Base64.getMimeDecoder().decode(mimeEncoded);
// 带行长度限制的编码
int lineLength = 76;
String delimiter = System.lineSeparator();
Base64.Encoder encoder = Base64.getMimeEncoder(lineLength, delimiter.getBytes());
String encodedWithLineBreaks = new String(encoder.encode(text.getBytes()));
+------------------+ +------------------+
| Base64编码类型 | | 用途 |
+------------------+ +------------------+
| 基本编码 | | 标准Base64编码 |
| URL安全编码 | | URL和文件名 |
| MIME编码 | | 电子邮件等 |
+------------------+ +------------------+
9. 注解增强
Java 8增强了注解的功能,包括重复注解和类型注解。
重复注解
// 定义容器注解
@Retention(RetentionPolicy.RUNTIME)
@interface Schedules {
Schedule[] value();
}
// 定义可重复注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
@interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
// 使用重复注解
@Schedule(dayOfMonth = "first", dayOfWeek = "Mon", hour = 12)
@Schedule(dayOfMonth = "last", dayOfWeek = "Fri", hour = 23)
public class RepeatingAnnotationsExample {
// ...
}
// 获取重复注解
Schedule[] schedules = RepeatingAnnotationsExample.class.getAnnotationsByType(Schedule.class);
+------------------+ +------------------+
| Java 8之前 | | Java 8重复注解 |
+------------------+ +------------------+
| @Schedules({ | | @Schedule(...) |
| @Schedule(...),| | @Schedule(...) |
| @Schedule(...) | | |
| }) | | |
+------------------+ +------------------+
类型注解
// 定义类型注解
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@interface NonNull {
}
// 使用类型注解
public class TypeAnnotationsExample {
// 变量类型注解
private @NonNull String text;
// 泛型类型注解
private List<@NonNull String> list;
// 方法返回类型注解
public @NonNull String getText() {
return text;
}
// 方法参数类型注解
public void setText(@NonNull String text) {
this.text = text;
}
// 异常类型注解
public void process() throws @NonNull Exception {
// ...
}
// 类型转换注解
String str = (@NonNull String) obj;
// 数组类型注解
@NonNull String[] array;
// 接收者类型注解
public void process(@NonNull TypeAnnotationsExample this) {
// ...
}
}
+------------------+ +------------------+
| 注解位置 | | 示例 |
+------------------+ +------------------+
| 类型声明 | | @NonNull String |
| 泛型参数 | | List<@NonNull T> |
| 方法返回类型 | | @NonNull String |
| | | getText() |
| 方法参数 | | setText(@NonNull |
| | | String text) |
| 异常声明 | | throws @NonNull |
| | | Exception |
+------------------+ +------------------+
10. Nashorn JavaScript引擎
Java 8引入了新的JavaScript引擎Nashorn,替代了旧的Rhino引擎。
// 创建ScriptEngine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// 执行JavaScript代码
engine.eval("print('Hello, Nashorn!')");
// 在JavaScript中调用Java方法
engine.eval("var System = Java.type('java.lang.System'); System.out.println('Hello from JavaScript!');");
// 在Java中调用JavaScript函数
engine.eval("function add(a, b) { return a + b; }");
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("add", 1, 2);
System.out.println(result); // 输出: 3
// 在JavaScript中使用Java对象
engine.put("person", new Person("John", 30));
engine.eval("print(person.getName() + ' is ' + person.getAge() + ' years old')");
// 使用JavaScript对象
engine.eval("var jsObject = { name: 'John', age: 30 };");
Object jsObject = engine.get("jsObject");
+------------------+ +------------------+
| Rhino (旧) | | Nashorn (Java 8) |
+------------------+ +------------------+
| 性能较低 | | 性能提升 |
| JSR-223支持 | | JSR-223支持 |
| 不支持ECMAScript | | 支持ECMAScript |
| 5.1的所有功能 | | 5.1及部分6功能 |
+------------------+ +------------------+
11. 并行数组操作
Java 8增强了Arrays类,添加了许多并行操作方法。
int[] numbers = {5, 3, 8, 1, 9, 2, 7, 4, 6};
// 并行排序
Arrays.parallelSort(numbers);
// 并行设置值
Arrays.parallelSetAll(numbers, i -> i * 2);
// 并行前缀操作
Arrays.parallelPrefix(numbers, (x, y) -> x + y);
+------------------+ +------------------+
| 串行数组操作 | | 并行数组操作 |
+------------------+ +------------------+
| Arrays.sort() | | Arrays.parallel |
| | | Sort() |
| 手动设置值 | | Arrays.parallel |
| | | SetAll() |
| 手动计算前缀和 | | Arrays.parallel |
| | | Prefix() |
+------------------+ +------------------+
12. StampedLock
Java 8引入了StampedLock,这是一种新的锁机制,支持乐观读取。
StampedLock lock = new StampedLock();
// 写锁
long stamp = lock.writeLock();
try {
// 写入共享数据
} finally {
lock.unlockWrite(stamp);
}
// 读锁
stamp = lock.readLock();
try {
// 读取共享数据
} finally {
lock.unlockRead(stamp);
}
// 乐观读
stamp = lock.tryOptimisticRead();
// 读取共享数据
if (!lock.validate(stamp)) {
// 数据已被修改,获取读锁并重新读取
stamp = lock.readLock();
try {
// 重新读取共享数据
} finally {
lock.unlockRead(stamp);
}
}
+------------------+ +------------------+ +------------------+
| ReentrantLock | | ReadWriteLock | | StampedLock |
+------------------+ +------------------+ +------------------+
| 互斥锁 | | 读写锁 | | 读写锁+乐观锁 |
| 可重入 | | 读共享,写互斥 | | 读共享,写互斥 |
| 不区分读写 | | 区分读写 | | 区分读写 |
| 不支持乐观读 | | 不支持乐观读 | | 支持乐观读 |
| 可条件等待 | | 可条件等待 | | 不可条件等待 |
+------------------+ +------------------+ +------------------+
13. 新的集合方法
Java 8为集合框架添加了一些新的方法。
// Map接口的新方法
Map<String, Integer> map = new HashMap<>();
// 如果键不存在则放入值
map.putIfAbsent("a", 1);
// 计算值
map.compute("a", (k, v) -> v == null ? 1 : v + 1);
map.computeIfAbsent("b", k -> k.length());
map.computeIfPresent("a", (k, v) -> v * 2);
// 合并值
map.merge("a", 1, (oldValue, newValue) -> oldValue + newValue);
// 遍历
map.forEach((k, v) -> System.out.println(k + ": " + v));
// 删除
map.remove("a", 1); // 只有当值为1时才删除
// List接口的新方法
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
// 遍历
list.forEach(System.out::println);
// 排序
list.sort(Comparator.naturalOrder());
list.sort(Comparator.reverseOrder());
list.sort(Comparator.comparing(String::length));
// 替换所有元素
list.replaceAll(String::toUpperCase);
// 删除满足条件的元素
list.removeIf(s -> s.equals("a"));
+------------------+ +------------------+
| Map新方法 | | List新方法 |
+------------------+ +------------------+
| putIfAbsent() | | forEach() |
| compute() | | sort() |
| computeIfAbsent()| | replaceAll() |
| computeIfPresent()| | removeIf() |
| merge() | | |
| forEach() | | |
+------------------+ +------------------+
14. 函数式编程增强
Java 8通过函数式接口和Lambda表达式,使Java支持函数式编程范式。
函数组合
// 函数组合
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); // g(f(x))
Function<Integer, Integer> i = f.compose(g); // f(g(x))
System.out.println(h.apply(1)); // (1 + 1) * 2 = 4
System.out.println(i.apply(1)); // (1 * 2) + 1 = 3
// 谓词组合
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Predicate<String> isShort = s -> s.length() < 5;
Predicate<String> isEmptyOrShort = isEmpty.or(isShort);
Predicate<String> isNotEmptyAndShort = isNotEmpty.and(isShort);
+------------------+ +------------------+
| 函数组合 | | 谓词组合 |
+------------------+ +------------------+
| f.andThen(g) | | p.negate() |
| f.compose(g) | | p.and(q) |
| Function.identity()| | p.or(q) |
+------------------+ +------------------+
柯里化和部分应用
// 柯里化
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Function<Integer, Function<Integer, Integer>> curriedAdd = a -> b -> a + b;
// 使用柯里化函数
Function<Integer, Integer> add5 = curriedAdd.apply(5);
System.out.println(add5.apply(3)); // 5 + 3 = 8
// 部分应用
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
Function<Integer, Integer> multiplyBy3 = b -> multiply.apply(3, b);
System.out.println(multiplyBy3.apply(4)); // 3 * 4 = 12
+------------------+ +------------------+
| 普通函数 | | 柯里化函数 |
+------------------+ +------------------+
| add(a, b) | | add(a)(b) |
| 一次接收所有参数 | | 分步接收参数 |
+------------------+ +------------------+
15. 其他增强
StringJoiner
// 使用StringJoiner
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("a");
joiner.add("b");
joiner.add("c");
System.out.println(joiner.toString()); // [a, b, c]
// 使用String.join
String joined = String.join(", ", "a", "b", "c");
System.out.println(joined); // a, b, c
数值流
// IntStream
IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4
IntStream intStream2 = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5
// LongStream
LongStream longStream = LongStream.range(1L, 5L);
// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);
// 数值流操作
int sum = IntStream.rangeClosed(1, 100).sum();
OptionalDouble avg = IntStream.rangeClosed(1, 100).average();
IntSummaryStatistics stats = IntStream.rangeClosed(1, 100).summaryStatistics();
+------------------+ +------------------+
| 普通Stream | | 数值Stream |
+------------------+ +------------------+
| Stream<Integer> | | IntStream |
| 需要装箱/拆箱 | | 无装箱/拆箱 |
| 无特殊数值操作 | | sum(), average() |
| | | max(), min() |
+------------------+ +------------------+
Files类增强
// 读取所有行
List<String> lines = Files.readAllLines(Paths.get("file.txt"));
// 使用Stream读取行
try (Stream<String> lineStream = Files.lines(Paths.get("file.txt"))) {
lineStream.forEach(System.out::println);
}
// 遍历目录
try (Stream<Path> pathStream = Files.walk(Paths.get("."))) {
pathStream.filter(Files::isRegularFile)
.forEach(System.out::println);
}
// 查找文件
try (Stream<Path> pathStream = Files.find(Paths.get("."), 10,
(path, attr) -> attr.isRegularFile() && path.toString().endsWith(".java"))) {
pathStream.forEach(System.out::println);
}
+------------------+ +------------------+
| Files新方法 | | 用途 |
+------------------+ +------------------+
| lines() | | 流式读取文件行 |
| walk() | | 遍历目录树 |
| find() | | 查找文件 |
| list() | | 列出目录内容 |
+------------------+ +------------------+
总结
Java 8引入了许多革命性的特性,这些特性深刻地改变了Java编程范式,使Java更加现代化、简洁和高效。这些特性包括:
- Lambda表达式和函数式接口
- 方法引用
- Stream API
- Optional类
- 接口默认方法和静态方法
- 新的日期时间API
- CompletableFuture
- Base64编码
- 注解增强
- Nashorn JavaScript引擎
- 并行数组操作
- StampedLock
- 新的集合方法
- 函数式编程增强
- 其他增强(StringJoiner、数值流、Files类增强等)
掌握这些特性对于提高开发效率和代码质量至关重要。在接下来的学习中,我们将基于Java 8,逐步学习Java 9到Java 25的新特性。
+------------------+
| Java 8特性 |
+------------------+
| |
| Lambda表达式 |
| 方法引用 |
| Stream API |
| Optional类 |
| 默认方法 |
| 新日期时间API |
| CompletableFuture|
| Base64编码 |
| 注解增强 |
| Nashorn引擎 |
| 并行数组操作 |
| StampedLock |
| 新集合方法 |
| 函数式编程增强 |
| 其他增强 |
| |
+------------------+
练习
- 使用Lambda表达式和Stream API对一个字符串列表进行过滤、转换和排序。
- 使用Optional类处理可能为null的对象。
- 使用CompletableFuture实现多个异步操作的组合。
- 使用新的日期时间API计算两个日期之间的天数。
- 定义一个带有默认方法的接口,并实现该接口。