Lambda 表达式

  • Lambda 表达式:带有参数变量的表达式
  • Lambda 表达式由参数列表、箭头(->)和 Lambda 主体组成
  • 基本语法:(parameters) -> expression(parameters) -> { statements; }(注意表达式与语句的区别)
  • Java 编译器可以从上下文(目标类型)推断出用什么函数式接口来配合 Lambda 表达式,因此可以在 Lambda 语法中省去标注参数类型,当 Lambda 仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略
  • 在 Lambda 主体中引用的局部变量必须使用 final 修饰或事实上最终的(类似匿名内部类)

函数式接口

  • 有且仅有一个抽象方法的接口(@FunctionalInterface 注解用于表示该接口会设计成一个函数式接口),如 Comparator、Runnable、Callable

函数描述符

  • 函数式接口的抽象方法的签名(Lambda 表达式的签名)
  • 特殊的 void 兼容规则:如果一个 Lambda 的主体是一个语句表达式,它就和一个返回 void的函数描述符兼容

使用 Lambda

  • Lambda 表达式以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的一个实例(即 Lambda 表达式是函数式接口一个具体实现的实例)
  • 作用:传递代码片段
  • 注意:只有在接受函数式接口的地方才可以使用 Lambda 表达式

常见的函数式接口

函数式接口抽象方法函数描述符默认方法静态方法
Comparator<T>int compare(T o1, T o2)(T,T) -> intreversed, thenComparingnaturalOrder, comparing, reverseOrder, nullsFirst, nullsLast
Runnablevoid run()() -> void
Callable<V>V call()() -> V
Predicate<T>boolean test(T t)T -> booleanand, or, negate
BiPredicate<T, U>boolean test(T t, U u)(T,U) -> boolean
Consumer<T>void accept(T t)T -> voidandThen
BiConsumervoid accept(T t, U u)(T,U) -> void
Supplier<T>T get()() -> T
Function<T,R>R apply(T t)T -> RandThen, composeidentity
BiFunction<T, U, R>R apply(T t, U u)(T,U) -> R
UnaryOperator<T>T apply(T t)T -> T
BinaryOperator<T>Tt apply(T t1, T t2)(T,T) -> T

方法引用

  • 基本语法:目标引用 :: 方法名
  • 作用:直接传递现有的方法实现
  • 方法引用主要有四类(仅有一个方法调用的 Lambda 方法体)
    • 指向静态方法的方法引用,args -> ClassName.staticMethod(args) 等价 ClassName::staticMethod
    • 指向任意类型的实例方法的方法引用,(arg0, rest) -> arg0.instanceMethod(rest) 等价 ClassName::instanceMethod(arg0 的类型是 ClassName)
    • 指向现有对象的实例方法的方法引用,args -> expr.instanceMethos(args) 等价 expr::instanceMethod
    • 构造函数引用,ClassName::new(需要有无参构造器)

  • java.util.stream.Stream
  • 流:从支持数据处理操作生成的元素序列
  • 集合是一个内存中的数据结构,包含数据结构中目前所有的值,其主要目的是以特定的时间/空间复杂度存储和访问元素
  • 流是在概念上固定的数据结构,其元素是按需计算的(即延迟计算),流的目的在于表达计算

注意:流只能遍历一次


流操作

  • 可分为两类操作

    • 中间操作(intermediate operation):可以连接起来的流操作,中间操作会返回另一个流
    • 终端操作(terminal operation):关闭流的操作,终端操作会从流的流水线生成结果(任何不是流的值)
  • 除非流水线上触发一个终端操作,否则中间操作不会执行任何处理(延迟执行)

  • 流的使用一般包括三件事:

    • 一个数据源(如集合)来执行一个查询
    • 一个中间操作链,形成一条流的流水线
    • 一个终端操作,执行流水线,并能生成结果

使用流

中间操作和终端操作
图 1 中间操作和终端操作

筛选

  • Stream<T> filter(Predicate<T> predicate):用谓词筛选,返回一个包括所有符合谓词的元素的流(用谓词筛选)

  • Stream<T> distinct():返回一个元素各异(根据流所生成元素的 hashCode 和 equals 方法实现)的流(筛选各不相同的元素)

  1. // distinct elements by property or key
  2. static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
  3. Map<Object, Boolean> seen = new ConcurrentHashMap<>();
  4. return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  5. }
  6. list.stream().filter(distinctByKey(b -> b.getName()));

