Java8新特性入门教程 - Lambda表达式、Stream API、Optional等完整指南
Java8新特性入门教程 - 从零开始学习Java8核心特性
目录
- Java8简介
- Lambda表达式
- 函数式接口
- 方法引用
- Stream API
- Optional类
- 新的日期时间API
- 接口默认方法和静态方法
- CompletableFuture异步编程
- 并行流和性能优化
- 实际应用案例
- 总结与最佳实践
1. Java8简介
Java 8于2014年3月发布,是Java语言发展史上的一个重要里程碑。Java 8引入了许多革命性的特性,特别是对函数式编程的支持,使得Java语言更加现代化和强大。
主要特性列表:
✅ Lambda表达式:简化代码,支持函数式编程
✅ Stream API:强大的数据处理能力
✅ Optional类:优雅处理空值
✅ 新的日期时间API:解决旧API的问题
✅ 接口默认方法:接口可以包含实现
✅ 方法引用:简化Lambda表达式
✅ CompletableFuture:异步编程支持
✅ 并行流:简化并行处理
现代化开发:Java8特性已成为Java开发的标准
代码简洁:大幅减少样板代码
性能提升:并行流等特性提升性能
广泛应用:Spring、Hibernate等框架都基于Java8
面试必备:Java8特性是面试高频考点
JDK版本:JDK 8或更高版本
IDE:IntelliJ IDEA、Eclipse等(推荐使用支持Java8的版本)
验证Java版本:
java -version
# 应该显示 java version "1.8.0_xxx" 或更高2. Lambda表达式
Lambda表达式是Java8引入的最重要的特性之一,它允许我们将函数作为方法的参数,或者将代码本身当作数据来处理。
Lambda表达式的语法:
(parameters) -> expression
// 或
(parameters) -> { statements; }语法规则
- 参数列表:可以包含零个或多个参数
- 箭头符号:
->分隔参数和函数体 - 函数体:可以是表达式或代码块
示例对比
传统方式(匿名内部类):
// 使用匿名内部类实现Runnable接口
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello Lambda");
}
};Lambda表达式方式:
// 使用Lambda表达式
Runnable r = () -> System.out.println("Hello Lambda");无参数Lambda
// 无参数,无返回值
Runnable runnable = () -> System.out.println("无参数Lambda");
// 执行
runnable.run();单个参数Lambda
// 单个参数,可以省略括号
Consumer<String> consumer = name -> System.out.println("Hello " + name);
// 也可以带括号
Consumer<String> consumer2 = (name) -> System.out.println("Hello " + name);
consumer.accept("Java");多个参数Lambda
// 多个参数必须使用括号
BinaryOperator<Integer> add = (a, b) -> a + b;
int result = add.apply(10, 20); // 30
// 可以指定参数类型
BinaryOperator<Integer> multiply = (Integer a, Integer b) -> a * b;带返回值的Lambda
// 表达式形式(自动返回)
Function<Integer, Integer> square = x -> x * x;
int result = square.apply(5); // 25
// 代码块形式(需要return)
Function<Integer, Integer> square2 = x -> {
return x * x;
};示例1:集合遍历
import java.util.Arrays;
import java.util.List;
List<String> names = Arrays.asList("张三", "李四", "王五");
// 传统方式
for (String name : names) {
System.out.println(name);
}
// Lambda方式
names.forEach(name -> System.out.println(name));
// 方法引用方式(更简洁)
names.forEach(System.out::println);示例2:线程创建
// 传统方式
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行");
}
});
// Lambda方式
Thread t2 = new Thread(() -> System.out.println("线程执行"));
t1.start();
t2.start();示例3:事件处理
import java.util.function.Consumer;
// 模拟按钮点击事件
class Button {
private Consumer<String> onClick;
public void setOnClick(Consumer<String> onClick) {
this.onClick = onClick;
}
public void click() {
if (onClick != null) {
onClick.accept("按钮被点击");
}
}
}
// 使用Lambda表达式
Button button = new Button();
button.setOnClick(message -> System.out.println(message));
button.click();示例4:条件过滤
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 传统方式:过滤偶数
List<Integer> evenNumbers = new ArrayList<>();
for (Integer num : numbers) {
if (num % 2 == 0) {
evenNumbers.add(num);
}
}
// Lambda方式:使用Predicate
Predicate<Integer> isEven = num -> num % 2 == 0;
List<Integer> evenNumbers2 = new ArrayList<>();
numbers.forEach(num -> {
if (isEven.test(num)) {
evenNumbers2.add(num);
}
});局部变量
int num = 10;
Runnable r = () -> {
// 可以访问外部变量,但必须是final或effectively final
System.out.println(num);
// num++; // 错误!不能修改
};实例变量和静态变量
public class LambdaScope {
private int instanceVar = 100;
private static int staticVar = 200;
public void test() {
Runnable r = () -> {
// 可以访问和修改实例变量
instanceVar++;
System.out.println(instanceVar);
// 可以访问和修改静态变量
staticVar++;
System.out.println(staticVar);
};
r.run();
}
}- 代码简洁:减少样板代码
- 可读性强:表达意图更清晰
- 函数式编程:支持函数式编程范式
- 并行处理:更容易实现并行处理
3. 函数式接口
函数式接口(Functional Interface)是只包含一个抽象方法的接口。Java8提供了@FunctionalInterface注解来标识函数式接口。
Predicate<T> - 断言接口
用于判断条件,返回boolean值。
import java.util.function.Predicate;
Predicate<String> isEmpty = s -> s.isEmpty();
Predicate<String> isLong = s -> s.length() > 5;
// 测试
System.out.println(isEmpty.test("")); // true
System.out.println(isLong.test("Hello")); // false
// 组合使用
Predicate<String> isNotEmptyAndLong = isEmpty.negate().and(isLong);
System.out.println(isNotEmptyAndLong.test("Hello World")); // true实际应用:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Predicate<String> startsWithC = name -> name.startsWith("C");
List<String> filtered = names.stream()
.filter(startsWithC)
.collect(Collectors.toList());
// 结果: [Charlie]Function<T, R> - 函数接口
接受一个参数,返回一个结果。
import java.util.function.Function;
// 字符串转整数
Function<String, Integer> parseInt = Integer::parseInt;
Integer result = parseInt.apply("123"); // 123
// 字符串转大写
Function<String, String> toUpperCase = String::toUpperCase;
String upper = toUpperCase.apply("hello"); // HELLO
// 组合函数
Function<Integer, Integer> multiply2 = x -> x * 2;
Function<Integer, Integer> add3 = x -> x + 3;
Function<Integer, Integer> multiplyAndAdd = multiply2.andThen(add3);
int result2 = multiplyAndAdd.apply(5); // 13 (5*2+3)实际应用:
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 结果: [ALICE, BOB, CHARLIE]Consumer<T> - 消费者接口
接受一个参数,不返回结果。
import java.util.function.Consumer;
Consumer<String> printer = System.out::println;
printer.accept("Hello World"); // 输出: Hello World
// 组合消费者
Consumer<String> print = s -> System.out.print(s);
Consumer<String> printLine = s -> System.out.println(s);
Consumer<String> printBoth = print.andThen(printLine);
printBoth.accept("Hello"); // 输出: HelloHello(换行)实际应用:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println("Hello, " + name));Supplier<T> - 供应者接口
不接受参数,返回一个结果。
import java.util.function.Supplier;
Supplier<String> getMessage = () -> "Hello World";
String message = getMessage.get(); // "Hello World"
// 随机数生成器
Supplier<Double> random = Math::random;
double value = random.get();
// 当前时间
Supplier<LocalDateTime> now = LocalDateTime::now;
LocalDateTime time = now.get();实际应用:
// 懒加载
Supplier<List<String>> expensiveOperation = () -> {
// 模拟耗时操作
return Arrays.asList("data1", "data2", "data3");
};
// 只有在需要时才执行
List<String> data = expensiveOperation.get();BiFunction<T, U, R> - 双参数函数
接受两个参数,返回一个结果。
import java.util.function.BiFunction;
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
int sum = add.apply(10, 20); // 30
BiFunction<String, String, String> concat = (s1, s2) -> s1 + s2;
String result = concat.apply("Hello", "World"); // HelloWorldUnaryOperator<T> - 一元操作符
接受一个参数,返回同类型结果(Function的特例)。
import java.util.function.UnaryOperator;
UnaryOperator<Integer> square = x -> x * x;
int result = square.apply(5); // 25
UnaryOperator<String> addPrefix = s -> "前缀_" + s;
String result2 = addPrefix.apply("测试"); // 前缀_测试BinaryOperator<T> - 二元操作符
接受两个同类型参数,返回同类型结果(BiFunction的特例)。
import java.util.function.BinaryOperator;
BinaryOperator<Integer> max = Integer::max;
int result = max.apply(10, 20); // 20
BinaryOperator<String> longer = (s1, s2) ->
s1.length() > s2.length() ? s1 : s2;
String result2 = longer.apply("short", "very long"); // very long@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// 可以有默认方法
default void printResult(int result) {
System.out.println("结果: " + result);
}
// 可以有静态方法
static Calculator getAddCalculator() {
return (a, b) -> a + b;
}
}
// 使用
Calculator add = (a, b) -> a + b;
int result = add.calculate(10, 20); // 30
add.printResult(result); // 输出: 结果: 304. 方法引用
方法引用是Lambda表达式的简化写法,当Lambda表达式只是调用一个已存在的方法时,可以使用方法引用。
静态方法引用
语法:类名::静态方法名
// Lambda表达式
Function<String, Integer> parseInt1 = s -> Integer.parseInt(s);
// 方法引用
Function<String, Integer> parseInt2 = Integer::parseInt;
// 使用
int num = parseInt2.apply("123"); // 123更多示例:
// Math.max
BinaryOperator<Integer> max1 = (a, b) -> Math.max(a, b);
BinaryOperator<Integer> max2 = Math::max;
// System.out.println
Consumer<String> print1 = s -> System.out.println(s);
Consumer<String> print2 = System.out::println;实例方法引用
语法:对象::实例方法名
String str = "Hello World";
// Lambda表达式
Predicate<String> isEmpty1 = s -> s.isEmpty();
// 方法引用
Predicate<String> isEmpty2 = str::isEmpty;
// 使用
boolean result = isEmpty2.test(""); // true更多示例:
StringBuilder sb = new StringBuilder();
Consumer<String> append1 = s -> sb.append(s);
Consumer<String> append2 = sb::append;类的实例方法引用
语法:类名::实例方法名
// Lambda表达式
Function<String, String> toUpper1 = s -> s.toUpperCase();
// 方法引用
Function<String, String> toUpper2 = String::toUpperCase;
// 使用
String result = toUpper2.apply("hello"); // HELLO更多示例:
// String.length
Function<String, Integer> length1 = s -> s.length();
Function<String, Integer> length2 = String::length;
// String.compareToIgnoreCase
Comparator<String> compare1 = (s1, s2) -> s1.compareToIgnoreCase(s2);
Comparator<String> compare2 = String::compareToIgnoreCase;构造方法引用
语法:类名::new
// Lambda表达式
Supplier<List<String>> list1 = () -> new ArrayList<>();
// 构造方法引用
Supplier<List<String>> list2 = ArrayList::new;
// 使用
List<String> list = list2.get();带参数的构造方法:
// 一个参数
Function<String, StringBuilder> sb1 = s -> new StringBuilder(s);
Function<String, StringBuilder> sb2 = StringBuilder::new;
// 多个参数需要使用BiFunction等
BiFunction<String, Integer, Person> person1 = (name, age) -> new Person(name, age);
BiFunction<String, Integer, Person> person2 = Person::new;- 代码更简洁:比Lambda表达式更短
- 可读性更好:直接表达方法调用
- 性能相同:编译后与Lambda表达式相同
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Lambda表达式
names.forEach(name -> System.out.println(name));
// 方法引用(更简洁)
names.forEach(System.out::println);
// Lambda表达式
names.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
// 方法引用(更简洁)
names.sort(String::compareToIgnoreCase);5. Stream API
Stream是Java8引入的用于处理集合数据的抽象概念。Stream不是数据结构,而是一个数据流,可以对集合进行各种操作。
- 不存储数据:Stream不存储元素,只是对数据源进行操作
- 函数式编程:支持函数式编程风格
- 惰性求值:中间操作是惰性的,只有终端操作才会执行
- 可并行:可以轻松实现并行处理
从集合创建
import java.util.stream.Stream;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);使用Stream.of()
Stream<String> stream = Stream.of("a", "b", "c");使用Stream.generate()
// 生成无限流
Stream<Double> randomStream = Stream.generate(Math::random);
randomStream.limit(10).forEach(System.out::println);使用Stream.iterate()
// 生成序列
Stream<Integer> numbers = Stream.iterate(0, n -> n + 2);
numbers.limit(10).forEach(System.out::println);
// 输出: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18filter - 过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 过滤偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 结果: [2, 4, 6, 8, 10]
// 过滤长度大于3的字符串
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> longNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
// 结果: [Alice, Charlie, David]map - 映射转换
// 字符串转大写
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 结果: [ALICE, BOB, CHARLIE]
// 获取字符串长度
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果: [5, 3, 7]
// 对象属性提取
class Person {
private String name;
private int age;
// 构造方法和getter/setter...
}
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30)
);
List<String> personNames = people.stream()
.map(Person::getName)
.collect(Collectors.toList());flatMap - 扁平化映射
// 将多个列表合并成一个
List<List<String>> lists = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f")
);
List<String> flatList = lists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果: [a, b, c, d, e, f]
// 分割字符串并扁平化
List<String> words = Arrays.asList("Hello World", "Java 8");
List<String> letters = words.stream()
.flatMap(word -> Arrays.stream(word.split(" ")))
.collect(Collectors.toList());
// 结果: [Hello, World, Java, 8]distinct - 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> distinct = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4]sorted - 排序
// 自然排序
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List<Integer> sorted = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 结果: [1, 1, 3, 4, 5, 9]
// 自定义排序
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> sortedByName = names.stream()
.sorted((s1, s2) -> s2.length() - s1.length())
.collect(Collectors.toList());
// 结果: [Charlie, Alice, Bob]
// 使用Comparator
List<String> sorted2 = names.stream()
.sorted(Comparator.comparing(String::length).reversed())
.collect(Collectors.toList());limit - 限制数量
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> first5 = numbers.stream()
.limit(5)
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]skip - 跳过元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> skipped = numbers.stream()
.skip(5)
.collect(Collectors.toList());
// 结果: [6, 7, 8, 9, 10]forEach - 遍历
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);collect - 收集
// 收集到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(
String::toString,
String::length
)
);
// 收集到指定集合
List<String> arrayList = stream.collect(
Collectors.toCollection(ArrayList::new)
);reduce - 归约
// 求和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
System.out.println(sum.get()); // 15
// 带初始值
int sum2 = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum2); // 15
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
System.out.println(max.get()); // 5count - 计数
long count = numbers.stream()
.filter(n -> n % 2 == 0)
.count();
System.out.println(count); // 2anyMatch、allMatch、noneMatch - 匹配
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 是否有元素满足条件
boolean anyEven = numbers.stream()
.anyMatch(n -> n % 2 == 0); // true
// 是否所有元素都满足条件
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0); // false
// 是否没有元素满足条件
boolean noneEven = numbers.stream()
.noneMatch(n -> n > 10); // truefindFirst、findAny - 查找
Optional<Integer> first = numbers.stream()
.filter(n -> n > 3)
.findFirst();
System.out.println(first.get()); // 4
Optional<Integer> any = numbers.stream()
.filter(n -> n > 3)
.findAny();min、max - 最值
Optional<Integer> min = numbers.stream()
.min(Integer::compare);
System.out.println(min.get()); // 1
Optional<Integer> max = numbers.stream()
.max(Integer::compare);
System.out.println(max.get()); // 5分组
List<Person> people = Arrays.asList(
new Person("Alice", 25, "Female"),
new Person("Bob", 30, "Male"),
new Person("Charlie", 25, "Male")
);
// 按年龄分组
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
// 按性别分组
Map<String, List<Person>> byGender = people.stream()
.collect(Collectors.groupingBy(Person::getGender));分区
// 分区(分为true和false两组)
Map<Boolean, List<Person>> partitioned = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 25));统计
// 统计信息
IntSummaryStatistics stats = people.stream()
.mapToInt(Person::getAge)
.summaryStatistics();
System.out.println("平均年龄: " + stats.getAverage());
System.out.println("最大年龄: " + stats.getMax());
System.out.println("最小年龄: " + stats.getMin());
System.out.println("总数: " + stats.getCount());连接字符串
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 简单连接
String joined = names.stream()
.collect(Collectors.joining());
// 结果: AliceBobCharlie
// 带分隔符
String joined2 = names.stream()
.collect(Collectors.joining(", "));
// 结果: Alice, Bob, Charlie
// 带前缀和后缀
String joined3 = names.stream()
.collect(Collectors.joining(", ", "[", "]"));
// 结果: [Alice, Bob, Charlie]案例1:数据处理管道
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 20)
);
// 找出年龄大于25的人的名字,按字母顺序排序,收集到List
List<String> result = people.stream()
.filter(p -> p.getAge() > 25)
.map(Person::getName)
.sorted()
.collect(Collectors.toList());
// 结果: [Bob, Charlie]案例2:复杂统计
// 按部门分组,计算每个部门的平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));6. Optional类
在Java8之前,处理null值容易导致NullPointerException。Optional类提供了一种优雅的方式来处理可能为null的值。
import java.util.Optional;
// 创建空的Optional
Optional<String> empty = Optional.empty();
// 创建非空Optional
Optional<String> nonEmpty = Optional.of("Hello");
// 创建可能为null的Optional
String str = null;
Optional<String> nullable = Optional.ofNullable(str);isPresent() - 判断是否存在
Optional<String> opt = Optional.of("Hello");
if (opt.isPresent()) {
System.out.println(opt.get());
}get() - 获取值
Optional<String> opt = Optional.of("Hello");
String value = opt.get(); // "Hello"
// 如果为空会抛出NoSuchElementException
Optional<String> empty = Optional.empty();
// String value2 = empty.get(); // 抛出异常orElse() - 提供默认值
Optional<String> opt = Optional.empty();
String value = opt.orElse("Default"); // "Default"
Optional<String> opt2 = Optional.of("Hello");
String value2 = opt2.orElse("Default"); // "Hello"orElseGet() - 提供Supplier
Optional<String> opt = Optional.empty();
String value = opt.orElseGet(() -> "Default from Supplier");orElseThrow() - 抛出异常
Optional<String> opt = Optional.empty();
String value = opt.orElseThrow(() -> new RuntimeException("值不存在"));ifPresent() - 如果存在则执行
Optional<String> opt = Optional.of("Hello");
opt.ifPresent(value -> System.out.println("值存在: " + value));map() - 映射转换
Optional<String> opt = Optional.of("hello");
Optional<String> upper = opt.map(String::toUpperCase);
System.out.println(upper.get()); // HELLO
// 如果为空,map不会执行
Optional<String> empty = Optional.empty();
Optional<String> upper2 = empty.map(String::toUpperCase);
System.out.println(upper2.isPresent()); // falseflatMap() - 扁平化映射
Optional<String> opt = Optional.of("hello");
Optional<String> upper = opt.flatMap(s -> Optional.of(s.toUpperCase()));filter() - 过滤
Optional<String> opt = Optional.of("hello");
Optional<String> filtered = opt.filter(s -> s.length() > 3);
System.out.println(filtered.get()); // hello
Optional<String> filtered2 = opt.filter(s -> s.length() > 10);
System.out.println(filtered2.isPresent()); // false避免空指针异常
// 传统方式(容易NPE)
public String getCity(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
return address.getCity();
}
}
return "Unknown";
}
// Optional方式(优雅)
public String getCity(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
}链式调用
Optional<User> user = findUserById(1);
String email = user
.map(User::getEmail)
.filter(e -> e.contains("@"))
.orElse("no-email@example.com");7. 新的日期时间API
Java8之前的java.util.Date和java.util.Calendar存在以下问题:
- 线程不安全
- API设计不友好
- 时区处理复杂
- 可变对象
LocalDate - 日期
import java.time.LocalDate;
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println(today); // 2024-01-15
// 创建指定日期
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate date2 = LocalDate.parse("2024-01-15");
// 获取日期信息
int year = date.getYear(); // 2024
int month = date.getMonthValue(); // 1
int day = date.getDayOfMonth(); // 15
DayOfWeek dayOfWeek = date.getDayOfWeek(); // MONDAY
// 日期计算
LocalDate tomorrow = date.plusDays(1);
LocalDate lastWeek = date.minusWeeks(1);
LocalDate nextMonth = date.plusMonths(1);LocalTime - 时间
import java.time.LocalTime;
// 获取当前时间
LocalTime now = LocalTime.now();
System.out.println(now); // 14:30:45.123
// 创建指定时间
LocalTime time = LocalTime.of(14, 30, 45);
LocalTime time2 = LocalTime.parse("14:30:45");
// 获取时间信息
int hour = time.getHour(); // 14
int minute = time.getMinute(); // 30
int second = time.getSecond(); // 45
// 时间计算
LocalTime later = time.plusHours(2);
LocalTime earlier = time.minusMinutes(30);LocalDateTime - 日期时间
import java.time.LocalDateTime;
// 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now); // 2024-01-15T14:30:45.123
// 创建指定日期时间
LocalDateTime dateTime = LocalDateTime.of(2024, 1, 15, 14, 30, 45);
LocalDateTime dateTime2 = LocalDateTime.parse("2024-01-15T14:30:45");
// 从LocalDate和LocalTime创建
LocalDateTime dt = LocalDate.now().atTime(LocalTime.now());
// 日期时间计算
LocalDateTime later = dateTime.plusDays(1).plusHours(2);Instant - 时间戳
import java.time.Instant;
// 获取当前时间戳
Instant now = Instant.now();
System.out.println(now); // 2024-01-15T06:30:45.123Z
// 从毫秒创建
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
// 转换为毫秒
long millis = instant.toEpochMilli();Duration - 持续时间
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2024, 1, 1, 0, 0);
LocalDateTime end = LocalDateTime.of(2024, 1, 2, 12, 0);
Duration duration = Duration.between(start, end);
System.out.println(duration.toHours()); // 36
System.out.println(duration.toDays()); // 1Period - 日期间隔
import java.time.Period;
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
Period period = Period.between(start, end);
System.out.println(period.getMonths()); // 11
System.out.println(period.getDays()); // 30ZonedDateTime - 时区日期时间
import java.time.ZonedDateTime;
import java.time.ZoneId;
// 获取当前时区的日期时间
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // 2024-01-15T14:30:45+08:00[Asia/Shanghai]
// 指定时区
ZonedDateTime utc = ZonedDateTime.now(ZoneId.of("UTC"));
ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
// 时区转换
ZonedDateTime shanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime tokyo = shanghai.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));import java.time.format.DateTimeFormatter;
LocalDateTime dateTime = LocalDateTime.now();
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
System.out.println(formatted); // 2024-01-15 14:30:45
// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-01-15 14:30:45", formatter);
// 预定义格式
String iso = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);8. 接口默认方法和静态方法
Java8允许接口包含默认方法实现。
public interface Vehicle {
// 抽象方法
void start();
// 默认方法
default void stop() {
System.out.println("车辆停止");
}
// 默认方法可以调用其他方法
default void restart() {
stop();
start();
}
}
// 实现类
public class Car implements Vehicle {
@Override
public void start() {
System.out.println("汽车启动");
}
// 可以选择重写默认方法
@Override
public void stop() {
System.out.println("汽车停止");
}
}public interface MathUtils {
// 静态方法
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
}
// 使用
int sum = MathUtils.add(10, 20);
int product = MathUtils.multiply(5, 6);interface A {
default void method() {
System.out.println("A");
}
}
interface B {
default void method() {
System.out.println("B");
}
}
// 必须重写方法解决冲突
class C implements A, B {
@Override
public void method() {
A.super.method(); // 调用A的方法
B.super.method(); // 调用B的方法
System.out.println("C");
}
}9. CompletableFuture异步编程
CompletableFuture是Java8引入的用于异步编程的类,它实现了Future接口,提供了更强大的异步处理能力。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
// 创建已完成的Future
CompletableFuture<String> future = CompletableFuture.completedFuture("Hello");
// 异步执行
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result";
});
// 获取结果
try {
String result = future2.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
System.out.println(future.get()); // HELLO WORLDCompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
// 组合两个Future
CompletableFuture<String> combined = future1.thenCombine(
future2,
(s1, s2) -> s1 + " " + s2
);
System.out.println(combined.get()); // Hello World10. 并行流和性能优化
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 顺序流
long sum1 = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
// 并行流
long sum2 = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();- 数据量大
- 每个元素处理耗时
- 无状态操作
- 不需要顺序
// 注意:小数据量时并行流可能更慢
List<Integer> smallList = Arrays.asList(1, 2, 3);
// 顺序流可能更快
long sum = smallList.stream().mapToInt(Integer::intValue).sum();11. 实际应用案例
List<Person> people = Arrays.asList(
new Person("Alice", 25, "Engineer"),
new Person("Bob", 30, "Manager"),
new Person("Charlie", 35, "Engineer")
);
// 找出工程师的平均年龄
double avgAge = people.stream()
.filter(p -> "Engineer".equals(p.getJob()))
.mapToInt(Person::getAge)
.average()
.orElse(0.0);// 将List转换为Map
Map<String, Person> personMap = people.stream()
.collect(Collectors.toMap(
Person::getName,
Function.identity()
));// 按职位分组统计
Map<String, Long> countByJob = people.stream()
.collect(Collectors.groupingBy(
Person::getJob,
Collectors.counting()
));12. 总结与最佳实践
- ✅ Lambda表达式:简化代码,支持函数式编程
- ✅ Stream API:强大的数据处理能力
- ✅ Optional:优雅处理空值
- ✅ 新日期时间API:解决旧API问题
- ✅ 接口默认方法:增强接口功能
- ✅ 方法引用:简化Lambda表达式
合理使用Lambda:不要过度使用,保持代码可读性
Stream性能:注意并行流的开销
Optional使用:不要滥用,主要用于返回值
方法引用:优先使用方法引用而非Lambda
函数式编程:理解函数式编程思想
在Lambda中修改外部变量:只能使用final或effectively final变量
并行流的误用:小数据量或需要顺序时不要用并行流
Optional的误用:不要用Optional作为方法参数
过度使用Stream:简单操作用传统方式可能更清晰
多练习:通过实际项目练习
理解原理:理解函数式编程思想
阅读源码:了解实现原理
关注性能:注意性能影响
保持简洁:代码要简洁但可读
结语
Java8的新特性极大地提升了Java语言的表达能力和开发效率。通过本教程的学习,相信你已经掌握了Java8的核心特性。
记住:
- 多实践:理论结合实践,多写代码
- 理解思想:理解函数式编程思想
- 关注性能:注意性能优化
- 持续学习:关注Java新版本特性
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。