1. Lambda表达式
1.1 介绍
Lambda表达式是一种没有名字的函数,也可称为闭包,是Java 8 发布的最重要新特性。
本质上是一段匿名内部类,也可以是一段可以传递的代码。
还有叫箭头函数的...
闭包
闭包就是能够读取其他函数内部变量的函数,比如在java中,方法内部的局部变量只能在方法内部使用,所以闭包可以理解为定义在一个函数内部的函数
闭包的本质就是将函数内部和函数外部链接起来的桥梁
1.2 特点
允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
1.2 和匿名内部类对比
Integer[] arr = { 1, 5, 9, 7, 3, 6, 54 };
// asList : 把数组 转换为List集合
List<Integer> integers = Arrays.asList(arr);
// 匿名内部类
integers.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println(t);
}
});
// lambda
integers.forEach(x->System.out.println(x));
1.3 应用场景
列表迭代
Map映射
Reduce聚合
代替一个不想命名的函数或是类,该函数或类往往并不复杂。
想尽量缩短代码量的各个场景均可以
1.4 代码实现
1.4.1 具体语法
1、(parameters) -> expression
2、(parameters) ->{ statements; }
1.4.2 语法特点
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写 {}
如果写上{} 就必须写return 和 ;
如果有 多条语句,必须写{} return 和 ; 也必须写
案例说明-简单
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2*x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
1.4.3 集合遍历
public static void main(String[] args) {
String[] arr = { "one", "two", "three" };
List<String> list = Arrays.asList(arr);
// jdk1.7-老版写法
for (String ele : list) {
System.out.println(ele);
}
System.out.println("---");
// jdk1.8-新版写法
list.forEach(x -> {
System.out.println(x);
});
// 就类似于这种写法,相当于自己创建了一个方法,然后遍历调用这个方法
// 把 集合中每个元素作为参数传递进去,并打印参数
for (String string : list) {
m1(string);
}
}
public static void m1(String x) {
System.out.println(x);
}
1.4.4 集合排序
public static void main(String[] args) {
Integer[] arr = { 9, 8, 10, 1, 3, 5 };
// 把数组转换为list
List<Integer> list = Arrays.asList(arr);
// jdk1.7-旧版写法,使用比较器进行排序
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list);
// jdk1.8-新版写法
// 把数组转换为list
list = Arrays.asList(arr);
list.sort((x, y) -> x - y);
System.out.println(list);
}
2. 函数式接口
2.1 介绍
英文称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可通过运用函数式编程极大地提高编程效率。
其可以被隐式转换为 lambda 表达式。
2.2 特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或默认方法
专用注解即@FunctionalInterface 检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上 抽象方法,就不能当成函数式接口去使用,也不能添加@FunctionalInterface这个注解
如果只有一个抽象方法,那么@FunctionalInterface注解 加不加 都可以当做函数式接口去使用
回调函数
简单来说就是回调函数,方法的参数是一个方法,在这个方法中对传递的方法进行调用
2.3 应用场景
想通过函数式编程,提高编程效率的各种场景均可。
2.4 代码实现
2.4.1 无参情况
public class FunInterface_01 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter func) {
// 调用接口内的成员方法
func.printMessage();
}
public static void main(String[] args) {
// 第一种调用 : 直接调用自定义call方法,传入函数
FunInterface_01.call(() -> {
System.out.println("HelloWorld!!!");
});
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter inter = () -> {
System.out.println("HelloWorld2!!!!");
};
// 调用这个实现的方法
inter.printMessage();
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter {
void printMessage();
}
2.4.2 有参情况
public class FunInterface_02 {
// 自定义静态方法,接收接口对象
public static void call(MyFunctionInter_02 func, String message) {
// 调用接口内的成员方法
func.printMessage(message);
}
public static void main(String[] args) {
// 调用需要传递的数据
String message = "有参函数式接口调用!!!";
// 第一种调用 : 直接调用自定义call方法,传入函数,并传入数据
FunInterface_02.call((str) -> {
System.out.println(str);
}, message);
// 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象
MyFunctionInter_02 inter = (str) -> {
System.out.println(str);
};
// 调用这个实现的方法
inter.printMessage(message);
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionInter_02 {
void printMessage(String message);
}
2.5 JDK自带常用的函数式接口
2.5.1 SUPPLIER接口
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
public class _03_JdkOwn_01 {
private static String getResult(Supplier<String> function) {
return function.get();
}
public static void main(String[] args) {
// 1
String before = "张三";
String after = "你好";
// 把两个字符串拼接起来
System.out.println(getResult(() -> before + after));
// 2 //创建Supplier容器,声明为_03_JdkOwn类型
// 此时并不会调用对象的构造方法,即不会创建对象
Supplier<_03_JdkOwn_01> sup = _03_JdkOwn_01::new;
_03_JdkOwn_01 jo1 = sup.get();
_03_JdkOwn_01 jo2 = sup.get();
}
public _03_JdkOwn_01() {
System.out.println("构造方法执行了");
}
}
2.5.2 CONSUMER接口
Consumer<T>接口 消费者接口所以不需要返回值
有一个accept(T)方法,用于执行消费操作,可以对给定的参数T 做任意操作
public class _04_JdkOwn_02 {
private static void consumeResult(Consumer<String> function, String message) {
function.accept(message);
}
public static void main(String[] args) {
// 传递的参数
String message = "消费一些内容!!!";
// 调用方法
consumeResult(result -> {
System.out.println(result);
}, message);
}
}
2.5.3 FUNCTION<T,R>接口
Function<T,R>接口 表示接收一个参数并产生结果的函数
顾名思义,是函数操作的
有一个R apply(T)方法,Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
public class _05_JdkOwn_03 {
// Function<参数, 返回值>
public static void convertType(Function<String, Integer> function,
String str) {
int num = function.apply(str);
System.out.println(num);
}
public static void main(String[] args) {
// 传递的参数
String str = "123";
// s是说明需要传递参数, 也可以写 (s)
convertType(s -> {
int sInt = Integer.parseInt(s);
return sInt;
}, str);
}
}
2.5.4 PREDICATE接口
Predicate<T>接口 断言接口
就是做一些判断,返回值为boolean
有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
public class _06_JdkOwn_04 {
// 自定义方法,并且 Predicate 接收String字符串类型
public static void call(Predicate<String> predicate, String isOKMessage) {
boolean isOK = predicate.test(isOKMessage);
System.out.println("isOK吗:" + isOK);
}
public static void main(String[] args) {
// 传入的参数
String input = "ok";
call((String message) -> {
// 不区分大小写比较,是ok就返回true,否则返回false
if (message.equalsIgnoreCase("ok")) {
return true;
}
return false;
}, input);
}
}
3. 方法引用和构造器调用
3.1概念说明
Lambda表达式的另外一种表现形式,提高方法复用率和灵活性。
3.2 特点
更简单、代码量更少、复用性、扩展性更高。
3.3 应用场景
若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。
不需要再复写已有API的Lambda的实现。
3.4 代码实现
3.4.1 方法引用
方法引用-3种形式
3.4.1.1 对象的引用 :: 实例方法名
public static void main(String[] args) {
Integer intObj = new Integer(123456);
// 常规lambda写法
Supplier<String> su = () -> intObj.toString();
System.out.println(su.get());
// 方法引用写法
Supplier<String> su1 = intObj::toString;
System.out.println(su1.get());
}
3.4.1.2 类名 :: 静态方法名
public static void main(String[] args) {
// 常规lambda写法
// 前两个泛型是参数类型,第三个是返回值类型
BiFunction<Integer, Integer, Integer> bi = (x, y) -> Integer.max(x, y);
int apply = bi.apply(10, 11);
System.out.println(apply);
// 方法引用写法
BiFunction<Integer, Integer, Integer> bif = Integer::max;
int apply2 = bif.apply(10, 11);
System.out.println(apply2);
}
3.4.1.3 类名 :: 实例方法名
public static void main(String[] args) {
// 常规lambda写法
// 两个泛型都是参数类型
BiPredicate<String, String> predicate = (x, y) -> x.equals(y);
System.out.println(predicate.test("a", "a"));
// 方法引用写法
// 使用第一个参数调用成员方法把第二个参数传入
// ::前面的类型 要和 第一个参数的类型一致
BiPredicate<String, String> predicate2 = String::equals;
System.out.println(predicate2.test("a", "b"));
}
3.4.3 构造器调用
public static void main(String[] args) {
// 无参构造器
// 常规lambda写法
Supplier<Object> objSup = () -> new Object();
System.out.println(objSup.get());
// 方法引用写法
Supplier<Object> s1 = Object::new;
System.out.println(s1.get());
// 有参构造器
// 常规lambda写法
Function<String, Integer> func = (x) -> new Integer(x);
System.out.println(func.apply("123456"));
// 方法引用写法
Function<String, Integer> func2 = Integer::new;
System.out.println(func2.apply("123456"));
}
3.4.4 数组调用
public static void main(String[] args) {
// lambda传统写法
Function<Integer, Integer[]> fun = (n) -> new Integer[n];
Integer[] intArray1 = fun.apply(5);
intArray1[0] = 100;
System.out.println(intArray1[0]);
// 数组引用新写法
Function<Integer, Integer[]> fun1 = Integer[]::new;
Integer[] intArray = fun1.apply(5);
intArray[0] = 100;
System.out.println(intArray[0]);
}
4. Stream API
4.1 概念说明
数据渠道、管道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API。
4.2 特点
Stream 不是数据结构,没有内部存储,自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
不支持索引访问。
延迟计算
支持并行
很容易生成数据或集合
支持过滤,查找,转换,汇总,聚合等操作。
4.3 应用场景
流式计算处理,需要延迟计算、更方便的并行计算
更灵活、简洁的集合处理方式场景
4.4 代码实现
4.4.1 运行机制说明
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
4.4.2 代码实现
4.4.2.1 生成stream流的5种方式说明
public static void main(String[] args) {
// 1 通过数组,Stream.of()
String[] str = { "a", "b", "c" };
Stream<String> str1 = Stream.of(str);
// System.out.println(str1);
// 2 通过集合
List<String> strings = Arrays.asList("a", "b", "c");
Stream<String> stream = strings.stream();
System.out.println(stream);
// 3 通过Stream.generate方法来创建
// 这是一个无限流,通过这种方法创建在操作的时候最好加上limit进行限制
Stream<Integer> generate = Stream.generate(() -> 1);
generate.limit(10).forEach(x -> System.out.println(x));
// 4 通过Stream iterate
Stream<Integer> iterate = Stream.iterate(1, x -> x + 2);
iterate.limit(100).forEach(x -> System.out.println(x));
// 5 已有类的stream源生成API
String str2 = "123456";
IntStream chars = str2.chars();
chars.forEach(x -> System.out.println(x));
}
4.4.2.2 常用转换算子
常用转换算子 filter,distinct,map,limit,skip,flatMap等
filter : 对元素进行过滤筛选,不符合的就不要了
distinct : 去掉重复的元素
skip : 跳过多少元素
limit : 取一个集合的前几条数据
map :
可以理解是在遍历集合的过程中,对元素进行操作,比如判断集合元素是否是a 返回boolean
因为 map的返回值,就是新集合中的元素,所以也可以在遍历的时候对集合的数据进行更改,比如都加 --
flatMap : 解决一个字符串数组 返回单一的字符串使用flatMap
本来集合中有两个数组,可以通过flatMap 把数组中的每一个元素都放到集合中,然后把数组去掉
注意只用此算子是不会真正进行计算的,只有后边调用动作算子才会真正计算。
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c", "a");
Stream<String> stream = strings.stream();
/**
* 对元素进行过滤筛选,不符合的就不要了
*/
// collect 把符合条件的转换为集合strings,属于动作算子,因为不用动作算子这些转换算子不会执行,所以看不到结果
// 只要 a
List<String> value = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
// 集合中只有 a 了
System.out.println(value);
// 使用过之后,需要重新生成stream
stream = strings.stream();
/**
* 跳过1个元素 原来是 abca 现在就是 bca
*/
value = stream.skip(1).collect(Collectors.toList());
// bca
System.out.println(value);
/**
* map : 可以理解是在遍历集合的过程中,对元素进行操作,比如判断集合元素是否是a 返回boolean
* 或者对集合元素进行更改数据,比如都加--
*/
stream = strings.stream();
// 判断集合元素,这样就是booelean 是a 就是true 否则就是false
List<Boolean> value1=stream.map(x -> x.equals("a")).collect(Collectors.toList());
// true,false,false,true
System.out.println(value1);
stream = strings.stream();
// 更改集合元素
value =stream.map(x -> x+"--").collect(Collectors.toList());
// a--, b--, c--, a--
System.out.println(value);
/**
* 去掉重复元素
*/
stream = strings.stream();
value = stream.distinct().collect(Collectors.toList());
// 去除一个a 只有a,b,c
System.out.println(value);
/**
* 取一个集合的前几条数据
*/
stream = strings.stream();
value = stream.limit(2).collect(Collectors.toList());
// ab
System.out.println(value);
/**
* 解决一个字符串数组 返回单一的字符串使用flatMap
*/
strings = Arrays.asList("1,2,3", "a,b,c");
stream = strings.stream();
// 本来集合中有两个数据 "1,2,3" 和 "a,b,c"
// 会把每一个元素 以 , 分割,得到字符串数组
// 然后把数组中每一个元素,都单独拿出来
// 最终就会得到 1,2,3,a,b,c 6个元素
// 通过 collect 把这6个元素 放到集合中
value =stream.map(x -> x.split(",")).flatMap(arr -> Arrays.stream(arr)).collect(Collectors.toList());
// 1, 2, 3, a, b, c
System.out.println(value);
}
4.4.2.3 常用动作算子
循环 forEach
计算 min、max、count、average
匹配 anyMatch、allMatch、noneMatch、findFirst、findAny
汇聚 reduce
收集器 collect
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c");
Stream<String> stream = strings.stream();
// 测试forEach
stream.filter(x -> x.equals("a")).forEach(x -> System.out.println(x));
// 测试count
stream = strings.stream();
long cnt = stream.count();
System.out.println(cnt);
// 测试collect
stream = strings.stream();
List<String> list = stream.map(x -> x + "--").collect(
Collectors.toList());
System.out.println(list);
}
5. 接口中的默认方法和静态方法
5.1 概念说明
1.8之前接口中只能定义public static final的变量和public abstract修饰的抽象方法。
1.8及以后版本,不仅兼容1.8以前的,并新增了默认方法定义和静态方法定义的功能。
即default方法和static方法。
让接口更灵活、更多变,更能够适应现实开发需要。
5.2 特点
默认方法
可以被重写,也可以不重写。如果重写的话,就按实现类的方法来执行。
调用的时候必须是实例化对象调用。
静态方法
跟之前的普通类的静态方法大体相同
唯一不同的是不能通过接口的实现类的对象来调用,必须是类.静态方法的方式。
5.3 应用场景
默认方法
是为了解决之前版本接口升级导致的大批实现类的强制重写方法升级的问题。
涉及到接口升级的场景可以多用,从而提高了程序的扩展性、可维护性。
静态方法
跟默认方法为类似,也是为了解决接口升级的问题,默认方法解决了接口方法增加后其子类不必要全部重写的问题,静态方法解决了一次编写静态方法所有接口及其子类通用的问题,跟lambda表达式并用效果更加,进一步提高了程序扩展性和可维护性。
允许在已有的接口中添加静态方法,接口的静态方法属于接口本身,不被继承,也需要提供方法的静态实现后,子类才可以调用。
5.4 代码实现
5.4.1 默认方法
public class _01_Interface {
public static void main(String[] args) {
MyInter inter = new MyInterImpl();
System.out.println(inter.add(3, 4));
inter.printMessage();
}
}
interface MyInter {
// 之前的用法
int add(int i, int j);
// 新加的默认方法
default void printMessage() {
System.out.println("在接口中的默认实现");
}
}
class MyInterImpl implements MyInter {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public void printMessage() {
System.out.println("在接口的实现类中的默认实现");
}
}
5.4.2 静态方法案例-1-一般情况
类名调用就行,各是各的,互不影响
public class _02_Interface {
public static void main(String[] args) {
Person.run();
Man.run();
}
}
interface Person {
public static void run() {
System.out.println("人类都可以跑!!!");
}
}
class Man implements Person {
public static void run() {
System.out.println("人类都可以跑,男人跑的更快!!!");
}
}
5.4.3 静态方法案例-2-高级使用
类名调用就行,各是各的,互不影响
public class _03_Interface {
public static void main(String[] args) {
Person_01 m1 = PersonFactory.create(Man_01::new);
m1.say();
}
}
interface PersonFactory {
public static Person_01 create(Supplier<Person_01> supplier) {
return supplier.get();
}
}
interface Person_01 {
default void say() {
System.out.println("人类会说话!!!");
}
public static void run() {
System.out.println("人类都可以跑!!!");
}
}
class Man_01 implements Person_01 {
@Override
public void say() {
System.out.println("人类会说话,男人说话更粗糙!!!");
}
public static void run() {
System.out.println("人类都可以跑,男人跑的更快!!!");
}
}