B站视频指路:尚硅谷Java入门视频教程(在线答疑+Java面试真题)_哔哩哔哩_bilibili
写在前面:马上秋招,打算从0开始再学一遍Java,开个知识点记录贴,就当做课堂笔记吧.
Lambda表达式
Lambda是一个匿名函数 我们可以把Lambda表达式理解为一段可以传递的代码(将代码像数据一样进行传递).使用它可以写出更简洁、更灵活的代码 作为一种更紧凑的代码风格 使Java的语言能力得到了提升
例:
package java8new;
import org.junit.Test;
import java.util.Comparator;
public class LambdaTest {
@Test
public void test(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("lambda");
}
};
r1.run();
Runnable r2 = ()-> System.out.println("lambda1223234234234");
r2.run();
}
@Test
public void test2(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare = comparator.compare(12, 22);
System.out.println(compare);
//Lambda表达式的写法
Comparator<Integer> comparator2 = (o1, o2) -> Integer.compare(o1,o2);
int compare2 = comparator2.compare(43, 22);
System.out.println(compare2);
//方法引用的写法
Comparator<Integer> comparator3 = Integer::compare;
int compare3 = comparator3.compare(43, 22);
System.out.println(compare3);
}
}
Lambda表达式的使用:
1.举例:(o1, o2) -> Integer.compare(o1,o2);
2.格式:
->: lambda操作符或箭头操作符
->左边: lambda形参列表 (其实就是借口中的抽象方法的形参列表)
->右边: lambda体 (其实就是重写的抽象方法的方法体)
3.lambda的使用:六种
4.lambda表达式的本质:作为函数式接口的实例
5.若一个接口中 只声明了一个抽象方法 则称为函数式接口
①无参 无返回值
@Test
public void test(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("lambda");
}
};
r1.run();
Runnable r2 = ()-> System.out.println("lambda1223234234234");
r2.run();
}
②Lambda需要一个参数 但是没有返回值
@Test
public void test(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("爱情三十六计");
System.out.println("________________________");
Consumer<String> con = (String s) -> {
System.out.println(s);
};
con.accept("就像一场游戏");
}
③数据类型可以省略 因为可以由编译器推断得出 称为 "类型推断"
@Test
public void test(){
Consumer<String> con = (String s) -> {
System.out.println(s);
};
con.accept("就像一场游戏");
System.out.println("***************");
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con.accept("就像一场游戏");
}
④Lambda若只有一个参数时 参数的小括号可以省略
@Test
public void test(){
Consumer<String> con = (s) -> {
System.out.println(s);
};
con.accept("就像一场游戏");
System.out.println("***************");
Consumer<String> con2 = s-> {
System.out.println(s);
};
con.accept("就像一场游戏");
}
⑤Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(22, 34));
System.out.println("********************");
Comparator<Integer> comparator2 = (o1,o2)->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(comparator2.compare(22, 34));
}
⑥当Lambda体只有一条语句时 return与大括号若有 都可以省略
@Test
public void test(){
Comparator<Integer> comparator1 = (o1,o2)->{
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(22, 34));
System.out.println("********************");
Comparator<Integer> comparator2 = (o1,o2)-> o1.compareTo(o2);
System.out.println(comparator1.compare(22, 34));
}
总结:
->左边: lambda形参列表的参数类型可以省略(类型推断):若lambda形参列表只有一个参数 其一对()也可省略
->右边:lambda体应该使用一对{}包裹 若lambda体只有一条执行语句(可能是return语句) 可以省略这一对{}和return关键字
函数式接口
·只包含一个抽象方法的接口 称为函数式接口
·可以通过Lambda表达式来创建该接口对象 (若Lambda表达式抛出一个受检异常(即:非运行时异常) , 那么该异常需要在目标接口的抽象方法上进行声明)
·我们可以在一个接口上使用@FunctionalInterface 注解 这样做可以检查它是否是一个函数式接口 同时javadoc也会包含一条声明 说明这个接口是一个函数式接口
·在java.util.function包下定义了Java8的丰富的函数式接口
如何理解函数式接口
以前用匿名实现类表示的现在都可以用Lambda表达式来写
Java内置四大核心函数式接口
用这些接口来做实例化时候 可以用lambda表达式
①消费型接口 Consumer<T> void accept(T t)
@Test
public void test() {
haha(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println(aDouble);
}
});
System.out.println("**************************");
haha(400,monry -> System.out.println(monry));
}
public void haha(double monry, Consumer<Double> con){
con.accept(monry);
}
②供给型接口 Supplier<T> T get()
③函数型接口 Function<T,R> R apply(T t)
④断定型接口 Predicate<T> boolean test(T t)
@Test
public void test() {
List<String> list = Arrays.asList("北京","南京","广州","东京","普京");
List<String> list2 = filterString(list, l -> l.contains("京"));
System.out.println(list2);
}
public List<String> filterString(List<String> list , Predicate<String> pre){
ArrayList<String> fi = new ArrayList<>();
for (String s: list) {
if(pre.test(s)){
fi.add(s);
}
}
return fi;
}
方法引用
·当要传递给Lambda体的操作 已经有实现的方法了 可以使用方法引用
·方法引用可以看做是Lambda表达式深层次的表达 换句话说 方法引用就是Lambda表达式 也局势函数式接口的一个实例 通过方法的名字来指向一个方法 可以认为是Lambda表达式的一个语法糖
·要求:实现接口的抽象方法的参数列表和返回值类型 必须与方法引用的方法的参数列表和返回值类型保持一致
·格式: 使用操作符"::"将类(或对象)与方法名分割开来
·如下三种主要使用情况
①对象::实例方法名
②类::静态方法名
③类::实例方法名
①使用情境:
当要传递给Lambda体的操作 已经有实现的方法了 可以使用方法引用
②方法引用 本质上就是Lambda表达式 而lambda表达式作为函数式接口的实例 所以方法引用 也是函数式就扣实例
③方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值相同(针对①和②)
package yinyong;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.concurrent.Flow;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用的使用
*
* Created by shkstart.
*/
public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
Consumer<String> con1 = s -> System.out.println(s);
con1.accept("北京");
System.out.println("***********");
PrintStream out = System.out;
Consumer<String> con2 = out::println;
con2.accept("北京");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee employee = new Employee(1001,"TOM",25,1234455);
Supplier<String> sup1 = ()->employee.getName();
System.out.println(sup1.get());
System.out.println("***********");
Supplier<String> sup2 = employee::getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = (o1, o2) -> Integer.compare(01,o2);
System.out.println(com1.compare(12, 21));
System.out.println("***********");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(32, 21));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> function = d->Math.round(d);
System.out.println(function.apply(12.3));
System.out.println("***********");
Function<Double,Long> function2 = Math::round;
System.out.println(function2.apply(12.8));
}
// 情况三:类 :: 实例方法
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1,s2)->s1.compareTo(s2);
System.out.println(com1.compare("ASD", "QWE"));
System.out.println("***********");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("123", "234"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> com1 = (t1, t2)->t1.equals(t2);
System.out.println(com1.test("Z", "K"));
System.out.println("***********");
BiPredicate<String,String> com2 = String::equals;
System.out.println(com2.test("Z", "K"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee zz = new Employee(1234, "ZZ", 12, 213124);
Function<Employee,String>function = e->e.getName();
System.out.println(function.apply(zz));
System.out.println("***********");
Function<Employee,String>function1 = Employee::getName;
System.out.println(function1.apply(zz));
}
}
构造器引用&数组引用
构造器引用:
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致.抽象方法的返回值类型即为构造器所属类的类型
//构造器引用
//Supplier中的T get()
@Test
public void test1(){
// Supplier<Employee> supplier = new Supplier<Employee>() {
// @Override
// public Employee get() {
// return new Employee();
// }
// };
Supplier<Employee> supplier1 = ()->new Employee();
System.out.println(supplier1.get());
System.out.println("******************");
Supplier<Employee> supplier2 = Employee::new;
System.out.println(supplier2.get());
}
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> function = id -> new Employee(id);
System.out.println(function.apply(1002));
System.out.println("******************");
Function<Integer,Employee> function2 = Employee::new;
System.out.println(function2.apply(1003));
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> function = (id,name)->new Employee(id,name);
System.out.println(function.apply(1234, "ZK"));
System.out.println("******************");
BiFunction<Integer,String,Employee> function2 = Employee::new;
System.out.println(function.apply(1734, "ER"));
}
数组引用:
把数组看作一个特殊的类 则写法与构造器引用一致
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer,String[]>function = len -> new String[len];
String[] apply = function.apply(5);
System.out.println(Arrays.toString(apply));
System.out.println("******************");
Function<Integer,String[]>function2 = String[]::new;
String[] apply2 = function.apply(12);
System.out.println(Arrays.toString(apply2));
}
Stream API
·Java8中有两大最为重要的改变 ①Lambda表达式 ②Stream API
·Stream API(java.util.stream)把真正的函数式编程风格引入到java中.这是目前为止对java类库最好的补充 因为Stream API可以极大提供Java程序员的生产力 让程序员写出高效 干净 简洁的代码
·Stream是Java8中处理集合的关键抽象概念 它可以指定你希望对集合进行的操作 可以执行非常复杂的查找、过滤、映射数据等操作 使用Stream API对集合数据进行操作 就类似于使用SQL执行的数据库查询 也可以使用Stream API来并行执行操作 简言之 Stream API提供了一种高效且简易于使用的处理数据的方式
为什么要用Stream API
·实际开发中 项目多数数据源来自于MySql、Oracle等 但现在数据源可以更多了 有MongDB、Radis等 而这些NoSQL的数据就需要Java层面去处理
·Stream和Collection集合的区别: Collection是一种静态的内存数据结构 而Stream是有关计算的 前者主要是面向内存 存储在内存中 后者是面向CPU 通过CPU实现计算
什么是Stream
Stream: 是数据渠道 用于操作数据源(集合 数组等)所产生的的元素序列
"集合讲的是数据,关注的是数据的存储; Stream将的是计算,关注的是对数据的运算"
tips:
①Stream自己不会存储元素
②Stream不会改变源对象 相反 他们会返回一个持有结果的新Stream
③Stream操作时延迟执行的 这意味着他们会订到需要结果的时候才执行
Stram的操作三个步骤
1.创建Stream
一个数据源(如:集合、数组),获取一个流
2.中间操作
一个中间操作链 对数据源的数据进行处理
3.终止操作(终端操作)
一旦执行终止操作 就执行中间操作链 并产生结果 之后 不会再被使用
Stram的实例化 (创建Stream):
创建Stream方式一:通过集合
Java8中的Collection接口被扩展 提供了两个获取流的方法
①default Stream<E> stream():返回一个顺序流
②default Stream<E> parallelStream():返回一个并行流
创建Stream方式二:通过数组
Java8中的Arrays的静态方法stream()可以获取数组流:
·static<T>Stream<T> stream<T[] array>:返回一个流
重载形式 能够处理对应基本类型的数组
①public static IntStream stream(int[] array)
②public static LongStream stream(long[] array)
③public static DoubleStream (double[] array)
创建Stream方式三:通过Stream的of()
可以调用Stream类静态方法of(),通过显示值创建一个流 它可以接收任意数量的参数
public static<T> Stream<T>of(T... values):返回一个流
创建Stream方式四:创建无限流(这种方式用的比较少)
可以使用静态方法Stream.iterate()和Stream.generate() 创建无限流
①迭代
public static Stream<T>iterate(final T seed,final UnaryOperator<T> f)
②生成
public static<T> Stream<T> generate(Supplier<T> s)
示例:
package StramAPI0327;
import org.junit.Test;
import yinyong.Employee;
import yinyong.EmployeeData;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamAPITest {
@Test
public void test(){
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
Stream<Employee> employeeStream = employees.parallelStream();
}
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1,"123",2,3);
Employee e2 = new Employee(2,"123",2,3);
Employee e3 = new Employee(3,"123",2,3);
Employee[] aa = new Employee[]{e1,e2,e3};
Stream<Employee> stream1 = Arrays.stream(aa);
}
@Test
public void test3(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7);
}
@Test
public void test4(){//
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);;
}
}
Stream的中间操作:
多个中间操作可以连接起来形成一个流水线 除非流水线上出发终止操作 否则中间操作不会执行任何的处理 而在终止操作时一次性全部处理 称为"惰性求值"
①筛选与切片
import org.junit.Test;
import yinyong.Employee;
import yinyong.EmployeeData;
import java.util.List;
import java.util.stream.Stream;
public class St1 {
@Test
public void test(){
List<Employee> employees = EmployeeData.getEmployees();
//filter(predicate p) 接收Lambda 从流中排除某些元素
Stream<Employee> stream = employees.stream();
stream.filter(e -> e.getSalary()>7000).forEach(System.out::println);
System.out.println();
//limit(n) 截断流 使其元素不超过给定数量
//跟迭代器一样 每用一次 需要重新声明一次
employees.stream().limit(2).forEach(System.out::println);
System.out.println();
//skip(n) 跳过元素 返回一个扔掉了前n个元素的流 若流中元素不足n个则返回一个空流
employees.stream().skip(6).forEach(System.out::println);
System.out.println();
//distinct() 筛选 通过流所生成元素的hachCode()和equals()去除重复元素
employees.add(new Employee(1008, "扎克伯格", 35, 2500.32));
employees.stream().distinct().forEach(System.out::println);
}
}
②映射
@Test
public void test(){
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str->str.toUpperCase(Locale.ROOT)).forEach(System.out::println);
}
@Test
public void test(){
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> stringStream = employees.stream().map(e -> e.getName());
stringStream.filter(e->e.length()>3).forEach(System.out::println);
}
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class St1 {
@Test
public void test(){
//map
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
Stream<Stream<Character>> streamStream = list.stream().map(St1::fromStringToStream);
streamStream.forEach(s->{
s.forEach(System.out::println);
});
//flatmap
Stream<Character> characterStream = list.stream().flatMap(St1::fromStringToStream);
characterStream.forEach(System.out::println);
}
//将字符串中的多个字符构成的集合转换为对应的Stream的实例
public static Stream<Character> fromStringToStream(String str){
ArrayList<Character> list = new ArrayList<>();
for(Character c : str.toCharArray()){
list.add(c);
}
return list.stream();
}
}
③排序
public class St1 {
@Test
public void test(){
//自然排序
List<Integer> list = Arrays.asList(1,0,23,-4,45,56,12,4);
list.stream().sorted().forEach(System.out::println);
//定制排序抛出异常 因为Employee没有实现Comparable接口
// List<Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1,e2)-> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
}
}
Stream的终止操作:
·终端操作会从流的流水线生成结果 其结果可以是任何不是流的值 例如:List Integer 甚至是void
·流进行了终止操作后 不能再次使用
①匹配与查找
import org.junit.Test;
import yinyong.Employee;
import yinyong.EmployeeData;
import java.util.*;
import java.util.stream.Stream;
public class St1 {
@Test
public void test(){
List<Employee> employees = EmployeeData.getEmployees();
//是否所有的员工年龄都大于18岁
boolean b = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(b);
//是否存在员工的工资大于10000
boolean b1 = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(b1);
//是否有员工姓"雷" 这里如果有 返回的是false
boolean b2 = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(b2);
//返回第一个元素
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first);
//返回当前流中的任意元素
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println(any);
//返回流中元素的个数
long count = employees.stream().filter(employee -> employee.getSalary()>5322).count();
System.out.println(count);
//返回流中的最大值
Stream<Double> doubleStream = employees.stream().map(e -> e.getSalary());
Optional<Double> max = doubleStream.max(Double::compare);
System.out.println(max);
//返回流中的最小值的员工
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
//内部迭代
employees.stream().forEach(System.out::println);
}
}
②归约
tips: map和reduce的连接通常称为map-reduce模式 因Google用它来进行网络搜索而出名
@Test
public void test(){
//计算1-10的自然数的和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
//计算公司所有员工的工资总和
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> doubleStream = employees.stream().map(Employee::getSalary);
// Optional<Double> reduce1 = doubleStream.reduce(Double::sum);
Optional<Double> reduce1 = doubleStream.reduce((d1, d2) -> d1 + d2);
System.out.println(reduce1);
}
③收集
Collector接口中方法的实现决定了如何对流执行收集的操作(如 收集到List Set Map)
另 Collectors实用类提供了很多静态方法 可以方便地创建收集器实例 具体方法与实例如下表:
@Test
public void test(){
//查找工资大于6000的员工 结果返回为一个List或者Set
//有序用list
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> collect = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println();
//无序用set
Set<Employee> collect1 = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
collect1.forEach(System.out::println);
System.out.println();
ArrayList<Employee> collect2 = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toCollection(ArrayList::new));
collect2.forEach(System.out::println);
}
Optional类
import org.junit.Test;
import yinyong.Employee;
import yinyong.EmployeeData;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class St1 {
//过去这么写 容易出现空指针异常
@Test
public void test(){
Boy boy = new Boy();
Boy zzzz = new Boy(new Girl("zzzz"));
String girlName = getGirlNameNew(boy);
System.out.println(getGirlNameNew(zzzz));
System.out.println(girlName);
}
public String getGirlName(Boy boy){
return boy.getGirl().getName();
}
//优化后的
public String getGirlName1(Boy boy){
if(boy!=null){
if (boy.getGirl()!=null){
return boy.getGirl().getName();
}
}
return null;
}
//使用Optional类优化后的
public String getGirlNameNew(Boy boy){
Optional<Boy> boy1 = Optional.ofNullable(boy);
Boy tjw = boy1.orElse(new Boy(new Girl("tjw")));
Girl girl = tjw.getGirl();
Optional<Girl> girl1 = Optional.ofNullable(girl);
Girl tjw2 = girl1.orElse(new Girl("tjw2"));
return tjw2.getName();
}
}
class Boy{
private Girl girl;
public Boy(Girl girl) {
this.girl = girl;
}
public Boy() {
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
}
class Girl{
private String name;
public Girl(String name) {
this.name = name;
}
public Girl() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
}