一、Java 8中的接口
依赖倒置原则规定了面向接口编程,这是一种编程规范,可以更好的进行需求的定义和业务的扩展。Java 8中接口除了抽象方法外,还可以定义 default 默认方法和 static 静态方法。
default 修饰的默认方法,属于实例方法,可以被实现类调用或重写。
- 调用:实现类必须 implements 接口,才能调用该接口的 default 默认方法。
- 重写:实现类 implements 不同接口时,接口中存在相同签名的方法(名称、参数、类型完全一致),则实现类必须重写该方法,明确方法定义;
static 修饰的静态方法,属于类的静态方法。但它不能被子类继承,只能用 interf ace 接口名称调用。
二、Lambda表达式
Lambda 表达式本质是一个匿名函数,用于把函数作为参数,传入方法中,实现函数式编程风格。使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法格式:
(parameters)-> expression 或 (parameters)->{ statements;}
三、函数式接口 Functional Interface
只有一个抽象方法的接口(可以定义多个非抽象方法)。可以使用 @FunctionalInterface 接口定义,强化语义规范。函数式接口,也被称为 SAM 接口( Single Abstract Method Interfaces )
基于函数式接口,可以使用 Lambda 表达式进行实现,实现函数式编程。
内置函数式接口(Built-in Functional Interfaces)
在 Java 8 中专门有一个包放函数式接口 java.util.function ,该包下的所有接口都有@FunctionalInterface 注解,提供函数式编程方式。
Predicate接口
Predicate 接口是只有一个参数的返回布尔类型值的断言型接口。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与 and ,或 or ,非 negate );
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
// 该方法是接受一个传入类型,返回一个布尔值.此方法应用于判断.
boolean test(T t);
// and方法与关系型运算符"&&"相似,两边都成立才返回true
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 与关系运算符"!"相似,对判断进行取反
default Predicate<T> negate() {
return (t) -> !test(t);
}
//or方法与关系型运算符"||"相似,两边只要有一个成立就返回true
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 该方法接收一个Object对象,返回一个Predicate类型.此方法用于判断第一个test的
方法与第二个test方法相同(equal).
eg:
public class Test4 {
public static void main(String[] args) {
List<String> list = Arrays.asList("dawdwa", "dawww", "dw%wa", "adww1", "111%%", "@@1weaw");
//判断1
Predicate<String> predicate1 = (lang)->{
return lang.length() != 5;
};
//判断2
Predicate<String> predicate2 = (lang)->{
for (int i = 0; i < lang.length(); i++) {
if (!Character.isLetter(lang.charAt(i))){
return false;
}
}
return true;
};
//组合判断
Predicate<String> predicate3 = predicate1.and(predicate2);//与
Predicate<String> predicate4 = predicate1.or(predicate2);//或
Predicate<String> predicate5 = predicate1.negate();//非
list.forEach(s -> {
System.out.println(s + "是否符合条件" + predicate3.test(s));
});
}
}
Function
Function 接口接受一个参数并生成结果。默认方法可用于将多个函数链接在一起( compose , andThen )。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
// 将Function对象应用到输入的参数上,然后返回计算结果。
R apply(T t);
// 将两个Function整合,并返回一个能够执行两个Function对象功能的Function对象
// 作用等同于:apply(before.apply(v))
default <V> Function<V, R> compose(Function<? super V, ? extends T> be
fore) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 将两个Function整合,并返回一个能够执行两个Function对象功能的Function对象
// 作用等同于:before.apply(apply())
default <V> Function<T, V> andThen(Function<? super R, ? extends V> af
ter) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
eg:
public class Test5 {
public static void main(String[] args) {
List<String> list = Arrays.asList("dawdwa", "dawww", "dw%wa", "adww1", "111%%", "@@1weaw");
//功能1
Function<String,String> fun1 = (lang)->{
System.out.println("fun1");
return lang.toLowerCase();
};
//功能2
Function<String,String> fun2 = (lang)->{
System.out.println("fun2");
return String.format("<%s>",lang);
};
Function<String,String> fun3 = fun1.andThen(fun2);//先fun1后fun2
Function<String,String> fun4 = fun1.compose(fun2);//先fun2后fun1
list.stream().map(fun3).forEach((lang)->{
System.out.println(lang);
});
System.out.println("-------------------");
list.stream()
.map(fun4)//map映射,中间操作,根据传入Function转换为指定格式对象
.forEach((lang)->{
System.out.println(lang);
});
}
}
Comparator
比较器接口,用于比较指定元素值的大小。 Java 8版本中,添加了多个新的 default 方 法,用于比较器合并、反转等操作。
public class Test6 {
public static void main(String[] args) {
List<String> list = Arrays.asList("daw", "daw", "1wd");
//比较1,比ASCII码
Comparator<String> com1 = (x,y)->{
return x.compareTo(y);
};
//比较2,比长度
Comparator<String> com2 = (x,y)->{
return y.length() - x.length();
};
Comparator<String> com3 = com1.thenComparing(com2);//组合比较,先比1后比2
Comparator<String> com4 = com3.reversed();//比较规则取反
list.sort(com3);
System.out.println(list);
list.sort(com4);
System.out.println(list);
}
}
四、Stream流
java.util.Stream 表示能应用在一组元素上一次执行的操作序列。
Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回 Stream 本身,可以连续完成多个操作。
- stream流不存储数据
- stream不改变数据源
- stream不可重复使用
常用方法
/**
* 返回一个串行流
*/
default Stream<E> stream()
/**
* 返回一个并行流
*/
default Stream<E> parallelStream()
/**
* 返回T的流
*/
public static<T> Stream<T> of(T t)
/**
* 返回其元素是指定值的顺序流。
*/
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
/**
* 过滤,返回由与给定predicate匹配的该流的元素组成的流
*/
Stream<T> filter(Predicate<? super T> predicate);
/**
* 此流的所有元素是否与提供的predicate匹配。
*/
boolean allMatch(Predicate<? super T> predicate)
/**
* 此流任意元素是否有与提供的predicate匹配。
*/
boolean anyMatch(Predicate<? super T> predicate);
Filter 过滤
过滤通过一个 predicate 接口来过滤并只保留符合条件的元素,该操作属于中间操作。所以过滤后的结果,可以继续进行其它 Stream 操作(例如 forEach ,forEach需要一个函数来对过滤后的元素依次执行。 forEach 是一个最终操作)。
Stream<T> filter(Predicate<? super T> predicate);//传入一个判断接口
public class Test7 {
public static void main(String[] args) {
List<String> list = Arrays.asList("大为", "对对对", "问答");
list.stream().filter((s)->{
return s.length() == 2;
}).forEach((s)->{
System.out.println(s);
});
}
}
Sorted 排序
排序是一个中间操作,返回的是排序好后的 Stream 。(不影响原数据)
Stream<T> sorted(Comparator<? super T> comparator);//传入一个比较器接口排序
public class Test8 {
public static void main(String[] args) {
List<String> list = Arrays.asList("法网法网发", "染哇4问问发涩发", "达娃大点否", "富热是法国", "达瓦","法网法网发","法网法网发");
//stream流先重,再按长度排序,长度相同按ASCII码排序,再取出第一位,即最长的
String s = list.stream().distinct().sorted((x, y) -> {
if (x.length() == y.length()) {
return x.compareTo(y);
}
return y.length() - x.length();
}).findFirst().get();
System.out.println(s);
}
}
Map 映射
映射是一个中间操作,会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);//传入功能接口
List<String> strings = Arrays.asList("abc", "def", "gkh", "abc");
// 转换字符串为大写,降序后,输出
strings.stream()
.map((item)->{return item.toUpperCase();})
.sorted((s1,s2)->{return s2.compareTo(s1);})
.forEach(System.out::println);
Match 匹配
Stream 提供了多种匹配操作,允许检测指定的 Predicate 是否匹配整个 Stream 。所有的匹配操作都是最终操作 ,并返回一个 boolean 类型的值。
//传入判断接口
boolean anyMatch(Predicate<? super T> predicate);//局部匹配
boolean allMatch(Predicate<? super T> predicate);//全局匹配
List<String> strings = Arrays.asList("abc", "deft", "gkh", "abc");
// 整体匹配
boolean isMatch1 = strings.stream().allMatch((s) -> s.length() == 3);
System.out.println(isMatch1); // false
// 局部匹配
boolean isMatch2 = strings.stream().anyMatch((s) -> s.length() == 3);
System.out.println(isMatch2); // true
Count 计数
计数是一个最终操作,返回 Stream 中元素的个数,返回值类型是 long。
long count();
List<String> strings = Arrays.asList("abc", "deft", "gkh", "abc");
// 统计“a”开头的元素个数
long count = strings.stream().filter((s)->s.startsWith("a")).count();
System.out.println(count);
Collect 收集
收集是一个最终操作,返回 Stream 中元素集合,返回值类型是集合(List、Set、Map) 或 字符串。
<R, A> R collect(Collector<? super T, A, R> collector);//传入要收集的集合类型
List:
//去重,取前4个元素,以list类型收集
List<String> musicList = list.stream().distinct().limit(4).collect(Collectors.toList());
System.out.println(musicList);
Set:
//自定义Collectors工具类没有的集合
LinkedHashSet<String> musicSet = list.stream().collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(musicSet);
Map:
Map<String, Integer> musicmap2 = list.stream().distinct().collect(Collectors.toMap(music -> music,//key
music -> music.length()//value
));
System.out.println(musicmap2);
对集合进行map映射:
public class Test9 {
public static void main(String[] args) {
List<String> list = Arrays.asList("1", "2", "33", "12", "2");
List<Integer> list1 = list.stream().collect(Collectors.mapping(s->Integer.parseInt(s)*10,
Collectors.toList()));
System.out.println(list1);
}
}
集合分组:
public class Test10 {
public static void main(String[] args) {
List<String> list = Arrays.asList("张伟", "王伟", "张伟", "张家界", "周杰伦", "小行星", "冯绍峰", "李准基", "李建");
Map<Character, List<String>> collect = list.stream().collect(Collectors.groupingBy(name -> name.charAt(0)));//按姓分组
System.out.println(collect);
Map<Boolean, List<String>> collect1 = list.stream().collect(Collectors.partitioningBy(name -> name.length() == 2));//按长度判断规则分组
System.out.println(collect1);
}
}
Statistics 统计
统计是一个最终操作,返回 Stream 中元素的各类统计信息,返回值类型是 XXXConsumer。
IntSummaryStatistics summaryStatistics();
public class Test11 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3213, 43, 123, 21);
IntStream intStream = list.stream().mapToInt(x -> x);//将stream转换成IntStream
IntSummaryStatistics statistics = intStream.summaryStatistics();
System.out.println("最大值:" + statistics.getMax());
System.out.println("最小值:" + statistics.getMin());
System.out.println("平均值:" + statistics.getAverage());
System.out.println("和:" + statistics.getSum());
}
}
Parallel Streams 并行流
Stream 有串行和并行两种,串行 Stream 上的操作是在一个线程中依次完成,而并行 S tream 则是在多个线程上同时执行。
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
public class Test12 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(1000000);
for (int i = 0; i < 1000000; i++) {
list.add(UUID.randomUUID().toString());
}
long begin = System.currentTimeMillis();
long count = list.parallelStream().sorted().count();
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
}
函数式接口总结
- Predicate 、 Function 、 Consumer 、 Comparator
- 通过链式编程,使得它可以方便地对数据进行链式处理。
- 方法参数都是函数式接口类型。
- 一个 Stream 只能操作一次,操作完就关闭了,继续使用这个 Stream 会报错。
- Stream 不保存数据,不改变数据源。
五、日期时间
- Java 8在 java.time 包下包含一个全新的日期和时间API。
- LocalDateTime //日期+时间
- format: yyyy-MM-ddTHH:mm:ss.SSS
- LocalDate //日期 format: yyyy-MM-dd LocalTime //时间 format: HH:mm:ss
格式化
//Format yyyy-MM-dd
LocalDate date = LocalDate.now();
System.out.println(String.format("Date format : %s", date));
//Format HH:mm:ss
LocalTime time = LocalTime.now().withNano(0);
System.out.println(String.format("Time format : %s", time));
//Format yyyy-MM-dd HH:mm:ss
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM
-dd HH:mm:ss");
String dateTimeStr = dateTime.format(dateTimeFormatter);
System.out.println(String.format("DateTime Format : %s", dateTimeStr));
字符串转日期格式
LocalDate date1 = LocalDate.of(2021, 1, 26);
LocalDate date2 = LocalDate.parse("2021-01-26");
System.out.println(date1);
System.out.println(date2);
LocalDateTime dateTime1 = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime dateTime2 = LocalDateTime.parse("2021-01-26T12:12:22");
System.out.println(dateTime1);
System.out.println(dateTime2);
LocalTime time1 = LocalTime.of(12, 12, 22);
LocalTime time2 = LocalTime.parse("12:12:22");
System.out.println(time1);
System.out.println(time2);
日期计算
// 计算一周后的日期
LocalDate localDate = LocalDate.now();
// 方法1
LocalDate after1 = localDate.plus(1, ChronoUnit.WEEKS);
System.out.println("一周后日期:" + after1);
// 方法2
LocalDate after2 = localDate.plusWeeks(1);
System.out.println("一周后日期:" + after2);
// 计算两个日期间隔多少天,计算间隔多少年,多少月
LocalDate date1 = LocalDate.parse("2021-02-26");
LocalDate date2 = LocalDate.parse("2021-12-23");
Period period = Period.between(date1, date2);
System.out.println("date1 到 date2 相隔:" + period.getYears() + "年" + per
iod.getMonths() + "月" + period.getDays() + "天");
// 计算两个日期间隔多少天
long day = date2.toEpochDay() - date1.toEpochDay();
System.out.println(date2 + "和" + date2 + "相差" + day + "天");
获取指定日期
LocalDate today = LocalDate.now();
// 获取当前月第一天:
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMon
th());
System.out.println("当前月第一天:" + firstDayOfThisMonth);
// 获取本月最后一天
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth
());
System.out.println("本月最后一天:" + lastDayOfThisMonth);
// 获取下一天:
LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
System.out.println("下一天(次月第一天):" + nextDay);
// 获取当年最后一天
LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("当前月第一天:" + lastday);
// 获取当年最后一个周日
LocalDate lastMondayOfThisYear = lastday.with(TemporalAdjusters.lastInMont
h(DayOfWeek.SUNDAY));
System.out.println("当年最后一个周日:" + lastMondayOfThisYear);