切片

  • Stream<T> limit(long maxSize):返回一个不超过给定长度的流(截短流)
  • Stream<T> skip(long n):返回一个扔掉了前 n 个元素的流(跳过元素)

映射

  • 提取或转换流中的元素

  • Stream<R> map(Function<T, R> mapper):对流中每一个元素应用函数(元素映射为新元素)

  • Stream<R> flatMap(Function<T, Stream<R>> mapper):把一个流中的每个元素都换成另一个,然后把所有转换的流连接起来成为一个流(流的扁平化)(元素映射为流)

    Java_8_flatMap_example
    图 2 Java_8_flatMap_example

查找和匹配

  • boolean allMatch(Predicate<T> predicate):流中的元素是否都能匹配给定的谓词
  • boolean anyMatch(Predicate<T> predicate):流中是否有一个元素能匹配给定的谓词
  • boolean noneMatch(Predicate<T> predicate):流中是否没有任何元素与给定的谓词匹配
  • Optional<T> findAny():返回当前流中的任意元素
  • Optional<T> findFirst():返回当前流中的第一个元素

归约

  • 将流中所有的元素迭代合并成一个结果
  • Optional<T> reduce(BinaryOperator<T> accumulator)
  • T reduce(T identity, BinaryOperator<T> accumulator):初始值 identity,accumulator 将两个流元素结合起来并产生一个新值(适用于不可变的归约)
  • U reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner)
  • Optional<T> min(Comparator<T> comparator):根据提供的 Comparator 返回此流的最小元素
  • Optional<T> max(Comparator<T> comparator):根据提供的 Comparator 返回此流的最大元素
  • R collect(Collector<T, A, R> collector)
  • R collect(Supplier<R> supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner):自定义收集器的供应源 supplier、累加器 accumulator、组合器 combiner

Collector 接口

  1. // T - 流中元素的类型,A - 累加器的类型,R - 收集操作的结果对象的类型
  2. public interface Collector<T, A, R> {
  3. Supplier<A> supplier(); // 建立新的结果容器
  4. BiConsumer<A, T> accumulator(); // 将元素添加到结果容器(累加器是原位更新)
  5. BinaryOperator<A> combiner(); // 合并两个结果容器
  6. Function<A, R> finisher(); // 对结果容器应用最终转换
  7. Set<Collector.Characteristics> characteristics(); // 定义收集器的行为:UNORDERED、CONCURRENT、IDENTITY_FINISH
  8. // UNORDERED——归约结果不受流中元素的遍历和累积顺序的影响
  9. // CONCURRENT——accumulator 函数可以从多个线程同时调用,且该收集器可以并行归约流(如果收集器没有标为 UNORDERED,则仅在用于无序数据源时才可以并行归约)
  10. // IDENTITY_FINISH——完成器方法返回的函数是一个恒等函数,可以跳过,即累加器对象 A 将会直接用作归约过程的最终结果 R
  11. }

自定义收集

  1. // 方法 1:自定义 Collector
  2. public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
  3. @Override
  4. public Supplier<List<T>> supplier() {
  5. return ArrayList::new; // 创建集合操作的起始点
  6. }
  7. @Override
  8. public BiConsumer<List<T>, T> accumulator() {
  9. return List::add; // 累积遍历过的项目,原位修改累加器
  10. }
  11. @Override
  12. public BinaryOperator<List<T>> combiner() {
  13. return (list1, list2) -> {
  14. list1.addAll(list2); // 修改第一个累加器,将其与第二个累加器的内容合并
  15. return list1; // 返回修改后的第一个累加器
  16. };
  17. }
  18. @Override
  19. public Function<List<T>, List<T>> finisher() {
  20. return Function.identity(); // 恒等函数
  21. }
  22. @Override
  23. public Set<Characteristics> characteristics() {
  24. // 为收集器添加 IDENTITY_FINISH和 CONCURRENT标志
  25. return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
  26. }
  27. }
  28. List<Dish> dishes = menuStream.collect(new ToListCollector<Dish>());
  29. // 方法 2:对于 CONCURRENT 和 IDENTITY_FINISH 但并非 UNORDERED 的收集操作
  30. List<Dish> dishes = menuStream.collect(ArrayList::new, List::add, List::addAll);

