一 Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
1.1 Lambda表达式语法
Java8中,引入了一个新的操作符“->”,该操作符成为箭头操作符或Lambda操作符。
箭头操作符将 Lambda 表达式拆分成两部分:
- 左侧:Lambda 表达式的参数列表
- 右侧:Lambda 表达式中所需要执行的功能,即Lambda体
1.1.1 语法格式
1)无参数时,或者有两个或以上的参数,则小括号不能省略,需要写() -> {System.out.println("halo");}
2)只有一个参数时,小括号可以省略(x, y) -> {return x + y;}
3)参数列表中的数据类型可以省略,因为编译器可以根据上下文推断出(x) -> System.out.println(x);x -> System.out.println(x);
4)当Lambda体只有一条语句时,return关键字和大括号可以省略(int x, int y) -> {return x + y;}(x, y) -> {return x + y;}
() -> System.out.println("halo")
1.1.2 类型推断
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”。1.1.3 函数式接口
1)函数式接口:接口只有一个抽象方法的接口,称为函数式接口。
2)Lambda表达式需要“函数式接口”的支持。1.2 实例
1)比较Employee 的年龄,如果年龄相等,则比较姓名
2)声明一个函数式接口,声明一个抽象方法。使用Lambda表达式获取一个字符串转换成全部大写后的字符串@Testpublic void test() {List<Employee> employeeList = Arrays.asList(new Employee(22, "子澳门"),new Employee(33, "实时"),new Employee(64, "案例"),new Employee(64, "得到"),new Employee(74, "陈浩"),new Employee(14, "洛神"));Collections.sort(employeeList, (x, y) -> {if (x.getAge() == y.getAge()) {return x.getName().compareTo(y.getName());} else {return Integer.compare(x.getAge(), y.getAge());}});for (Employee item : employeeList) {System.out.println(item);}}
@FunctionalInterfacepublic interface MyFun {String getValue(String val);}
// 声明一个方法,转换输入的字符串String getVal(String val, MyFun myFun) {return myFun.getValue(val);}
@Testpublic void test2() {String val = "abcdhskdeok";String ret = getVal(val, (x) -> x.toUpperCase());System.out.println(ret);}
二 函数式接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
2.1 自定义函数式接口
1)需要一个接口,接口中只能声明一个抽象方法
2)可以使用注解@FunctionalInterface
3)代码如下@FunctionalInterfacepublic interface MyFunc<T> {T getVal(T t);}
使用函数式接口
public class MyFuncTest {public String getval(String val, MyFunc<String> myFunc) {return myFunc.getVal(val);}@Testpublic void test1() {String a = "aaaabbbbccc";String a1 = getval(a, (x) -> {String s = a.substring(x.indexOf('b'));return s;});System.out.println(a1);}}
2.2 四大函数式接口
| 函数式接口 | 参数类型 | 返回类型 | 用途 | | —- | —- | —- | —- | | Consumer
消费型接口 | T | void | 对类型为T的对象应用操作,抽象方法:void accept(T t) | | Supplier
供给型接口 | 无 | T | 返回类型为T的对象,抽象方法:T get() | | Function
函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。抽象方法:R apply(T t) | | Predicate
断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回 boolean值。抽象方法:
boolean test(T t) |
2.3.1 消费型接口:Consumer
private void happy(double money, Consumer<Double> consumer) {consumer.accept(money);}
@Testpublic void test() {happy(100, (x) -> {System.out.println("消费了 " + x + " 元");});}
2.3.2 供给型接口:Supplier
// 需求:产生指定个数的整数,并放入到集合中public List<Integer> getNum(int num, Supplier<Integer> supplier) {List<Integer> list = new ArrayList<>();while (num > 0) {Integer integer = supplier.get();list.add(integer);num--;}return list;}
@Testpublic void test2() {List<Integer> list = getNum(10, () -> {/*** 实际上产生什么样的整数,在这里定义*/int v = (int) (Math.random() * 100);return v;});for (Integer i : list) {System.out.println(i);}}
2.3.3 函数型接口:Function
public String handler(String str, Function<String, String> func) {String s = func.apply(str);return s;}
@Testpublic void test3() {String str = "halo java";String end = handler(str, (x) -> x.toUpperCase());System.out.println(end);}
2.3.4 断定型接口:Predicate
public boolean isTrue(String str, Predicate<String> predicate) {return predicate.test(str);}
@Testpublic void test4() {boolean b = isTrue("a", (x) -> {return x.length() > 3;});System.out.println("a的长度是否大于3 " + b);}
2.4 其他类型的函数式接口
参考链接
1丶https://www.cnblogs.com/theRhyme/p/10774341.html
三 方法引用
- 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
- 可以理解为:方法引用是 Lambda 表达式的另外一种的表现形式。
- 注意:实现抽象方法的参数列表和返回值类型,必须与方法引用方法的参数列表和返回值类型保持一致!
- 方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来
3.1 方法引用语法格式
1)对象 :: 实例方法
2)类 :: 静态方法@Testpublic void test1() {PrintStream out = System.out;Consumer<String> consumer = (x) -> {System.out.println(x);}; // 这种是使用Lambda表达式实现Consumer<String> consumer1 = out::println; // 使用方法引用来实现consumer.accept("aaa");consumer1.accept("bbb");}
3)类 :: 实例方法public void test2() {Comparator<Integer> comparator = (x, y) -> {return Integer.compare(x, y);};Comparator<Integer> comparator1 = Integer::compare;int i = comparator.compare(100, 200);int i1 = comparator1.compare(100, 200);System.out.println(i);System.out.println(i1);}
4)上面的三种语法都是要求抽象方法的参数列表和方法引用的方法的参数列表一致,但是:如果Lambda表达式中的第一个参数是 实例方法的调用者,第二个参数是实例方法的参数时,或者实例方法的第二个参数为空。可以使用 类 :: 实例方法@Testpublic void test3() {Predicate<String> pre = String::isEmpty;boolean b = pre.test("aaa");System.out.println(b);}
@Testpublic void test4() {Function<String, String> func = String::trim;Function<String, String> func2 = String::toString; // toString方法的参数列表为空System.out.println(func.apply("aaa"));System.out.println(func2.apply("bbb"));}
3.2 构造器引用
与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
语法格式:ClassName :: new@Testpublic void test5() {Supplier<String> supplier = String::new;String string = supplier.get();System.out.println(string.isEmpty());}
3.3 数组引用
语法格式:type[] :: new@Testpublic void test6() {Function<Integer,String[]> func = String[]::new;String[] strings = func.apply(10);System.out.println(strings.length);}
四 Stream API
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)。 Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。4.1 Stream是什么
1)是数据渠道,用于对数据源(数组,集合等)的数据进行操作生成新的数据流
2)集合讲的是数据,流讲的是计算
3)注意:
- Stream 自己不会存储元素
- Stream 不会改变源对象,相反,而会生成一个持有结果的新Stream
Stream 操作时延迟的,这意味着会等待到需要结果的时候才会执行操作
4.2 Stream的操作步骤
4.2.1 创建 Stream对象
由一个数据源(数组,集合等)来获取到一个流
可以通过Collection系列集合提供的 Stream(),parallelStream() 方法获取流
Integer[] arr = {2, 3, 5, 7, 9, 34};List<Integer> list = Arrays.asList(arr);Stream<Integer> stream = list.stream()
可以通过 Arrays 中的静态方法 stream() 方法获取数组流
@Testpublic void test2() {Integer[] arr = {2, 3, 5, 7, 9, 34};Stream<Integer> stream = Arrays.stream(arr);System.out.println(stream);}
可以通过 Stream 类的静态方法 of() 获取流
@Testpublic void test3() {Integer[] arr = {2, 3, 5, 7, 9, 34};Stream<Integer> stream = Stream.of(arr);}
创建无限流 Stream.iterate() 方法
@Testpublic void test4() {Stream<Integer> stream = Stream.iterate(0, x -> x + 2);stream.forEach(x-> System.out.println(x));}
4.2.2 中间操作
一般分为:筛选和切片,映射,排序,查找和匹配,规约和收集
一个中间操作链,对数据源的数据进行处理。中间操作不会立即执行,只有在需要结果的时候才执行,这里定义的是规则。多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
内部迭代:迭代操作由Stream API来完成。
4.2.2.1 筛选和切片
这里不会对流中的元素进行改变,只是对流中的元素的总数进行更改
filter(Predicate p):接收Lambda表达式的断定型接口,从流中过滤掉某些元素
@Testpublic void test1() {Integer[] arr = {2, 3, 5, 7, 9, 34};Stream<Integer> arr = Stream.of(arr);Stream<Integer> stream = arr.filter((x) -> {return x > 30;});stream.forEach(System.out::println);}
limit(n):截断流,使流的数据总量不超过某个值
@Testpublic void test2(){Integer[] arr = {2, 3, 5, 7, 9, 34};Stream<Integer> arr = Stream.of(arr);Stream<Integer> stream = arr.limit(5);stream.forEach(System.out::println);}
skip(n):跳过元素,扔掉流中的前n个元素,如果流的元素不足n个,则返回空。和limit() 是互补的
@Testpublic void test3() {Integer[] arr = {2, 3, 5, 7, 9, 34};/*** 2, 3, 5, 7, 9, 34* 2, 3, 5, 7, 9* 2, 3, 5* 3, 5*/Stream.of(arr).filter(x -> x < 30).limit(3).skip(1).forEach(System.out::println);}
distinct():去重,通过流中的元素的hashCode()和equals()方法去除相同元素
@Testpublic void test4() {Integer[] arr = {2, 2, 3, 4, 5, 5, 5, 6};Stream.of(arr).distinct().forEach(System.out::print); //23456}
4.2.2.2 映射
对流中的元素按照一定的规则更改,从而生成另一个元素
map():接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
String[] arr = {"aaa", "bbb", "ccc", "ddd"};@Testpublic void test1() {Stream.of(arr).map(x -> "java_" + x).forEach(System.out::println);}
flatMap():接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Testpublic void test2() {Stream.of(arr).map(x -> "java_" + x).flatMap((x) -> {char[] chars = x.toCharArray();List<Character> list = new ArrayList<>();for (char c : chars) {list.add(c);}Stream<Character> stream = list.stream();return stream;}).forEach(System.out::println);}
peek():没有返回值,修改流中的元素
@Testpublic void test3() {List<Person> personList = Arrays.asList(new Person("lz"), new Person("kk"));List<Person> lz = personList.stream().peek(item -> {String name = item.getName();if (name.equals("lz")) {item.setAge(19);}}).collect(Collectors.toList());System.out.println(lz); // [Person(name=lz, age=19), Person(name=kk, age=0)]}
4.2.2.3 排序
自然排序和定制排序
sorted():自然排序
@Testpublic void test1() {Stream.of(ins).sorted().forEach(System.out::println);}
sorted(conparator
con): 定制排序@Testpublic void test2() {Stream.of(ins).sorted((x, y) -> {return -(x - y);}).forEach(System.out::println);}
4.2.3 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果。
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。4.2.3.1 查找和匹配
allMatch():检查是否匹配所有元素,返回布尔值
@Testpublic void test1() {boolean b = Stream.of(arr).allMatch(x -> x.length() == 3);System.out.println(b);}
anyMatch():检查是否至少有一个元素符合条件,返回布尔值
@Testpublic void test2() {boolean b = Stream.of(arr).anyMatch(x -> x.equals("ccc"));System.out.println(b);}
noneMatch():检查是否所有元素都不匹配,返回布尔值,有匹配到的元素返回 false,所有都不匹配返回 true
@Testpublic void test3() {boolean b = Stream.of(arr).noneMatch(x -> x.length() == 4);System.out.println(b); // false}
findFirst():返回流的第一个元素
public void test4() {Optional<String> optional = Stream.of(arr).findFirst();String s = optional.get();System.out.println(s);}
findAny():返回流的任意一个元素,一般使用并行流
@Testpublic void test5() {Optional<String> optional = Stream.of(arr).findAny();String s = optional.get();System.out.println(s);}
count():返回流中的元素个数
public void test6(){long l = Stream.of(arr).count();System.out.println(l);}
min():返回流中的最小值,max():返回流中的最大值
@Testpublic void test7() {Integer[] ins = {3, 6, 89, 2, 3, 52, 1};Optional<Integer> max = Stream.of(ins).max((x, y) -> x - y);Integer integer = max.get();System.out.println(integer);Optional<Integer> optional = Stream.of(ins).min((x, y) -> x - y);Integer i = optional.get();System.out.println(i);}
forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)。
4.2.3.2 规约和收集
reduce():可以将流中的元素反复结合起来,得到一个值
@Testpublic void test1() {List<Integer> list = Arrays.asList(2, 4, 6, 83, 5, 7, 3, 4, 67, 7);Integer sum = list.stream().reduce(0, (x, y) -> x + y); // 0-表示起始值System.out.println(sum);}
collect():将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
@Testpublic void test() {List<Employee> employeeList = Arrays.asList(new Employee(22, "子澳门"),new Employee(33, "实时"),new Employee(24, "得到"));List<String> list = employeeList.stream().map(x -> x.getName()).collect(Collectors.toList());list.forEach(System.out::println);}
4.3 StreamAPI实例
1)返回一个数组中的数字的平方
@Testpublic void test() {List<Integer> list = Arrays.asList(3, 4, 6, 8, 3);list.stream().map(x -> x * x).forEach(System.out::println);}
2)使用map,reduce计算流中元素的个数
@Testpublic void test2() {List<Integer> list = Arrays.asList(3, 4, 6, 8, 3);long count = list.stream().count();System.out.println(count);Optional<Integer> optional = list.stream().map(x -> 1).reduce(Integer::sum);Integer sum = optional.get();System.out.println(sum);}
4.4 Optional类
Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在, 原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法
Optional.of(T t) : 创建一个 Optional 实例Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t): 若 t 不为 null,创建 Optional 实例,否则创建空实例isPresent() : 判断是否包含值orElse(T t) : 如果调用对象包含值,返回该值,否则返回torElseGet(Supplier s) : 如果调用对象包含值,返回该值,否则返回 s 获取的值map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()flatMap(Function mapper): 与 map 类似,要求返回值必须是Optional
/*** ifPresent,当T不为null时,执行lambda表达式的方法*/@Testpublic void test() {Optional<Person> optional = Optional.ofNullable(new Person());optional.ifPresent(x -> {System.out.println(x);});}
五 接口的默认方法和静态方法
5.1 默认方法
public interface MyIF {/*** 默认方法,使用关键字 default定义*/default String getName(String name) {return name;}}
接口默认方法的“类优先”原则:若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
1)选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
2)接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须在实现类中覆盖该方法来解决冲突。public interface MyIF2 {default String getName(String name) {return name;}}
public class MyImpl implements MyIF, MyIF2 {@Overridepublic String getName(String name) {return MyIF.super.getName("aaa");}}
5.2 静态方法
public interface MyIF {public static String staticMethod() {return "静态方法";}}
六 新时间日期API
使用 LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
同时,这些类当中,也提供了一些方法,来计算加上时间日期,可以得出自己想要的计划时间。
6.1 LocalDate、LocalTime、LocalDateTime
1、获取当前的日期时间
@Testpublic void test1() {LocalDateTime localDateTime = LocalDateTime.now();System.out.println(localDateTime);}
2、创建一个自己指定的时间日期对象
LocalDateTime ldt2 = LocalDateTime.of(2002, 11, 11, 23, 23, 23);System.out.println(ldt2);LocalDate ld2 = LocalDate.of(2002, 11, 3);System.out.println(ld2);LocalTime lt2 = LocalTime.of(22, 33, 11);System.out.println(lt2);
3、日期时间的计算
@Testpublic void test1() {LocalDateTime now = LocalDateTime.now();LocalDateTime time = now.plusHours(8); // 向后推迟 8 个小时System.out.println(now);System.out.println(time);}
6.2 时间戳(Instant)
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始到某个时间点所耗费的毫秒数
使用now() 方法获取的当前时间戳是以格林威治时间为准的,也即是 UTC/GMT 0 (零时区)
@Testpublic void test4() {Instant instant = Instant.now();System.out.println(instant); // 2020-08-23T11:18:38.168ZOffsetDateTime offset = instant.atOffset(ZoneOffset.ofHours(8));System.out.println(offset); // 当前时间}
6.3 Duration 和 Period
1)Duration:用于计算两个“时间”间隔
2)Period:用于计算两个“日期”间隔
@Testpublic void test5() {LocalDate ld1 = LocalDate.now();LocalDate ld2 = LocalDate.of(2023, 5, 8);Period period = Period.between(ld1, ld2);System.out.println(period.getYears());System.out.println(period.getMonths());System.out.println(period.getDays());}
@Testpublic void test6() {LocalTime lt1 = LocalTime.of(22, 22, 33);LocalTime lt2 = LocalTime.now();Duration duration = Duration.between(lt1, lt2);System.out.println(duration.toDays());System.out.println(duration.toHours());System.out.println(duration.getSeconds());}
6.4 时间矫正器
TemporalAdjuster:时间校正器。有时我们可能需要获取,例如:将日期调整到“下个周日”等操作。
TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
@Testpublic void test1() {LocalDateTime ldt = LocalDateTime.now();System.out.println(ldt);LocalDateTime ldt2 = ldt.withDayOfMonth(10);System.out.println(ldt2);LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); // 下周五System.out.println(ldt3);// 自定义 下一个工作日LocalDateTime ldt5 = ldt.with(l -> {LocalDateTime ldt4 = (LocalDateTime) l;if (ldt4.equals(DayOfWeek.FRIDAY)) {return ldt4.plusDays(3);} else if (ldt4.equals(DayOfWeek.SATURDAY)) {return ldt4.plusDays(2);} else {return ldt4.plusDays(1);}});System.out.println(ldt5);}
6.5 格式化时间
- DateTimeFormatter ```java @Test public void test2() { DateTimeFormatter df = DateTimeFormatter.ISO_DATE; LocalDateTime ldt = LocalDateTime.now(); String format = ldt.format(df); System.out.println(format); }
<a name="yZ8QE"></a># 七 注解新特性<a name="FssmH"></a>## 7.1 重复注解1)在JDK7及以前,不能重复使用注解,只能使用一次。<br />2)JDK8新增了一个注解:**@Repeatable**,用于声明当前注解为可重复注解。- 自定义的注解```java@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.SOURCE)@Repeatable(MyAnnotations.class)public @interface MyAnnotation {String value() default "";}
当重复使用注解时绑定的注解 ```java @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface MyAnnotations {
MyAnnotation[] value();
}
- 使用重复注解```javapublic class RepeatAnnotationTest {@MyAnnotation@MyAnnotationpublic void fun(){}@Testpublic void test() throws NoSuchMethodException {Class<RepeatAnnotationTest> clazz = RepeatAnnotationTest.class;Method fun = clazz.getMethod("fun");MyAnnotation[] myAnnotations = fun.getAnnotationsByType(MyAnnotation.class);for (MyAnnotation i : myAnnotations) {System.out.println("============");String value = i.value();System.out.println(value);}}}
7.2 类型注解
1)JDK8多了一个属性
public enum ElementType {/*** Type parameter declaration* 表示这个注解可以作用在类型的前面* @since 1.8*/TYPE_PARAMETER}
