【Java】全面理解Java8特性
目录
一、Java8中Interface接口
二、lambda表达式
语法格式
三、函数式接口Functional Interface
1. 作用
2. 内置函数式接(Built-in Functional Interfaces)
Predicate接口
Function
Comparator
四、Stream
1. Filter 过滤
2. Sorted 排序
3. Map 映射
4. Match 匹配
5. Collect收集
6. Statistics 统计
7. 函数式接口总结
五、日期时间
1. 格式化
2. 字符串转日期格式
3. 日期计算
4. 获取指定日期
一、Java8中Interface接口
Java8 中,接口中除了抽象方法外,还可以定义default 默认方法和 static 静态方法。
default 修饰的默认方法,属于实例方法,可以被实现类调用或重写。
调用:实现类必须 implements 接口,才能调用该接口的 default 默认方法。
重写:实现类 implements不同接口时,接口中存在相同签名的方法(名称、参数、类型完全一致),则实现类必须重写该方法明确方法定义;
static 修饰的静态方法,属于类的静态方法。但它不能被子类继承,只能用 interface 接口名称调用。
二、lambda表达式
Lambda表达式本质是一个匿名函数,用于把函数作为参数,传入方法中,实现函数式编程风格。
使用 Lambda表达式可以使代码变的更加简洁紧凑。
语法格式
(parameters)->expression或(parameters)->{statements;}
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("s","sjv","safa","safth"));
// Comparator接口的匿名实现方式(传统)
// list.sort(new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length()-o2.length();
// }
// });
// lambda表达式的语法规则:(参数) -> {方法实现逻辑}
// 使用lambda表达式,@FunctionalInterface接口,只有一个抽象方法(无方法体)
// 通过lambda表达式实现Comparator接口
list.sort((s1, s2) -> {
return s1.length()-s2.length();
});
// 通过lambda表达式实现Consumer接口
list.forEach((str)->{
System.out.println(str.toUpperCase());
});
System.out.println(list);
}
三、函数式接口Functional Interface
只有一个抽象方法的接口(可以定义多个非抽象方法)。可以使用 @FunctionalInterface 接
口定义,强化语义规范。
函数式接口,也被称为SAM接口(Single Abstract Method Interfaces)。
1. 作用
基于函数式接口,可以使用Lambda 表达式进行实现,实现函数式编程。
2. 内置函数式接(Built-in Functional Interfaces)
在 Java 8中专门有一个包放函数式接口 java.util.function,该包下的所有接口都有 @FunctionalInterface 注解,提供函数式编程方式。
Predicate接口
Predicate 接口是只有一个参数的返回布尔类型值的 断言型 接口。该接口包含多种默认方法来将Predicate 组合成其他复杂的逻辑(比如:与 and,或or,非negate)。
public static void main(String[] args) {
// Predicate predicate = new Predicate() {
// @Override
// public boolean test(Object o) {
// return false;
// }
// };
List<String> arrayList = new ArrayList<>(Arrays.asList("Basic","QBasic","c","c++","PowerBuilder","go"));
// 查找名称小于3个字符的元素
Predicate<String> predicate1 = (lang)-> {
if (lang.length()<=3) {
return true;
}
return false;
};
// 查询前缀以“b"或”c“开头的元素
Predicate<String> predicate2 = (lang)->{
if (lang.toLowerCase().startsWith("b")||lang.toLowerCase().startsWith("c")){
return true;
}
return false;
};
// 条件组合:形成”与“ 或 ”或“逻辑关系
Predicate<String> predicate3 = predicate1.and(predicate2);
Predicate<String> predicate4 = predicate1.or(predicate2);
// ”非“逻辑 (取反)
Predicate<String> predicate5 = predicate1.negate(); // >3
arrayList.forEach((lang)->{
if (predicate1.test(lang)){
System.out.println(lang);
}
});
}
Function
Function 接口接受一个参数并生成结果。默认方法可用于将多个函数链接在一起(compose,andThen)。
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("Basic", "QBasic", "c", "c++", "PowerBuilder", "go"));
// Function<参数类型,返回值类型>
// 转换成大写字母形式
Function<String, String> fun1 = (lang) -> {
lang = lang.toUpperCase();
return lang;
};
// 加上【】
Function<String, String> fun2 = (lang) -> {
lang = String.format("【%s】", lang);
return lang;
};
// 组合
Function<String, String> fun3 = fun1.andThen(fun2); //先fun1,后fun2
Function<String,String> fun4 = fun1.compose(fun2);
list.forEach((lang) -> {
String ret = fun3.apply(lang);
System.out.println(ret);
});
}
Comparator
比较器接口,用于比较指定元素值的大小。 Java 8版本中,添加了多个新的 default 方法,用于比较器合并、反转等操作。
public static void main(String[] args) {
List<String> list = Arrays.asList("Basic", "QBasic", "c", "c++", "PowerBuilder", "to","go");
Comparator<String> comparator1 = (lang1, lang2) -> {
int ret = lang1.compareTo(lang2);
if (ret ==0){
return lang1.length()-lang2.length();
}
return ret;
};
Comparator<String> comparable2 = comparator1.reversed(); // 反转
list.sort(comparable2);
list.forEach((lang)->{
System.out.println(lang);
});
}
四、Stream
java.util.Stream表示能应用在一组元素上一次执行的操作序列。
Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回 stream 本身,可以连续完成多个操作。
1. Filter 过滤
过滤通过一个 predicate 接口来过滤并只保留符合条件的元素,该操作属于中间操作。所以过滤后的结果,可以继续进行其它 stream操作(例如forEach,forEach需要一个函数来对过滤后的元素依次执行。 forEach 是一个最终操作)。
public static void main(String[] args) {
Stream<String> stream = Stream.of("Basic", "QBasic", "c", "c++", "PowerBuilder", "go", "BuilderPower");
// 过滤所有小写字母内容的元素,并排序
stream.filter((lang) -> {
for (int i = 0; i < lang.length(); i++) {
char c = lang.charAt(i);
if (c > 'A' && c < 'Z') {
return true;
}
}
return false;
}).sorted((c1, c2) -> {
if (c1.length() == c2.length()) {
return c1.compareTo(c2);
} else {
return c1.length() - c2.length();
}
}).forEach((lang) -> {
System.out.println(lang);
});
}
2. Sorted 排序
排序是一个 中间操作,返回的是排序好后的Stream 。(不影响原数据)
public static void main(String[] args) {
Stream<String> stream = Stream.of("Basic", "QBasic", "c", "c++", "PowerBuilder", "go", "BuilderPower");
// 过滤所有小写字母内容的元素,并排序
stream.filter((lang) -> {
for (int i = 0; i < lang.length(); i++) {
char c = lang.charAt(i);
if (c > 'A' && c < 'Z') {
return true;
}
}
return false;
}).sorted((c1, c2) -> {
if (c1.length() == c2.length()) {
return c1.compareTo(c2);
} else {
return c1.length() - c2.length();
}
}).forEach((lang) -> {
System.out.println(lang);
});
}
3. Map 映射
映射是一个中间操作,会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(10, 20, 30, 40, 50, 60);
// 映射:对每一个元素进行一次处理
stream.map((n) -> {
return n + 1;
}).forEach((n) -> {
System.out.println(n);
});
List<String> stringList = Arrays.asList("afa", "asf", "afsa");
//转换字符串为大写,降序后输出
stringList.stream().map((item) -> {
return item.toUpperCase();
}).sorted((s1, s2) -> {
return s1.compareTo(s2);
}).forEach((item) -> {
System.out.println(item);
});
}
4. Match 匹配
Stream 提供了多种匹配操作,允许检测指定的 Predicate 是否匹配整个 Stream 。所有的匹配操作都是 最终操作 ,并返回一个boolean 类型的值。
public static void main(String[] args) {
List<String> stringList = Arrays.asList("gds","asfa","das");
// allMatch:所有元素是否匹配
boolean bool1 = stringList.stream().allMatch((item) -> {
return item.length() == 3;
});
System.out.println(bool1);
// anyMatch:任意元素是否匹配
boolean bool2 = stringList.stream().anyMatch((item) -> {
return item.length() == 3;
});
System.out.println(bool2);
}
5. Collect收集
收集是一个 最终操作,返回 stream 中元素集合,返回值类型是集合(List、set、Map)或 字符串。
public static void main(String[] args) {
List<String> stringList = Arrays.asList("1556","5612","8489");
//将Stream流中的每个元素进行mapping映射操作后,然后收集至一个List集合
//Collectors.mapping
List<Integer> numberInt = stringList.stream().collect(Collectors.mapping(s -> Integer.parseInt(s) * 10, Collectors.toList()));
System.out.println(numberInt);
// 分组操作
// Collectors.groupingBy
// 按照元素的字符长度进行分组
Stream<String> stream = Stream.of("Basic", "QBasic", "c", "c++", "PowerBuilder", "go","BuilderPower");
Map<Integer, List<String>> resultMapGroup1 = stream.collect(Collectors.groupingBy(s -> s.length()));
resultMapGroup1.forEach((k,v)->{
System.out.println(k+":"+v);
});
// stream.collect(Collectors.groupingBy((s->s.length()))).forEach((k,v)->{
// System.out.println(k+":"+v);
// });
// 按照元素的首字母进行分组
stream = Stream.of("Basic", "QBasic", "c", "c++", "PowerBuilder", "go","BuilderPower");
stream.collect(Collectors.groupingBy(s->s.charAt(0))).forEach((k,v)->{
System.out.println(k+":"+v);
});
}
public static void main(String[] args) {
Stream<String> stream = Stream.of("Basic", "QBasic", "c", "c++", "PowerBuilder", "go","BuilderPower");
// 分区操作(只有true和false)
stream.collect(Collectors.partitioningBy(s->s.length()>3)).forEach((k,v)->{
System.out.println(k+":"+v);
});
}
6. Statistics 统计
统计是一个最终操作,返回 Stream中元素的各类统计信息,返回值类型是 XXXConsumer。
public static void main(String[] args) {
//统计操作
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
// Stream ===> IntStream
IntStream intStream = list.stream().mapToInt(s -> s*10);
IntSummaryStatistics statisticsResult = intStream.summaryStatistics();
// 获取统计结果
System.out.println("最大值:"+statisticsResult.getMax());
System.out.println("最小值:"+statisticsResult.getMin());
System.out.println("平均值:"+statisticsResult.getAverage());
System.out.println("累加和:"+statisticsResult.getSum());
}
7. 函数式接口总结
a. Predicate、Function、Consumer、Comparator
b. 通过链式编程,使得它可以方便地对数据进行链式处理。
c. 方法参数都是函数式接口类型
d. 一个stream 只能操作一次,操作完就关闭了,继续使用这个 stream会报错。Stream不保存数据,不改变数据源。
五、日期时间
- LocalDateTime //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
- LocalDate //日期 format: yyyy-MM-dd
- LocalTime //时间 format: HH:mm:ss
1. 格式化
public static void main(String[] args) {
// 日期格式化
// 默认格式: yyyy-MM-dd
LocalDate date = LocalDate.now();
System.out.println(String.format("Date Format:%s",date));
// 默认格式:HH:mm:ss
LocalTime time =LocalTime.now().withNano(0);
System.out.println(String.format("Time Format:%s",time));
LocalDateTime dateTime = LocalDateTime.now();
// 自定义格式:yyyy-MM-dd HH:mm:ss
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String dateTimeStr = dateTime.format(dateTimeFormatter);
System.out.println(String.format("DateTime Format:%s",dateTimeStr));
}
2. 字符串转日期格式
public static void main(String[] args) {
// 字符串格式的日期内容,转换为LocalDate等之类的日期对象
// 按照指定的日期的时间域值进行转换
LocalDate date1 = LocalDate.of(2024,9,11);
System.out.println(date1);
// 按照默认格式(yyyy-MM-dd)的字符串,使用parse()方法进行转换
LocalDate date2 = LocalDate.parse("2024-09-11");
System.out.println(date2);
// 按照默认格式(yyyy-MM-ddTHH:mm:ss)
LocalDateTime dateTime1 = LocalDateTime.of(2024,9,11,17,50,30);
LocalDateTime dateTime2 =LocalDateTime.parse("2024-09-11T17:50:30");
System.out.println(dateTime1);
System.out.println(dateTime2);
// 按照默认格式(HH:mm:ss)
LocalTime time1 = LocalTime.of(17,50,30);
LocalTime time2 =LocalTime.parse("17:50:30");
System.out.println(time1);
System.out.println(time2);
}
3. 日期计算
public static void main(String[] args) {
// 字符串格式的日期时间
String strDatetime ="2024年12月15日";
// 将字符串格式的日期,转换为LocalDate类型的日期对象
LocalDate date = LocalDate.parse(strDatetime, DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
System.out.println(date);
// 20天前
LocalDate date1 = date.plusDays(-20);
System.out.println(date1);
//20天后
LocalDate date2 = date.plusDays(20);
System.out.println(date2);
}
public static void main(String[] args) {
//计算两个日期间隔多少天,计算间隔多少年,多少月
LocalDate date1 = LocalDate.parse("2024-07-12");
LocalDate date2 = LocalDate.parse("2024-09-11");
Period period =Period.between(date1,date2);
System.out.println("date1到date2相间隔"+period.getYears()+"年"+period.getMonths()+"月"+period.getDays()+"天");
long day = date2.toEpochDay() - date1.toEpochDay();
System.out.println(date2+"和"+date1+"相差"+day+"天");
}
4. 获取指定日期
public static void main(String[] args) {
LocalDate today = LocalDate.now();
//获取当前月第一天
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("当前月第一天:"+firstDayOfThisMonth);
//获取本月最后一天
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("当前月最后一天:"+lastDayOfThisMonth);
// 获取下一天
LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
System.out.println("下一天(次月第一天):"+nextDay);
LocalDate nextDay2 = today.plusDays(1);
System.out.println("当前下一天:"+nextDay2);
//获取当年最后一天
LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("当年最后一天:"+lastday);
//获取当年最后一个周
LocalDate lastMondayOfThisYear = lastday.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
System.out.println("当年最后一个周一:"+lastMondayOfThisYear);
}