前言

b站java课程学习笔记整理。

b站视频: 黑马程序员全套Java教程_Java基础入门视频教程,零基础小白自学Java必备教程

355. 体验Lambda表达式

函数式编程思想:

尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式去做。

示例:

1
2
3
4
5
6
7
8
9
10
package LambdaDemo;

public class Demo {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("启动");
}).start();
}
}

356. Lambda表达式的标准格式

1
2
3
4
5
6
7
8
9
package LambdaDemo;

public class Demo {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("启动");
}).start();
}
}

Lambda表达式的代码分析

  • ():里面没有内容,可以看成是方法形式参数为空
  • ->:用箭头指向后面要做的事
  • {}:包含一段代码,我们成为代码块,可以看成是方法体中的内容。

三要素:形参,箭头,代码块。

  • Lambda表达式的格式:(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
  • ->:固定写法,代表指向动作。
  • 代码块: 具体要做的事,也就是以前我们写的方法体的内容。

357. Lambda表达式的练习1(抽象方法无参无返回值)

使用前提:

  • 有一个接口
  • 这个接口中有且仅有一个抽象方法
1
2
3
4
5
6
7
8
9
package Lambda练习1;

public class EatableImpl implements Eatable{
@Override
public void eat() {
System.out.println("吃吃吃");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Lambda练习1;

public class Demo {
public static void main(String[] args) {
Eatable e = new EatableImpl();
useEatable(e);
useEatable(()->{
System.out.println("吃饭");
});
}
private static void useEatable(Eatable e){
e.eat();
}
}

358. Lambda表达式的练习2(抽象方法带参无返回值)

1
2
3
4
5
6
package Lambda练习2;

public interface Flyable {
void fly(String s);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Lambda练习2;

public class FlyDemo {
public static void main(String[] args) {
useFlyable((String s) ->{
System.out.println(s);
System.out.println("飞机自驾游");
});
}
private static void useFlyable(Flyable f){
f.fly("风和日丽");
}

}

359. Lambda表达式的练习3(抽象方法带参带返回值)

1
2
3
4
5
6
package Lambda练习3;

public interface Addable {
int add(int x, int y);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package Lambda练习3;

public class AddDemo {
public static void main(String[] args) {
useAddable((int x, int y) -> {
//return x + y;
//这个就类似于方法体了。
return x-y;
});
}

public static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}

360. Lambda表达式的省略模式

  • 参数类型可以省略。但是有多个参数的情况下不能只省略一个。
  • 如果参数有且仅有一个,那么小括号可以省略。
  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至return。

361. Lambda表达式的注意事项

  • 使用Lambda一定要有接口,并且接口中有且仅有一个抽象方法。
  • 必须有上下文环境。
    • 根据局部变量的赋值Runnable r =() ->System.out.println("Lambda表达式");
    • 根据调用方法的参数new Thread(()->System.out.println("lambda表达式")).start();

362. Lambda表达式和匿名内部类的区别

所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类。
  • Lambda表达式:只能是接口

使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同

  • 匿名内部类:编译后,产生一个单独的.class字节码文件。
  • Lambda:编译后没有.class文件。对应的字节码会在运行时动态生成。

363. 接口组成更新

接口的组成

  • 常量
    • public static final
  • 抽象方法
    • public abstract
  • 默认方法(Java 8)
  • 静态方法(Java 8)
  • 私有方法(Java 9)

364. 接口中的默认方法

java中提供的更新接口的默认方法default。添加默认方法时不会强制让实现类重写这个方法(不过也可以重写)。

示例:

1
2
3
default void show3(){
System.out.println("show3");
}

365. 接口中静态方法

使用static关键字修饰。

只能同故宫接口名去调用。和static的成员方法类似。public可以省略

例子:

1
2
3
static void test(){
System.out.println("静态方法")
}

366. 接口中私有方法

  • 格式1:private 返回值类型 方法名(参数列表){}

​ 范例1:private void show(){}

  • 格式2:private static 返回值类型 方法名(参数列表){}

​ 范例2:private static void method(){}

可以是普通的私有方法,也可以是静态的私有方法。

私有方法是用来将接口中的各种方法的共同点抽象出来,简化代码的书写。

注意事项:

  • 默认方法可以调用私有的静态方法和非静态方法。
  • 静态方法只能调用私有的静态方法

367. 方法引用

通过方法引用来使用已经存在的方案。

方法引用符:::

1
2
3
4
5
6
7
8
9
10
11
12
13
package Lambda练习2;

public class FlyDemo {
public static void main(String[] args) {
// 把参数传递给了println方法
useFlyable(System.out::println);
}
private static void useFlyable(Flyable f){
f.fly("风和日丽");
}

}

原则: 可推导的就是可省略的。

368. 方法引用符

  • ::该符号位引用运算符,他所在的表达式称为方法引用。
  • 可推导的就是可省略的。
  • 方法引用是Lambda的孪生兄弟
1
2
3
4
5
6
package 方法引用符;

public interface Printable {
void printInt(int i);
}

1
2
3
4
5
6
7
8
9
10
11
package 方法引用符;

public class Demo {
public static void main(String[] args) {
usePrintable(System.out::println);
}

public static void usePrintable(Printable p){
p.printInt(666);
}
}

369. 引用类方法

引用类方法,其实就是引用类的静态方法。

  • 格式:类名::静态方法
  • 范例:Integer::parseInt

370. 引用对象的实例方法

格式:对象::成员方法

范例:"Hello world"::toUpperCase

371. 引用类的实例方法

格式:类名::成员方法

范例:String :: substring

372. 引用构造器

其实就是引用构造方法。

格式:类名::new

范例:Student::new

373. 函数式接口

有且仅有一个抽象方法的接口。(即lambda表达式的使用前提)

如何检测一个接口是不是函数式接口?

  • @FunctionalInterface 用来检查。就类似于@Override

374. 函数式接口作为方法的参数

如果方法的参数是一个函数式接口,可以使用Lambda表达式作为参数传递。

  • startThread(()->System.out.println(Thread.currentThread().getName()+"启动"));

375. 函数式接口作为方法的返回值

如果方法的返回值是一个函数式接口,可以使用Lambda表达式作为结果返回

1
2
3
private static Comparator<String>getComparator(){
return (s1,s2)->s1.length - s2.length();
}

376.函数式接口之supplier

Supplier<T>:包含一个无参的方法。

  • T get(): 获得结果
  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier<T>接口也被称为生产型接口,如果制定了泛型是什么类型,那么接口中的get方法就会产生什么类型的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package supplier使用;

import java.util.function.Supplier;

public class Demo {
public static void main(String[] args) {
String s = getString(()-> "林青霞");
System.out.println(s);
}
private static String getString(Supplier<String> sup){
return sup.get();
}
}

377. supplier接口之获取最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package supplier使用;

import java.util.function.Supplier;

public class Demo {
public static void main(String[] args) {
int[] arr = {1, 2, 23, 5, 6};
int maxValue = getMax(() -> {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
}

public static int getMax(Supplier<Integer> sup) {
return sup.get();

}
}

378. Consumer接口

Consumer<T>:包含两个方法。

  • void accept(T t): 对给定的参数执行此操作
  • default Consumer <T> andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作。
  • Consumer<T>接口也被称为消费型接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
package Consumer接口;

import java.util.function.Consumer;

public class Demo {
public static void main(String[] args) {
operatorString("dasdsa", System.out::println, s-> System.out.println(new StringBuilder(s).reverse()));
}
private static void operatorString(String name, Consumer<String> con1,Consumer<String> con2){
con1.andThen(con2).accept(name);
}
}

消费就是对数据执行一次操作。

379. 练习:按要求打印信息

  • String[] strArray = {"林青霞,30","张曼玉,35","王祖贤,33"};
  • 按照姓名:XX,年龄:XX的格式把信息打印出来。
  • 要求:
    • 把打印姓名的动作作为第一个Consumer接口的Lambda实例。
    • 把打印年龄的动作作为第二个Consumer接口的Lambda实例。
    • 将两个Consumer几口按照顺序组合到一起使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Consumer接口;

import java.util.function.Consumer;

public class Demo2 {
public static void main(String[] args) {
String[] strArray = {"林青霞,30","张曼玉,35","王祖贤,33"};

printInfo(strArray,str -> System.out.print("姓名:"+str.split(",")[0]),str->
System.out.println(",年龄"+Integer.parseInt(str.split(",")[1])));
}
private static void printInfo(String[] strArray, Consumer<String> con1,Consumer<String> con2){
for(String str : strArray){
con1.andThen(con2).accept(str);
}
}
}

380. predicate接口1

Consumer<T>:常用四个方法。

  • boolean test(T t):对给定的参数进行判断,判断逻辑由Lambda表达式实现,返回一个布尔值。
  • default Predicate<T> negate(): 返回一个逻辑的否定,对应逻辑非。
  • default Predicate<T> and(Predicate other):返回一个短路与的组合判断。
  • default Predicate<T>or(Predicate other):返回一个组合判断,对应短路或。
  • Predicate<T>接口通常用于判断参数是否满足指定的条件。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package PredicateDemo;

import java.util.function.Predicate;

public class Demo {
public static void main(String[] args) {
boolean b1 = checkString("hello",s-> s.length()>8);
System.out.println(b1);
}
private static boolean checkString(String s, Predicate<String> pre){
return pre.test(s);
}
}

381. predicate接口2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package PredicateDemo;

import java.util.function.Predicate;

public class Demo {
public static void main(String[] args) {
boolean b1 = checkString("helloworld",s-> s.length()>8,s->s.length()<15);
System.out.println(b1);
}
private static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2){
return pre1.and(pre2).test(s);
}
}

382. 练习:筛选满足条件的数据

  • String[] strArray = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};

  • 使用predicate接口,将符号要求的字符串筛选到集合ArrayList中。

  • 同时满足以下要求: 姓名长度大于2,年龄大于33

  • 分析:

    使用and方法连接判断条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package predicate应用;

import java.util.ArrayList;
import java.util.function.Predicate;

public class Demo {
public static void main(String[] args) {
String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
ArrayList<String> list = myFilter(strArray, s -> s.split(",")[0].length() > 2,
s -> Integer.parseInt(s.split(",")[1]) > 33);
for(String str:list){
System.out.println(str);
}
}

public static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> array = new ArrayList<>();
for (String str : strArray) {
if (pre1.and(pre2).test(str)) {
array.add(str);
}
}
return array;
}
}

383. Function接口

Function<T,R>:常用两个方法。

  • R apply(T t)将此函数应用于给定的参数
  • default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。

384. Function接口的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package predicate应用;


import java.util.function.Function;


public class Demo {
public static void main(String[] args) {
String s = "林青霞,33";
convert(s,name->name.split(",")[1], Integer::parseInt,i->i+70);
}
private static void convert(String s, Function<String, String>fun1,Function<String, Integer>fun2,Function<Integer, Integer>fun3){
int i = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(i);
}
}