预定义收集器

  • Collectors 实用类中提供了很多静态工厂方法用于创建常见的收集器 collector 的实例,这些收集器主要提供三大功能

  • 将流元素归约和汇总为一个值

    • 计算流中元素的个数:Collectors.counting
    • 最大值和最小值:Collectors.maxBy、Collectors.minBy,返回类型是 Optional
    • 汇总:求和 Collectors.summingInt、Collectors.summingLong、Collectors.summingDouble;求平均数 Collectors.averagingInt、Collectors.averagingLong、Collectors.averagingDouble
    • 收集关于流中元素 Integer 属性的统计值(元素个数、总和、平均值、最大值、最小值等):Collectors.summarizing
    • 连接对流中每个元素调用 toString 方法所生成的字符串:Collectors.joining
    • 广义的归约汇总Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op)Collector<T, ?, U> reducing(U identity, Function<T, U> mapper, BinaryOperator<U> op):归约操作的起始值 identity(也是流中没有元素时的返回值),转换函数 mapper,累积函数 op 将两个转换得到的结果累积成一个同类型的值(使用于可变的归约)
  • 元素分组

    • Collector<T, ?, Map<K, List<T>>> groupingBy(Function<T, K> classifier):分组函数 classifier 把流中的元素分成不同的组,把分组函数返回的值作为映射的键,把流中所有具有这个分类值的元素的列表作为对应的映射值,groupingBy(f) 实际上是 groupingBy(f, toList()) 的简便写法(注意:分组函数的返回值即映射的键不能为 null
    • Collector<T, ?, Map<K, D>> groupingBy(Function<T, K> classifier, Collector<T, A, D> downstream)下游的收集器 downstream分到同一组中的所有流元素执行进一步归约操作,可实现多级分组按子组收集数据
  1. Map<String, Map<String, Summary>> sumMap = stuList.stream()
  2. .collect(Collectors.groupingBy(Student::getClassName,
  3. Collectors.groupingBy(Student::getGender,
  4. Collectors.collectingAndThen(Collectors.toList(), stus -> {
  5. int score1Total = 0, score2Total = 0;
  6. for (Student student : stus) {
  7. score1Total += student.getScore1();
  8. score2Total += student.getScore2();
  9. }
  10. // int score1Total = stus.stream().mapToInt(Student::getScore1).sum();
  11. // int score2Total = stus.stream().mapToInt(Student::getScore2).sum();
  12. Student stu = stus.get(0);
  13. return new Summary(stu.getClassName(), stu.getGender(), score1Total, score2Total);
  14. }))
  15. ));
  • 元素分区

    • Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<T> predicate):分区函数 predicate,得到的分组 Map 的键类型是 Boolean,最多可以分为两组
    • Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<T> predicate, Collector<T, A, D> downstream)
  • 其它

    • Collector<T, ?, List<T>> toList():把流中所有元素收集到一个 List
    • Collector<T, ?, Set<T>> toSet():把流中所有元素收集到一个 Set,删除重复项
    • Collector<T, ?, Map<K, U>> toMap(Function<T, K> keyMapper, Function<T, U> valueMapper)
    • Collector<T, ?, Map<K, U>> toMap(Function<T, K> keyMapper, Function<T, U> valueMapper, BinaryOperator<U> mergeFunction)
    • Collector<T, ?, M> toMap(Function<T, K> keyMapper, Function<T, U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier):keyMapper - 产生 key 的映射函数;valueMapper - 产生 value 的映射函数(产生的 value 不能为 null);mergeFunction - 一个合并函数,用于解决与相同 key 相关联的 value 之间的冲突,提供给 Map.merge(Object, Object, BiFunction);mapSupplier - 返回一个新的空的 Map 的函数,其中将插入结果
    • Collector<T, ?, C> toCollection(Supplier<C> collectionFactory):把流中所有项目收集到给定的供应源创建的集合
    • Collector<T, A, RR> collectingAndThen(Collector<T, A, R> downstream, Function<R, RR> finisher)包裹另一个收集器,对其结果应用转换函数 finisher 把收集器的结果转换为另一种类型
    • Collector<T, ?, R> mapping(Function<T, U> mapper, Collector<U, A, R> downstream):mapper 对流中的元素做变换,downstream 将变换的结果对象收集起来
      Collectors类中的静态工厂方法
      图 3 Collectors类中的静态工厂方法

