Java8新特性
Interface
新特性
函数式编程(Functional Programming, FP)是一种编程范式,它将计算视为数学函数的求值,并强调函数的应用。与命令式编程不同,函数式编程更关注表达式的计算和函数的组合,而不是变量的状态和赋值操作。就像管道符一样,一个函数的输出是下一个函数的输入
Lambda表达式和Stream流都是函数式编程的应用
Java Lambda
Lambda表达式是Java 8引入的一个重要特性,使得我们可以用更加简洁和函数式的方式编写代码。
Lambda表达式可以用于实现函数式接口(即只有一个抽象方法的接口),并在集合操作、并行处理等场景中得到广泛应用。
基本语法
Lambda表达式的基本语法如下:
1 2
| (parameters) -> expression (parameters) -> { statements; }
|
- 参数部分:可以是一个或多个参数。如果只有一个参数且类型可以推断,则可以省略括号。
- 箭头符号:
->
将参数与函数体分开。
- 函数体:可以是单个表达式或一组语句。如果是单个表达式,可以省略大括号和返回关键字。
基础示例
无参数的Lambda表达式
1 2
| Runnable runnable = () -> System.out.println("Hello, Lambda!"); new Thread(runnable).start();
|
单参数的Lambda表达式
1 2
| Consumer<String> consumer = s -> System.out.println(s); consumer.accept("Hello, World!");
|
多参数的Lambda表达式
1 2
| BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b; System.out.println(add.apply(5, 3));
|
使用Lambda表达式操作集合
过滤和映射集合
1 2 3 4 5
| List<String> list = Arrays.asList("one", "two", "three", "four"); list.stream() .filter(s -> s.length() > 3) .map(String::toUpperCase) .forEach(System.out::println);
|
集合排序
1 2 3
| List<String> list = Arrays.asList("apple", "banana", "cherry"); list.sort((a, b) -> a.compareTo(b)); System.out.println(list);
|
函数式接口
常见函数式接口
Java 8 引入了一些常用的函数式接口,位于 java.util.function
包中。下面是一些常用接口及其示例。
Consumer<T>
1 2
| Consumer<String> printer = System.out::println; printer.accept("Hello, Consumer!");
|
Function<T, R>
1 2
| Function<String, Integer> length = String::length; System.out.println(length.apply("Hello"));
|
Supplier<T>
1 2
| Supplier<String> supplier = () -> "Hello, Supplier!"; System.out.println(supplier.get());
|
Predicate<T>
1 2
| Predicate<Integer> isPositive = x -> x > 0; System.out.println(isPositive.test(5));
|
BiFunction<T, U, R>
1 2
| BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b; System.out.println(multiply.apply(3, 4));
|
实战
Runnable
接口
1 2 3 4 5 6 7 8
| new Thread(new Runnable() { @Override public void run() { System.out.println("The runable now is using!"); } }).start();
new Thread(() -> System.out.println("It's a lambda function!")).start();
|
Comparator
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| List<Integer> strings = Arrays.asList(1, 2, 3);
Collections.sort(strings, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2;} });
Collections.sort(strings, (o1, o2) -> o1 - o2);
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 - o2; Collections.sort(strings, comparator);
|
Listener
接口
1 2 3 4 5 6 7 8 9
| JButton button = new JButton(); button.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { e.getItem(); } });
button.addItemListener(e -> e.getItem());
|
Java Stream
Java 8 引入了 Stream API,它提供了一种高效且易于使用的处理数据集合(如集合、数组等)的方式。
Stream API 使得我们能够以声明式的方式编写代码,从而提高代码的可读性和可维护性。
什么是 Stream
Stream 是一个数据元素的序列,它支持各种操作以计算结果。Stream 操作有两种类型:
- 中间操作:返回一个新的 Stream,这些操作是惰性的,只在终端操作执行时才会执行。
- 终端操作:产生一个结果(如
List
、Integer
等),或者执行副作用(如 forEach
)。
Stream 的创建
Stream 可以从多种数据源创建,例如集合、数组、文件等。
从集合创建 Stream
1 2
| List<String> list = Arrays.asList("one", "two", "three"); Stream<String> stream = list.stream();
|
从数组创建 Stream
1 2
| String[] array = {"one", "two", "three"}; Stream<String> stream = Arrays.stream(array);
|
从文件创建 Stream
1 2 3 4 5 6
| Path path = Paths.get("file.txt"); try (Stream<String> lines = Files.lines(path)) { lines.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); }
|
使用 Stream.of 创建 Stream
1
| Stream<String> stream = Stream.of("one", "two", "three");
|
中间操作
中间操作是惰性的,这意味着它们在调用时不会立即执行,而是在终端操作调用时执行。
filter
过滤元素,保留符合条件的元素。
1
| Stream<String> filteredStream = stream.filter(s -> s.length() > 3);
|
map
将每个元素映射为另一个对象。
1
| Stream<Integer> mappedStream = stream.map(String::length);
|
flatMap
将每个元素转换为一个流,然后将这些流合并为一个流。
1
| Stream<String> flatMappedStream = stream.flatMap(s -> Arrays.stream(s.split(" ")));
|
sorted
对元素进行排序。
1
| Stream<String> sortedStream = stream.sorted();
|
distinct
去除重复元素。
1
| Stream<String> distinctStream = stream.distinct();
|
peek
对每个元素执行操作并返回一个新的 Stream。主要用于调试。
1
| Stream<String> peekedStream = stream.peek(System.out::println);
|
终端操作
终端操作会触发流的计算,并且流一旦使用终端操作就不能再使用。
forEach
对每个元素执行操作,无返回值。
1
| stream.forEach(System.out::println);
|
collect
将流转换为其他形式。
1
| List<String> list = stream.collect(Collectors.toList());
|
reduce
将流中的元素组合起来,产生一个单一的值。
1
| Optional<String> concatenated = stream.reduce((s1, s2) -> s1 + s2);
|
count
返回流中元素的个数。
1
| long count = stream.count();
|
anyMatch
判断是否有任意一个元素符合给定的条件。
1
| boolean anyMatch = stream.anyMatch(s -> s.length() > 3);
|
allMatch
判断是否所有元素都符合给定的条件。
1
| boolean allMatch = stream.allMatch(s -> s.length() > 3);
|
noneMatch
判断是否没有任何元素符合给定的条件。
1
| boolean noneMatch = stream.noneMatch(s -> s.length() > 3);
|
findFirst
返回流中的第一个元素。
1
| Optional<String> firstElement = stream.findFirst();
|
findAny
返回流中的任意一个元素(适用于并行流)。
1
| Optional<String> anyElement = stream.findAny();
|
toArray()
将流中的元素收集到一个数组中。
1
| String[] array = stream.toArray(String[]::new);
|
collect(Collectors.toList())
将流中的元素收集到一个列表中。
1
| List<String> list = stream.collect(Collectors.toList());
|
collect(Collectors.toSet())
将流中的元素收集到一个集合中,去除重复元素。
1
| Set<String> set = stream.collect(Collectors.toSet());
|
min()
& max()
返回流中的最小值和最大值。
1 2
| Optional<String> min = stream.min(Comparator.naturalOrder()); Optional<String> max = stream.max(Comparator.naturalOrder());
|
并行流
Stream API 支持并行处理,可以利用多核处理器来提高性能。
1 2
| List<String> list = Arrays.asList("one", "two", "three"); Stream<String> parallelStream = list.parallelStream();
|
实战
计算字符串的总长度
1 2 3 4 5
| List<String> list = Arrays.asList("one", "two", "three"); int totalLength = list.stream() .mapToInt(String::length) .sum(); System.out.println(totalLength);
|
查找最长的字符串
1 2 3 4
| List<String> list = Arrays.asList("one", "two", "three"); Optional<String> longest = list.stream() .max(Comparator.comparingInt(String::length)); longest.ifPresent(System.out::println);
|
过滤、转换并收集结果
1 2 3 4 5 6
| List<String> list = Arrays.asList("one", "two", "three", "four"); List<String> result = list.stream() .filter(s -> s.length() > 3) .map(String::toUpperCase) .collect(Collectors.toList()); System.out.println(result);
|
使用 Stream API 可以很方便地将 List 转换为数组以及将数组转换为 List。以下是两种操作的示例:
将 List 转换为数组
1 2
| List<String> list = Arrays.asList("one", "two", "three"); String[] array = list.stream().toArray(String[]::new);
|
这里的 toArray()
方法接受一个参数,该参数是一个数组构造器引用,用于创建正确大小的数组。
将数组转换为 List
1 2
| String[] array = {"one", "two", "three"}; List<String> list = Arrays.stream(array).collect(Collectors.toList());
|
这里使用了 Arrays.stream()
方法将数组转换为流,并使用 collect(Collectors.toList())
将流中的元素收集到一个列表中。
总结
- Stream API 是 Java 8 中引入的强大工具,使我们能够以声明式和函数式的方式处理集合数据
- 它提供了丰富的操作集合数据的方法,包括中间操作和终端操作,支持顺序和并行处理
- 熟练掌握 Stream API 可以大大提高代码的可读性和性能。
Java Optional
Java 8 引入了 Optional 类,用于解决空指针异常(NullPointerException)问题。
Optional 是一个容器对象,它可以包含或不包含非空值。通过使用 Optional,可以更加优雅地处理可能为 null 的值,避免因为 null 引起的异常。
创建 Optional
创建包含非空值的 Optional
1
| Optional<String> optional = Optional.of("value");
|
创建可能为空的 Optional
1
| Optional<String> optional = Optional.ofNullable(null);
|
检查 Optional 是否包含值
判断 Optional 是否包含值
1
| boolean present = optional.isPresent();
|
如果 Optional 包含值,则返回 true,否则返回 false。
判断 Optional 是否为空
1
| boolean empty = optional.isEmpty();
|
如果 Optional 不包含值,则返回 true,否则返回 false。这是 Java 11 引入的新方法。
获取 Optional 的值
获取 Optional 中的值
1
| String value = optional.get();
|
如果 Optional 包含值,则返回该值,否则抛出 NoSuchElementException 异常。
处理 Optional 的值
如果值存在,则执行操作
1
| optional.ifPresent(val -> System.out.println("Value: " + val));
|
如果 Optional 包含值,则执行给定的操作,否则什么也不做。
如果值不存在,则执行操作
1
| optional.ifEmpty(() -> System.out.println("Value is empty"));
|
如果 Optional 不包含值,则执行给定的操作,否则什么也不做。这是 Java 11 引入的新方法。
获取默认值
获取 Optional 中的值,如果值不存在则返回默认值
1
| String value = optional.orElse("default");
|
如果 Optional 包含值,则返回该值,否则返回指定的默认值。
获取 Optional 中的值,如果值不存在则使用供应函数返回值作为默认值
1
| String value = optional.orElseGet(() -> "default");
|
如果 Optional 包含值,则返回该值,否则调用提供的供应函数,并返回其结果作为默认值。
抛出异常
如果值不存在,则抛出指定的异常
1
| String value = optional.orElseThrow(() -> new IllegalArgumentException("Value is empty"));
|
如果 Optional 包含值,则返回该值,否则抛出指定的异常。
使用 map 转换值
1
| Optional<Integer> lengthOptional = optional.map(String::length);
|
如果 Optional 包含值,则对该值应用给定的函数,并返回包含结果的 Optional;否则返回一个空的 Optional。
过滤值
1
| Optional<String> filteredOptional = optional.filter(val -> val.startsWith("prefix"));
|
如果 Optional 包含值且满足给定的谓词条件,则返回包含该值的 Optional;否则返回一个空的 Optional。
组合多个 Optional
组合两个 Optional
1
| Optional<String> resultOptional = optional1.flatMap(val1 -> optional2.map(val2 -> val1 + val2));
|
如果两个 Optional 都包含值,则应用提供的函数,否则返回一个空的 Optional。
组合多个 Optional
1
| Optional<String> resultOptional = OptionalUtils.combine(optional1, optional2, optional3, ..., (val1, val2, val3, ...) -> );
|
如果所有的 Optional 都包含值,则应用提供的函数,否则返回一个空的 Optional。这个方法需要一个工具类 OptionalUtils
来实现,因为标准库中并没有直接提供类似的组合方法。
示例
使用 Optional 避免空指针异常
1 2
| Optional<String> optional = Optional.ofNullable(getValue()); String result = optional.orElse("default");
|
这里 getValue()
方法可能返回 null,使用 Optional 可以避免空指针异常。
使用 map 转换值
1 2
| Optional<String> optional = Optional.ofNullable(getString()); Optional<Integer> lengthOptional = optional.map(String::length);
|
如果字符串不为 null,则获取其长度;否则返回一个空的 Optional。
总结
Optional 类是 Java 8 中引入的一种处理可能为 null 的值的方式,它提供了一系列方法来更加优雅地处理可能为空的值,避免空指针异常。
虽然使用 Optional 会增加一些代码的复杂性,但它能够提高代码的健壮性和可读性,建议在合适的场景中使用 Optional。
Java Date-Time API
java.time
包解决了 java.util.Date
的大部分痛点
- 非线程安全
- 时区处理麻烦
- 各种格式化、和时间计算繁琐
- 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。
java.time 主要类
java.util.Date
既包含日期又包含时间,而 java.time
把它们进行了分离
1 2 3
| LocalDateTime.class LocalDate.class LocalTime.class
|
LocalDate - 日期
LocalDate
类表示一个日期,不包含时间和时区信息。
创建 LocalDate
1 2
| LocalDate date = LocalDate.now(); LocalDate date = LocalDate.of(2022, 5, 18);
|
获取日期信息
1 2 3
| int year = date.getYear(); int month = date.getMonthValue(); int day = date.getDayOfMonth();
|
操作日期
1 2
| LocalDate tomorrow = date.plusDays(1); LocalDate yesterday = date.minusDays(1);
|
LocalTime - 时间
LocalTime
类表示一个时间,不包含日期和时区信息。
创建 LocalTime
1 2
| LocalTime time = LocalTime.now(); LocalTime time = LocalTime.of(12, 30, 45);
|
获取时间信息
1 2 3
| int hour = time.getHour(); int minute = time.getMinute(); int second = time.getSecond();
|
操作时间
1 2
| LocalTime nextHour = time.plusHours(1); LocalTime prevHour = time.minusHours(1);
|
LocalDateTime - 日期时间
LocalDateTime
类表示一个日期和时间,不包含时区信息。
创建 LocalDateTime
1 2
| LocalDateTime dateTime = LocalDateTime.now(); LocalDateTime dateTime = LocalDateTime.of(2022, 5, 18, 12, 30, 45);
|
获取日期时间信息
1 2
| LocalDate date = dateTime.toLocalDate(); LocalTime time = dateTime.toLocalTime();
|
操作日期时间
1 2
| LocalDateTime nextHour = dateTime.plusHours(1); LocalDateTime prevHour = dateTime.minusHours(1);
|
ZonedDateTime - 带时区的日期时间
ZonedDateTime
类表示一个带有时区的日期时间。
创建 ZonedDateTime
1 2
| ZonedDateTime zonedDateTime = ZonedDateTime.now(); ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Asia/Shanghai"));
|
获取时区信息
1
| ZoneId zoneId = zonedDateTime.getZone();
|
日期时间格式化与解析
Java Date-Time API 提供了 DateTimeFormatter
类用于日期时间的格式化和解析。
日期时间格式化
1 2
| DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedDateTime = dateTime.format(formatter);
|
日期时间解析
1 2
| DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime parsedDateTime = LocalDateTime.parse("2022-05-18 12:30:45", formatter);
|
日期时间间隔
Java Date-Time API 提供了 Duration
和 Period
类来表示时间间隔。
Duration - 时间间隔
1 2
| Duration duration = Duration.between(dateTime1, dateTime2); long seconds = duration.getSeconds();
|
Period - 日期间隔
1 2
| Period period = Period.between(date1, date2); int days = period.getDays();
|
其他常用功能
时间单位
1
| long minutes = ChronoUnit.MINUTES.between(time1, time2);
|
日期调整器
1
| LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth());
|
总结