Lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数。主要用来优化匿名内部类的结构
匿名内部类写法:
A a = new A() {
@Override
public void test() {
}
};
Lambda表达式:
不关注接口的数据类型,不关注接口的结构,只关注方法本身。
//lambda标签写法
(参数)->{
}
标准格式
Lambda表达式的语法为:
([参数列表]) ->{
代码体;
}
无参的代码演示
public class Demo02Lambda {
public static void main(String[] args) {
Drinkable drinkable = ()->{
System.out.println("大口的喝...");
};
drinkable.drink();
}
}
interface Drinkable{
void drink();
}
有参的代码演示
public class Demo03Lambda {
public static void main(String[] args) {
Eatable eatable = (food)->{
System.out.println("大口的吃"+food);
return "吃饱了";
};
String result = eatable.eat("牛肉");
System.out.println(result);
}
}
interface Eatable{
String eat(String food);
}
省略模式
Lambda表达式省略写法
- 如果方法体只有一句话,则可以省略方法的花括号
- 如果方法形参只有一个参数,则可以省略圆括号
- 如果方法体只有一个return语句,则return也可以省略
如常规表达式为
(i)->{
return i+i;
}
省略模式后为
i->i+i
使用限制
lambda表达式使用有几个条件需要特别注意:
- lambda表达式是针对接口才能使用
- 接口中必须有且仅有一个抽象方法,能被@FunctionalInterface注解修饰的方法
函数式接口
lambda表达式是针对接口的,有且仅有一个抽象方法,这种接口称为函数接口。lambda表达式使用时不关心接口名、抽象方法名,只关心抽象方法的参数列表和返回类型。因此JDK8提供了大量的常用的函数式接口。
这些函数接口都在java.util.function包下,常用接口有Supplier接口、Consumer接口、Function接口、Predicate接口。
Supplier接口
supplier表示供应商,供给的意思,这类接口的特点是:无参有返回值。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
代码演示
public class Demo04FunctionReference {
public static void main(String[] args) {
Supplier<Integer> supplier = ()->{
return new Random().nextInt();
};
Integer result = supplier.get();
System.out.println("随机产生一个整数: "+result);
}
}
Consumer接口
Consumer表示消费的意思,这类接口的特点是:有参无返回值。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
代码演示
// 小写字符串转成大写字符串
Consumer<String> consumer = (s)->{
System.out.println(s.toUpperCase());
};
consumer.accept("hello blb!");
Function接口
Function表示方法函数的意思 ,这类接口的特点是:有参有返回值
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
代码演示
// 将字符串转成数字
Function<String,Integer> function = (s)->{
return Integer.parseInt(s);
};
System.out.println(function.apply("123"));
Predicate接口
Predicate表示判定的意思,这类接口的特点是:有参有boolean类型返回值
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
代码演示
// 判断一个整数是否是偶数
Predicate<Integer> predicate = (i)->{
return i%2==0;
};
System.out.println("是否是整数:"+predicate.test(18));
方法引用
方法引用是一种更简洁的lamdba表达式,如果一个lamdba表达式的方法体只是在调用一个方法时,就可以使用方法引用来简化化。
- 对象引用实例方法
- 类名引用静态方法
- 类名引用实例方法
- 引用构造方法
对象引用实例方法
对象名::方法名
代码演示
// 定义函数接口,引用System.out对象的println方法
public void testMethod1(){
Consumer<Object> c = System.out::println;
c.accept("bailiban");
c.accept(123);
}
类名引用静态方法
类名::静态方法名
代码演示
// 定义函数接口,引用String类的valueOf静态方法
public void testMethod2(){
Function<Integer, String> function = String::valueOf ;
System.out.println(function.apply(123));
}
类名引用实例方法
类名::成员方法名
代码演示
// 定义函数接口,引用String类的length方法
public static void testMethod3(){
Function<String,Integer> function = String::length;
System.out.println(function.apply("bailiban"));
}
tip:String类的length方法是没有参数的,但是Function函数是有一个参数的,这个参数表示是String类的任意对象。
引用构造方法
类名::new
代码演示
// 定义函数接口,引用Student类的构造器方法
public class Demo08FunctionReference {
@Test
public void testMethod4(){
Function<String,Student> function = Student::new;
Student blb = function.apply("blb");
System.out.println(blb);
}
}
class Student {
private String name ;
public Student(String name){
this.name = name ;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
Stream流
对一个集合体做过滤、排序、统计等操作时,可以使用Stream流来简化操作。
代码演示
public class Demo09Stream {
public static void main(String[] args) {
// 创建集合并添加元素
List<String> list = new ArrayList<>();
Collections.addAll(list,"刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
// 通过流过滤姓名以“黄”开头的元素,然后遍历
list.stream().filter((s)->{
return s.startsWith("黄");
}).forEach((s)->{
System.out.println(s);
});
}
}
Stream对象的获取
- 所有Collection对象都有stream方法
@Test
public void testStream01(){
// List获取Stream
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// Set获取Stream
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
// Map获取Stream
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
- Stream中的静态of方法
Stream<String> stringStream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
Stream类的API
Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
-
终结方法:返回值类型不再是 Stream 类型的方法,因此不再支持链式调用。主要有包括 count 和 forEach 方法。
-
非终结方法:返回值类型仍然是 Stream 类型的方法,因此支持链式调用。
方法名 | 方法作用 | 返回值类型 | 方法种类 |
count | 统计个数 | long | 终结 |
foreach | 遍历 | void | 终结 |
filter | 过滤 | Stream | 非终结 |
limit | 限定前几个 | Stream | 非终结 |
skip | 跳过前几个 | Stream | 非终结 |
map | 映射 | Stream | 非终结 |
sorted | 排序 | Stream | 非终结 |
distinct | 去重 | Stream | 非终结 |
使用规则
- Stream只能操作一次
- Stream方法返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
forEach
void forEach(Consumer action)该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
代码演示
@Test
public void testStream02(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
// stream.forEach(s->{
// System.out.println(s);
// });
stream.forEach(System.out::println);
}
count
long count()
,Stream流提供count方法来统计其中的元素个数。
代码演示
@Test
public void testStream03(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
System.out.println(stream.count());
}
filter
Stream<T> filter(Predicate<? super T> predicate)
可以通过 filter 方法将一个流转换成另一个子集流。该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素。
代码演示
@Test
public void testStream04(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
stream.filter(s-> s.startsWith("黄")).forEach(System.out::println);
}
limit
Stream<T> limit(long maxSize)
limit
方法可以对流进行截取,只取用前n个。
代码演示
@Test
public void testStream05(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
stream.limit(3).forEach(System.out::println);
}
skip
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个。
代码演示
@Test
public void testStream06(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","黄忠","黄月英");
stream.skip(3).forEach(System.out::println);
}
map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。
代码演示
@Test
public void testStream07(){
Stream<String> stream = Stream.of("1","2","3","4","5","6");
stream.map(s->Integer.parseInt(s)).forEach(System.out::println);
}
sorted
可以需要将数据进行排序,可以使用Stream的sorted方法排序。
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
代码演示
@Test
public void testStream08(){
Stream<Integer> stream = Stream.of(9,4,1,2,8);
// stream.sorted().forEach(System.out::println);
stream.sorted((o1,o2)->o2-o1).forEach(System.out::println);
}
distinct
如果数据需要去掉重复元素可以使用distinct方法
Stream<T> distinct();
代码演示
@Test
public void testStream09(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","张飞","赵云");
stream.distinct().forEach(System.out::println);
}
Collector
对流操作完成以后,如果需要将数据保存到数组或者集合中,可以收集流中的数据。
<R, A> R collect(Collector<? super T, A, R> collector);
代码演示
@Test
public void testStream10(){
Stream<String> stream = Stream.of("刘备","张飞","赵云","诸葛亮","张飞","赵云");
// 用List集合来收集
// List<String> list = stream.collect(Collectors.toList());
// 用Set集合来收集
// Set<String> set = stream.collect(Collectors.toSet());
// System.out.println(set);
// 用ArrayList集合来收集
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
System.out.println(arrayList);
}
日期时间
JDK8新增了3个日期时间类,分别是:LocalDate、LocalTime、LocalDateTime
LocalDate(日期)
日期包含年月日信息,日期的操作的常用API如下
代码演示
@Test
public void dateTime01(){
// 获取当前对应的日期
LocalDate now = LocalDate.now();
System.out.println(now);
LocalDate date = LocalDate.of(2020, 12, 13);
System.out.println(date);
// 获取年份
System.out.println(now.getYear());
// 获取月份,英文
System.out.println(now.getMonth());
// 获取月份值
System.out.println(now.getMonthValue());
// 获取当月中的第几天,也就是几号
System.out.println(now.getDayOfMonth());
// 获取当周中的第几天,也就是星期
System.out.println(now.getDayOfWeek());
// 获取年中的第几天
System.out.println(now.getDayOfYear());
// 修改年份为2019
System.out.println(now.withYear(2019));
// 修改月份为2
System.out.println(now.withMonth(2));
// 修改日期为3号
System.out.println(now.withDayOfMonth(3));
}
LocalTime(时间)
时间包含时分秒信息,时间操作常用的API如下
代码演示
@Test
public void dateTime02(){
LocalTime time = LocalTime.of(11,12,13);
System.out.println(time);
// 获取当前时间
LocalTime now = LocalTime.now();
// 获取小时
System.out.println(now.getHour());
// 获取分
System.out.println(now.getMinute());
// 获取秒
System.out.println(now.getSecond());
// 获取纳秒
System.out.println(now.getNano());
// 修改小时为12
System.out.println(now.withHour(12));
// 修改分钟为22
System.out.println(now.withMinute(22));
// 修改秒为33
System.out.println(now.withSecond(33));
}
LocalDateTime(日期时间)
日期时间包含年月日时分秒信息,常用API如下
代码演示
@Test
public void dateTime03(){
LocalDateTime time = LocalDateTime.of(2020,12,11,10,11,12);
System.out.println(time);
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
// 获取年份
System.out.println(now.getYear());
// 获取月份,英文
System.out.println(now.getMonth());
// 获取月份值
System.out.println(now.getMonthValue());
// 获取当月中的第几天,也就是几号
System.out.println(now.getDayOfMonth());
// 获取当周中的第几天,也就是星期
System.out.println(now.getDayOfWeek());
// 获取年中的第几天
System.out.println(now.getDayOfYear());
// 获取小时
System.out.println(now.getHour());
// 获取分
System.out.println(now.getMinute());
// 获取秒
System.out.println(now.getSecond());
// 获取纳秒
System.out.println(now.getNano());
// 修改年份为2019
System.out.println(now.withYear(2019));
// 修改月份为2
System.out.println(now.withMonth(2));
// 修改日期为3号
System.out.println(now.withDayOfMonth(3));
// 修改小时为12
System.out.println(now.withHour(12));
// 修改分钟为22
System.out.println(now.withMinute(22));
// 修改秒为33
System.out.println(now.withSecond(33));
}
tip:
时间跟日期的修改都是返回一个新的日期时间对象,原来的日期时间对象不改变。
格式化
可以通过java.time.format.DateTimeFormatter
类可以进行日期时间的解析与格式化。
代码演示
@Test
public void dateTime04(){
// 日期转成格式化字符串
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String formatNow = now.format(dtf);
System.out.println(formatNow);
// 格式化字符串转成日期
LocalDateTime parse = LocalDateTime.parse("2021年01月28日 04时14分43秒", dtf);
System.out.println(parse);
}
日期时间格式工具类
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
//System.out.println(formatter.format(localDateTime));
日期时间差
JDK8中通过Duration
用于计算两个“时间”间隔。通过Period
用于计算两个“日期”间隔的类。
Duration时间差
Duration代码演示
@Test
public void dateTime05(){
LocalTime now = LocalTime.now();
System.out.println(now);
LocalTime time = LocalTime.of(17,48,12);
System.out.println(time);
Duration duration = Duration.between(now,time);
// 时间差转成小时
System.out.println(duration.toHours());
// 时间差转成分钟
System.out.println(duration.toMinutes());
// 时间差转成秒
System.out.println(duration.getSeconds());
// 时间差转成纳秒
System.out.println(duration.toNanos());
}
Period日期差
Period代码演示
@Test
public void dateTime06(){
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.of(2030,4,30);
Period period = Period.between(now,date);
// 获取年份差
System.out.println(period.getYears());
// 获取月份差
System.out.println(period.getMonths());
// 获取日期差
System.out.println(period.getDays());
}
时间校正器
有时候我们需要将日期或者时间进行校正,比如设置为“下个月的第1天”等,可以通过时间校正器进行。
-
TemporalAdjuster:时间校正器。
-
TemporalAdjusters:通过静态方法提供大量的常用的TemporalAdjuster实现。
@Test
public void dateTime07(){
LocalDateTime now = LocalDateTime.now();
// 设置下月1号的校正器
TemporalAdjuster firstDayOfNextMonth = temporal -> {
LocalDateTime time = (LocalDateTime) temporal;
return time.plusMonths(1).withDayOfMonth(1);
};
// 通过校正器调节now的值
System.out.println(now.with(firstDayOfNextMonth));
// TemporalAdjusters获取下一年的第1天的校正器
System.out.println(now.with(TemporalAdjusters.firstDayOfNextYear()));
}
//6、获取这个月的最后1天(超纲)
// LocalDate now = LocalDate.of(2022, 2, 1);
// LocalDate localDate = now.with(TemporalAdjusters.lastDayOfMonth());
// System.out.println(localDate);
//7、获取下个月的最后1天(超纲)
LocalDate localDate = LocalDate.now().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
System.out.println(localDate);