其它

  • long count():返回此流中的元素数
  • void forEach(Consumer<T> action):对此流的每个元素执行操作
  • void forEachOrdered(Consumer<T> action):按流中元素的顺序对此流的每个元素执行操作
  • Stream<T> sorted():返回由此流的元素组成的流,根据自然顺序排序(要求流中元素的类型必须实现 Comparable 接口)
  • Stream<T> sorted(Comparator<T> comparator):返回由此流的元素组成的流,根据提供的 Comparator 进行排序
  • S unordered():把有序流变成无序流,S 是 BaseStream 的实现类型
  • Stream<T> peek(Consumer<T> action):返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作(拆分)
  • Object[] toArray():返回一个包含此流的元素的数组

静态方法

  • Stream<T> concat(Stream<T> a, Stream<T> b):合并两个流

数值流

  • 流有三种基本的原始类型特化:IntStream、DoubleStream 和 LongStream,流中的元素类型分别为 int、long 和 double

  • 原始类型流映射到数值流

    • IntStream mapToInt(ToIntFunction<T> mapper)
    • LongStream mapToLong(ToLongFunction<T> mapper)
    • DoubleStream mapToDouble(ToDoubleFunction<T> mapper)
  • 数值流转换回对象流

    • Stream<Integer> boxed()Stream<Long> boxed()Stream<Double> boxed():装箱
    • Stream<U> mapToObj(IntFunction<U> mapper)Stream<U> mapToObj(LongFunction<U> mapper)Stream<U> mapToObj(DoubleFunction<U> mapper)
  • 数值流之间的相互转换

    • LongStream mapToLong(IntToLongFunction mapper)IntStream mapToInt(LongToIntFunction mapper)
    • DoubleStream mapToDouble(IntToDoubleFunction mapper)DoubleStream mapToDouble(LongToDoubleFunction mapper)IntStream mapToInt(DoubleToIntFunction mapper)LongStream mapToLong(DoubleToLongFunction mapper)

类方法

  • IntStream range(int startInclusive, int endExclusive)LongStream range(long startInclusive, long endExclusive):生成某个范围内的数字,不包含结束值
  • IntStream rangeClosed(int startInclusive, int endInclusive)LongStream rangeClosed(long startInclusive, long endInclusive):生成某个范围内的数字,包含结束值

实例方法

  • OptionalInt max()OptionalLong max()OptionalDouble max():求最大值
  • OptionalInt min()OptionalLong min()OptionalDouble min():求最大值
  • int sum()long sum()double sum():求和,默认返回 0

构建流

  • 从集合创建

    • 使用 Collection 接口中的抽象方法 Stream<E> stream()创建一个流
  • 由值创建流

    • 使用 Stream 类中静态方法 Stream.of(T… values) 通过显式值创建一个流
    • 使用 Stream 类中静态方法Stream.empty(),创建一个空流
  • 由数组创建流

    • 使用 Arrays 类中静态方法 Arrays.stream(T[] array) 从数组创建一个流
  • 由文件生成流

    • 使用 Files 类中静态方法Files.lines(Path path, Charset cs),返回一个由指定文件中的各行构成的字符串流
  • 由函数生成流:创建无限流

    • 迭代:使用 Stream 类中静态方法 Stream.iterate(T seed, UnaryOperator<T> f),依次对每个新生成的值应用函数 f,流的第一个元素是初始值 seed
    • 生成:使用 Stream 类中静态方法 Stream.generate(Supplier<T> s):流的每个元素由 s 提供

