我们可以用匿名内部类来比较两个Integer参数的大小
Comparator com = new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1, o2);}
};TreeSet treeSet = new TreeSet<>(com)
上面代码中, 真正有用的代码只有一行,其余都是冗余的
return Integer.compare(o1, o2);
用Lambda表达式就可以用更简洁的代码实现这一功能
Comparator com = (x, y) -> Integer.compare(x, y);
TreeSet treeSet = new TreeSet<>((x, y) -> Integer.compare(x, y));
使用 Lambda 表达式结合 Stream API, 只要给出相应的集合, 我们就可以完成对集合的各种过滤并输出结果信息。
Lambda 表达式在 Java 语言中引入了 “->” 操作符, “->” 操作符被称为 Lambda 表达式
的操作符或者箭头操作符, 它将 Lambda 表达式分为两部分:
只包含一个抽象方法的接口, 称为函数式接口。
可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常, 那么该异常需要在目标接口的抽象方法上进行声明)
可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口, 同时 javadoc 也会包含一条声明, 说明这个接口是一个函数式接口。
四大核心函数式接口

orElse和orElseGet的区别 :
String text = "hello";System.out.println("============ Using orElseGet: ===============");String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultName);System.out.println("============ Using orElse : ===============");defaultText = Optional.ofNullable(text).orElse(getDefaultName());public String getDefaultName() {System.out.println("Getting Default Name");return "default string";}
执行结果如下:

当使用 orElseGet()方法时, getDefaultName()方法并不执行, 因为 Optional 中含有值, 而使用 orElse 时则照常执行。
结论: 当值存在时, orElse 相比于 orElseGet多创建了一个对象
当要传递给 Lambda 体的操作, 已经有实现的方法了, 可以使用方法引用!
这里需要注意的是:实现抽象方法的参数列表, 必须与方法引用方法的参数列表保持一致!
那么什么是方法引用呢? 方法引用就是操作符“::” 将方法名和对象或类的名字分隔开来
当需要引用方法的第一个参数是调用对象, 并且第二个参数是需要引用方法的第二个参
数(或无参数)时: ClassName::methodName 。
ClassName::new
eg.
Function fun = (n) -> new MyClass(n);
//等同于:
Function fun = MyClass::new;//数组引用
type[]::new
Stream 是 Java8 中处理集合的关键抽象概念, 它可以指定你希望对集合进行的操作, 可以执行非常复杂的查找、 过滤和映射数据等操作。
流是数据渠道, 用于操作数据源(集合、 数组等) 所生成的元素序列。 “集合讲的是数据, 流讲的是计算! ”
注意:
① Stream 自己不会存储元素。
② Stream 不会改变源对象。 相反, 他们会返回一个持有结果的新 Stream。
③ Stream 操作是延迟执行的。 这意味着他们会等到需要结果的时候才执行。
//iterate 通过迭代的方式创建无限流,源码如下
public static Stream iterate(final T seed, final UnaryOperator f) {Objects.requireNonNull(f);final Iterator iterator = new Iterator() {@SuppressWarnings("unchecked")T t = (T) Streams.NONE;@Overridepublic boolean hasNext() {return true;} @Overridepublic T next() {return t = (t == Streams.NONE) ? seed : f.apply(t);}return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator,Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
};
public static Stream iterate(final T seed, final UnaryOperator f)
iterate这个函数有2个参数,第一个参数是基值,第二个参数是基于第一个参数的运算函数。
eg.如下会生成自然数序列
Stream limit = Stream.iterate(0, n -> n +1).limit(10);
//generate主要通过生成的方式创建无限流
public static Stream generate(Supplier s) {Objects.requireNonNull(s);return StreamSupport.stream(new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
//generate
public static Stream generate(Supplier s)
generate方法返回一个无限连续的无序流,其中每个元素由提供的Supplier生成。
generate方法用于生成常量流和随机元素流。
多个中间操作可以连接起来形成一个流水线, 除非流水线上触发终止操作, 否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理, 称为“惰性求值
entityList.stream().map(Entity::getTag).flatMap(Collection::stream)
查找与匹配
规约操作 :
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional op = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op.get());
收集

//去掉重复的任务(根据taskId) 这里用到了collectingAndThen()根据属性进行去重的操作,进行结果集的收集,然后将收集到的结果集进行下一步处理
newList = typePoList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(TargetEntity::getTaskId))), ArrayList::new));public static Collector collectingAndThen(Collector downstream, Function finisher)第一个参数是Collector接口的子类,还是对于Collector的处理,Collectors工具类里面的toList()、toSet()、joining()、mapping()、collectingAndThen()等几乎所有的方法都可以使用。
第二个参数是一个Function函数,Function函数是这样的:R apply(T t),这里将第一个参数downstream的结果集,交给第二个参数Function函数处理,例子里也就是将collection转换成List
流进行了终止操作后, 不能再次使用
Java 8 中允许接口中包含具有具体实现的方法, 该方法称为“默认方法” , 默认方法使用
default 关键字修饰
默认方法的原则:
在 Java8 中, 默认方法具有**“类优先”** 的原则。
若一个接口中定义了一个默认方法, 而另外一个父类或接口中又定义了一个同名的方法时, 遵循如下的原则。
选择父类中的方法。 如果一个父类提供了具体的实现, 那么接口中具有相同名称
和参数的默认方法会被忽略。
.接口冲突。 如果一个父接口提供一个默认方法, 而另一个接口也提供了一个具有
相同名称和参数列表的方法(不管方法是否是默认方法) , 那么必须覆盖该方法来解
决冲突。
public interface MyFunction{default String getName(){return "function";}
}public interface MyInterface{default String getName(){return "interface";}
}
实现类 MyClass 同时实现了 MyFunction 接口和 MyInterface 接口, 由于 MyFunction 接口和MyInterface 接口中都存在 getName()默认方法, 所以, MyClass 必须覆盖getName()方法来解决冲突
public class MyClass implements MyInterface,MyFunction{@Overridepublic String getName(){return MyInterface.super.getName();}
}
在 Java8 中, 接口中允许添加静态方法, 使用方式 接口名.方法名
public interface MyFunction{default String getName(){return "binghe";}static void send(){System.out.println("Send Message...");}
}MyFunction.send();
LocalDate、 LocalTime、 LocalDateTime 类的实例是不可变的对象.它们提供了简单的日期或时间, 并不包含当前的时间信息,也不包含和时区相关的信息
// 获取当前系统时间LocalDateTime localDateTime1 = LocalDateTime.now();System.out.println(localDateTime1);// 2022-12-03T22:10:12.179// 指定日期时间LocalDateTime localDateTime2 = LocalDateTime.of(2022, 10, 27, 22, 10,10);System.out.println(localDateTime2);// 2022-10-27T22:10:10LocalDateTime localDateTime3 = localDateTime1.plusYears(3).minusMonths(3);System.out.println(localDateTime3);//2025-09-03T22:10:53.235System.out.println(localDateTime1.getYear()); // 运行结果: 2022System.out.println(localDateTime1.getMonthValue()); // 运行结果: 12System.out.println(localDateTime1.getDayOfMonth()); // 运行结果: 3System.out.println(localDateTime1.getHour()); // 运行结果: 22System.out.println(localDateTime1.getMinute()); // 运行结果: 10System.out.println(localDateTime1.getSecond()); // 运行结果: 53LocalDateTime localDateTime4 = LocalDateTime.now();System.out.println(localDateTime4); // 22022-12-03T22:10:53.236LocalDateTime localDateTime5 = localDateTime4.withDayOfMonth(10);System.out.println(localDateTime5); // 2022-12-10T22:10:53.236
Instant instant1 = Instant.now(); //默认获取UTC时区的时间System.out.println(instant1);//2022-12-03T14:14:36.305Z// 偏移量运算OffsetDateTime offsetDateTime = instant1.atOffset(ZoneOffset.ofHours(8));System.out.println(offsetDateTime);//2022-12-03T22:14:36.305+08:00// 获取时间戳System.out.println(instant1.toEpochMilli());//1670076876305//以Unix元年为起点,进行偏移量运算Instant instant2 = Instant.ofEpochSecond(60);System.out.println(instant2);//1970-01-01T00:01:00Z
Duration:用于计算两个“时间” 间隔。
Period:用于计算两个“日期” 间隔
LocalTime time1 = LocalTime.now();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}LocalTime time2 = time1.plusSeconds(1);Duration duration = Duration.between(time1, time2);System.out.println(duration.toMillis());// 运行结果: 1000LocalDate localDate_1 = LocalDate.of(2018,9, 9);LocalDate localDate_2 = LocalDate.now();Period period = Period.between(localDate_1, localDate_2);System.out.println(period.getYears()); // 运行结果: 4System.out.println(period.getMonths()); // 运行结果: 2System.out.println(period.getDays()); // 运行结果: 24
时间校正其TemporalAdjuster eg.将时间调整到下周日
LocalDate nextSunday = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY))//自定义 下一个工作日LocalDateTime localDateTime2 = localDateTime1.with(l -> {LocalDateTime localDateTime = (LocalDateTime) l;DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {return localDateTime.plusDays(3);} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {return localDateTime.plusDays(2);} else {return localDateTime.plusDays(1);}});
java.time.format.DateTimeFormatter 类: 该类提供了三种格式化方法:
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ISO_DATE;LocalDateTime localDateTime = LocalDateTime.now();String strDate1 = localDateTime.format(dateTimeFormatter1);System.out.println(strDate1);//2022-12-03// Date -> StringDateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String strDate2 = dateTimeFormatter2.format(localDateTime);System.out.println(strDate2);//2022-12-03 22:38:44// String -> DateLocalDateTime localDateTime2 = localDateTime.plusDays(2);System.out.println(localDateTime2);//2022-12-05T22:38:44.483LocalDateTime localDateTime1 = localDateTime2.parse(strDate2, dateTimeFormatter2);System.out.println(localDateTime1);//2022-12-03T22:38:44
注意下: localDateTime2.parse(strDate2, dateTimeFormatter2); 这里实际转换的时间是strDate2的时间
Java8 中加入了对时区的支持, 带时区的时间为分别为: ZonedDate、 ZonedTime、 ZonedDateTime。
每个时区都对应着 ID, 地区 ID 都为 “{区域}/{城市}” 的格式, 例如 : Asia/Shanghai
等。
//获取所有时区信息ZoneId.getAvailableZoneIds();// 通过时区构建LocalDateTimeLocalDateTime localDateTime1 = LocalDateTime.now(ZoneId.of("America/El_Salvador"));System.out.println(localDateTime1);//2022-12-03T08:43:33.981// 以时区格式显示时间LocalDateTime localDateTime2 = LocalDateTime.now();ZonedDateTime zonedDateTime1 = localDateTime2.atZone(ZoneId.of("Asia/Shanghai"));System.out.println(zonedDateTime1);// 2022-12-03T22:43:33.986+08:00[Asia/Shanghai]

Java 8 对注解处理提供了两点改进: 可重复的注解及可用于类型的注解。
@Repeatable(BingheAnnotations.class)注解
@Repeatable(BingheAnnotations.class)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMET
ER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface BingheAnnotation {String value();
}@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMET
ER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface BingheAnnotations {BingheAnnotation[] value();
}
在 BingheAnnotations 注解类中, 定义了一个 BingheAnnotation 注解类的数组, 也就是说, 在 BingheAnnotations 注解类中, 包含有多个 BingheAnnotation 注解。
故在 BingheAnnotation 注解类上指定@Repeatable(BingheAnnotations.class)来说明可以在类、 字段、 方法、 参数、 构造方法上重复使用 BingheAnnotation 注解。
下一篇:C语言文件操作