并行流

  • parallelStream()
  • parallel():把顺序流转成并行流
  • sequential():把并行流转成顺序流

  • 并行流背后使用的基础架构是 Java 7 中引入的 Fork/Join 分支/合并框架(以递归方式将可以并行的任务拆分成更小的任务,在不同的线程上执行,然后将每个子任务的结果合并起来生成整体结果)

  • 默认的线程池是 ForkJoinPool,默认的线程数量是处理器数量(Runtime.getRuntime().availableProcessors()

Optional<T>

  • Optional 类(java.util.Optional)是一个容器类,代表一个值存在或不存在
  • 基础类型的 Optional 对象:OptionalInt、OptionalLong、OptionalDouble

静态方法

  • Optional<T> empty():返回一个空的 Optional 实例
  • Optional<T> of(T value):返回具有指定非空值的 Optional 实例,如果该值为 null,则抛出一个 NullPointerException 异常
  • Optional<T> ofNullable(T value):返回一个指定值的 Optional,如果值为 null,则返回一个空的 Optional

实例方法

  • boolean isPresent():在值存在时就返回 true,否则返回 false
  • void ifPresent(Consumer<T> block):在值存在的时候执行给定的代码块,否则什么也不做
  • Optional<U> map(Function<T, U> mapper):如果值存在,就对该值执行提供的 mapping 函数调用,否则返回一个空的 Optional 对象
  • Optional<U> flatMap(Function<T, Optional<U>> mapper):如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象(将两层的 Optional 对象转换为单一 Optional 对象)
  • Optional<T> filter(Predicate<T> predicate):如果值存在并且满足提供的谓词,就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象
  • T get():在值存在时返回值,否则抛出一个 NoSuchElementException 异常
  • T orElse(T other):返回值(如果存在),否则返回 other
  • T orElseGet(Supplier<T> other):返回值(如果存在),否则调用 other 并返回该调用的结果
  • T orElseThrow(Supplier<X> exceptionSupplier):返回包含的值(如果存在),否则抛出由 exceptionSupplier 创建的异常

新的日期和时间 API

  • java.time 包中,日期-时间对象是不可变的
  • Temporal 的实现类:Instant(代表一个具体的时刻)、LocalDate(代表不带时区的日期)、LocalTime(代表不带时区的时间)、LocalDateTime(代表不带时区的日期、时间)、Year(代表年)、YearMonth(代表年月)、ZonedDateTime(代表带相对于指定时区的日期、时间)
  • TemporalAmount 的实现类:Duration(代表以秒和纳秒衡量的时长)、Period(代表以年、月、日衡量的时长)
  • ZoneId(代表一个时区)、ZoneOffset(ZoneId 的子类,代表与 UTC/格林尼治时间的绝对偏差)
  • Clock(用于获取指定时区的当前日期、时间)
  • DayOfWeek(星期枚举类)、Month(月份枚举类)
  • ChronoUnit(时间单位枚举类):YEARS、MONTHS、WEEKS、DAYS、HOURS、MINUTES、SECONDS、NANOS 等
  • 格式器类:DateTimeFormatter,其所有实例都是线程安全的
  • 根据模式字符串来创建 DateTimeFormatter 格式器,类方法:DateTimeFormatter ofPattern(String pattern)
  • 使用 DateTimeFormatter 格式化为字符串
    • 调用 DateTimeFormatter 对象的 format(TemporalAccessor temporal) 将日期、时间(LocalDate、LocalDateTime、LocalTime 等实例)格式化为字符串
    • 调用 LocalDate、LocalDateTime、LocalTime 等日期、时间对象的 format(DateTimeFormatter formatter) 方法执行格式化
  • 使用 DateTimeFormatter 解析字符串通过日期、时间对象提供的 parse(CharSequence text)parse(CharSequence text, DateTimeFormatter formatter) 方法解析日期、时间字符串(默认的格式是 DateTimeFormatter.ISO_LOCAL_DATE_TIME、DateTimeFormatter.ISO_LOCAL_DATE、DateTimeFormatter.ISO_LOCAL_TIME,如 "2011-12-03T10:15:30.1234","T" 不区分大小写,秒和纳秒可选)
  1. // 获取当前日期
  2. LocalDate today = LocalDate.now();
  3. today.lengthOfMonth(); // today.get(ChronoField.DAY_OF_MONTH);
  4. // 判断是否时闰年
  5. today.isLeapYear();
  6. // 获取当前时间
  7. LocalDateTime now = LocalDateTime.now();
  8. // 设置时间
  9. LocalDateTime dateTime = LocalDateTime.of(2018, 10, 1, 23, 59, 59);
  10. // 获取年、月、日、时、分、秒
  11. dateTime.getYear(); // dateTime.get(ChronoField.YEAR);
  12. dateTime.getMonth().getValue(); // dateTime.get(ChronoField.MONTH_OF_YEAR);
  13. dateTime.getDayOfMonth(); // dateTime.get(ChronoField.DAY_OF_MONTH);
  14. dateTime.getHour();
  15. dateTime.getMinute();
  16. dateTime.getSecond();
  17. // 调整日期/时间,返回修改了属性的新对象
  18. dateTime.withDayOfMonth(25); // dateTime.with(ChronoField.DAY_OF_MONTH, 25)
  19. // 当月的最后一天
  20. dateTime.with(TemporalAdjusters.lastDayOfMonth());
  21. // 比较先后
  22. dateTime.isAfter(now);
  23. dateTime.isBefore(now);
  24. // 加减时间
  25. dateTime.plusDays(1); // dateTime.plus(1, ChronoUnit.DAYS);
  26. dateTime.minusDays(1);
  27. // 格式化
  28. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  29. // LocalDateTime 转 String
  30. formatter.format(dateTime); // dateTime.format(formatter);
  31. // String 转 LocalDateTime
  32. LocalDateTime.parse("2018-10-01 23:59:59", formatter);
  33. // LocalDateTime 转 LocalDate、LocalTime
  34. LocalDate localDate = dateTime.toLocalDate();
  35. LocalTime localTime = dateTime.toLocalTime();
  36. // LocalDate、LocalTime 转 LocalDateTime
  37. LocalDateTime localDateTime = localDate.atStartOfDay();
  38. LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
  39. // 获取当前时间戳
  40. long millisecond = Instant.now().toEpochMilli(); // 转换当前时间的毫秒值
  41. long second = Instant.now().getEpochSecond(); // 获取当前时间的秒数
  42. Instant.ofEpochMilli(millisecond); // 毫秒值转 Instant
  43. // 将此日期转换为从 1970-01-01 开始的天数
  44. dateTime.toEpochDay();
  45. // 将此日期时间转换为从 1970-01-01T00:00:00Z 开始的秒数
  46. dateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
  47. // LocalDateTime 转换为 ZonedDateTime,再转换为 Instant,再转换为 Date
  48. Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
  49. // Date 转换为 Instant,再转换为 LocalDateTime
  50. LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
  51. // 获取相差时间间隔
  52. amount = start.until(end, ChronoUnit.DAYS); // end.toEpochDay() - start.toEpochDay()
  53. amount = ChronoUnit.DAYS.between(start, end);
  54. // 时间长度:Duration、Period
  55. Duration d1 = Duration.between(time1, time2);
  56. Duration d1 = Duration.between(dateTime1, dateTime2);
  57. Duration d2 = Duration.between(instant1, instant2);
  58. Period tenDays = Period.between(date1, date2);
  59. Period sixMonths = Period.ofMonths(6);
  60. Duration sixSeconds = Duration.ofSeconds(6);
  61. dateTime.plus(sixMonths);
  62. dateTime.plus(sixSeconds);
  63. // 时区 ID
  64. ZoneId zoneId = ZoneId.systemDefault();
  65. // ZoneId zoneId = TimeZone.getDefault().toZoneId();
  66. // ZoneId romeZone = ZoneId.of("Asia/Shanghai");
  67. zoneId.getId(); // 时区 ID:Asia/Shanghai
  68. zoneId.getRules(); // 时区规则:ZoneRules[currentStandardOffset=+08:00]
  69. // ZonedDateTime 有 LocalDateTime 几乎相同的方法,不同的是它可以设置时区
  70. ZoneId zoneId = ZoneId.of("UTC+8");
  71. ZonedDateTime zonedDateTime = ZonedDateTime.of(2018, 10, 1, 23, 59, 59, 1234, zoneId);
  72. // 为时间点添加时区信息
  73. ZonedDateTime zdt1 = date.atStartOfDay(zoneId); // 时间为 00:00:00
  74. ZonedDateTime zdt2 = dateTime.atZone(zoneId);
  75. ZonedDateTime zdt3 = instant.atZone(zoneId);

表示时间点的日期-时间类的通用方法
图 4 表示时间点的日期-时间类的通用方法

日期-时间类中表示时间间隔的通用方法
图 5 日期-时间类中表示时间间隔的通用方法

  • TemporalAdjusters 工厂类中返回 TemporalAdjuster(调整器)实例的静态方法

    • firstDayOfMonth():当月的第一天
    • lastDayOfMonth():当月的最后一天
    • firstDayOfNextMonth():下月的第一天
    • lastInMonth(DayOfWeek dayOfWeek):下月的最后一天
    • firstDayOfNextYear():明年的第一天
    • firstDayOfYear():当年的第一天
    • lastDayOfYear():今年的最后一天
    • dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek):同一个月中,第几个符合星期几要求的值
    • firstInMonth(DayOfWeek dayOfWeek):同一个月中,第一个符合星期几要求的值
    • lastInMonth(DayOfWeek dayOfWeek):同一个月中,最后一个符合星期几要求的值
    • previous(DayOfWeek dayOfWeek):在当前日期之前第一个符合指定星期几要求的日期
    • next(DayOfWeek dayOfWeek):在当前日期之后第一个符合指定星期几要求的日期
    • previousOrSame(DayOfWeek dayOfWeek):在当前日期之后第一个符合指定星期几要求的日期,如果当前日期已经符合要求,直接返回该对象
    • nextOrSame(DayOfWeek dayOfWeek):在当前日期之后第一个符合指定星期几要求的日期,如果当前日期已经符合要求,直接返回该对象

    DateTimeFormatter_Predefined_Formatters
    图 6 DateTimeFormatter_Predefined_Formatters

Base64

  • Base64 是一种用 64 个字符来表示任意二进制数据的方法
  • Base64 编码会把 3 字节的二进制数据编码为 4 字节的文本数据,,长度比原来增加 1/3
  • java.util.Base64 工具类提供的静态方法用于获取以下三种 Base64 编码方案的编解码器(Base64.Encoder、Base64.Decoder)
    • Basic(RFC4648):输出被映射到一组字符 A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持 A-Za-z0-9+/
    • URL and Filename safe(RFC4648URLSAFE):输出映射到一组字符 `A-Za-z0-9+`,输出是 URL 和文件
    • MIME(RFC2045):输出映射到 MIME 友好格式,即输出每行不超过 76 个字符,并且使用 '\r' 并跟随 '\n' 作为分割,编码输出最后没有行分割

内部类

  • Base64.Encoder,实例方法:String encodeToString(byte[] src):使用 Base64 编码方案将指定的字节数组编码为字符串 int encode(byte[] src, byte[] dst):使用 Base64 编码方案对来自指定字节数组的所有字节进行编码,将生成的字节写入给定的输出字节数组 byte[] encode(byte[] src):使用 Base64 编码方案将指定字节数组中的所有字节编码为新分配的字节数组 OutputStream wrap(OutputStream os):使用 Base64 编码方案包装用于编码字节数据的输出流

  • Base64.Decoder,实例方法byte[] decode(String src):使用 Base64 编码方案将 Base64 编码的字符串解码为新分配的字节数组 byte[] decode(byte[] src):使用 Base64 编码方案从输入字节数组中解码所有字节,将结果写入新分配的输出字节数组 int decode(byte[] src, byte[] dst):使用 Base64 编码方案从输入字节数组中解码所有字节,将结果写入给定的输出字节数组 InputStream wrap(InputStream is):返回一个输入流,用于解码 Base64 编码字节流

其它语言新特性

注解

重复注解

  • 要求:将注解标记为 @Repeatable,提供一个注解的容器

类型注解

  • 从 Java 8 开始,注解已经能应用于任何类型,包括 new 操作符、类型转换、instanceof 检查、泛型类型参数,以及 implements 和 throws 子句

通用目标类型推断