Java8学习笔记【上】【尚硅谷】

Java 8 学习笔记

1. Java 8

1.1 生态

  • Lambda 表达式
  • 函数式接口
  • 方法引用 / 构造器引用
  • Stream API
  • 接口中的默认方法 / 静态方法
  • 新时间日期 API
  • 其他新特性

1.2 新特性

  • 速度更快
java8对于底层的数据结构做了优化,例如 HashMap:数组+链表 --> 红黑树
java8对虚拟机做了优化,例如 HotSpot JVM 将移除永久区,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace
  • 代码更少
  • 强大的 Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional (Kotlin ?)

1.3 温故而知新

  • Hashmap 底层结构/原理 老话题不再阐述 …
  • 并发hashmap …
  • Java虚拟机 …
  • Java内存模型 …

2. 课前引入

2.1 问题引入

对于一个list中的数据,进行按照条件过滤,每次都要编写新的方法,而且方法中存在大量的重复代码

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.99),
            new Employee("赵六", 16, 3333.99),
            new Employee("田七", 35, 7777.99)
    );


    @Test
    public void t1(){
        //年龄大于20
        for (int i = 0; i < employees.size(); i++) {
            if (employees.get(i).getAge()>20){
                System.out.println(employees.get(i));
            }
        }
    }
    @Test
    public void t2(){
        //薪资大于7000
        for (int i = 0; i < employees.size(); i++) {
            if (employees.get(i).getSalary()>7000){
                System.out.println(employees.get(i));
            }
        }
    }

2.2 优化1:策略设计模式

策略设计模式(按照给定的策略(单独的类)进行过滤)

接口

public interface MyPredicate <T>{
    boolean test(T t);
}

接口实现:age>=35

public class FilterEmployeeByAge implements MyPredicate<Employee>{
    @Override
    public boolean test(Employee employee) {
        return employee.getAge()>=35;
    }
}

接口实现:Salary()>=7000

public class FilterEmployeeBySalary implements MyPredicate<Employee>{
    @Override
    public boolean test(Employee employee) {
        return employee.getSalary()>=7000;
    }
}

方法实现:

    /**
     * 优化1:策略设计模式(按照给定的策略(单独的类)进行过滤)
     */
    @Test
    public void test01() {
        //所有年龄大于35
        List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
        System.out.println(list.toString());

        //所有薪资大于7000
        List<Employee> list1 = filterEmployee(employees, new FilterEmployeeBySalary());
        System.out.println(list1.toString());
    }

	//实现方法,调用接口MyPredicate,主要内容 myPredicate.test()方法
    public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) {
        List<Employee> employees = new ArrayList<>();
        for (Employee emp : list) {
            if (myPredicate.test(emp)) {
                employees.add(emp);
            }
        }
        return employees;
    }

2.3 优化2:匿名内部类

    /**
     * 优化2:匿名内部类
     */
    @Test
    public void test02() {
        List<Employee> ans = filterEmployee(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee emp) {
                return emp.getSalary() >= 7000;
            }
        });
        System.out.println(ans.toString());
    }

2.3 优化3:Lambda

匿名函数

Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。

    /**
     * 优化3:lambda
     */
    @Test
    public void test03() {
        List<Employee> ans = filterEmployee(this.employees, emp -> emp.getSalary() >= 7000);
        ans.forEach(System.out::println);
    }

存在的问题:

以上的解决方案都用到了 filterEmployee 方法,也就是 都使用了 MyPredicate接口,那么有没有不使用接口的方法,即使用stream流

2.4 优化4:Stream流

    /**
     * 优化4:stream api
     */
    @Test
    public void test04() {
        //薪资>=5000
        employees.stream()
                .filter(emp -> emp.getSalary() >= 5000)
                .forEach(System.out::println);
        //获取所有姓名
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

演变过程:

垃圾代码 --> 策略模式 --> 匿名内部类 --> Lambda表达式--> stream流

3.lambda表达式

3.1 基础语法一览

image-20210813103450671

基础语法:

- 操作符:->
- 左侧:参数列表
- 右侧:执行代码块 / Lambda 体

口诀:

  • 写死小括号,拷贝右箭头,落地大括号
  • 左右遇一括号省
  • 左侧推断类型省

3.2 具体实现

  • 无参数,无返回值:() -> sout

例如 Runnable接口:

public class Test02 {
	int num = 10; //jdk 1.7以前 必须final修饰
    //1.8之后,可以不写final,系统会给你加上
    
    @Test
    public void test01(){
        //匿名内部类
        new Runnable() {
            @Override
            public void run() {
                //在局部类中引用同级局部变量
                //只读
                System.out.println("Hello World" + num);
                //如果使用num++,会报错,因为num是final
            }
        };
    }

    @Test
    public void test02(){
        //语法糖
     	Runnable runnable = () -> {
         	System.out.println("Hello Lambda");
     	};
    }
}
  • 有一个参数,无返回值
@Test
public void test03(){
    Consumer<String> consumer = (a) -> System.out.println(a);
    consumer.accept("我觉得还行!");
}
  • 有一个参数,无返回值 (小括号可以省略不写)
@Test
public void test03(){
    Consumer<String> consumer = a -> System.out.println(a);
    consumer.accept("我觉得还行!");
}
  • 有两个及以上的参数,有返回值,并且 Lambda 体中有多条语句
@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> {
        System.out.println("比较接口");
        return Integer.compare(a, b);
    };
}
  • 有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写)
@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
}
  • Lambda 表达式 参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”

类型推断 也算是jdk8以后的新特性

3.3 函数式接口定义

函数式接口:

接口中只有一个抽象方法的接口,可以使用 @FunctionalIterface 来判断是不是

测试:

  • 定义一个函数式接口:
@FunctionalInterface
public interface MyFun {

    Integer count(Integer a, Integer b);
}
  • 用一下:
@Test
public void test05(){
    MyFun myFun1 = (a, b) -> a + b;
    MyFun myFun2 = (a, b) -> a - b;
    MyFun myFun3 = (a, b) -> a * b;
    MyFun myFun4 = (a, b) -> a / b;
}
  • 再用一下:
public Integer operation(Integer a, Integer b, MyFun myFun){
    return myFun.count(a, b);
}

@Test
public void test06(){
    Integer result = operation(1, 2, (x, y) -> x + y);
    System.out.println(result);
}

3.4 课后题

image-20210813104548793

**案例一:**调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递

  • 定义实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    
    private Integer id;
    private String name;
    private Integer age;
    private Double salary;
}
  • 定义 List 传入数据
List<Employee> emps = Arrays.asList(
    new Employee(101, "Z3", 19, 9999.99),
    new Employee(102, "L4", 20, 7777.77),
    new Employee(103, "W5", 35, 6666.66),
    new Employee(104, "Tom", 44, 1111.11),
    new Employee(105, "Jerry", 60, 4444.44)
);
  • @Test
@Test
public void test01(){
    Collections.sort(emps, (e1, e2) -> {
        if (e1.getAge() == e2.getAge()){
            return e1.getName().compareTo(e2.getName());
        } else {
            return Integer.compare(e1.getAge(), e2.getAge());
        }
    });

    for (Employee emp : emps) {
        System.out.println(emp);
    }
}

**案例二:**声明函数式接口,接口中声明抽象方法,String getValue(String str); 声明类 TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值;再将一个字符串的第二个和第四个索引位置进行截取字串

**案例三:**声明一个带两个泛型的函数式接口,泛型类型为<T, R> T 为参数,R 为返回值;接口中声明对应的抽象方法;在 TestLambda 类中声明方法,使用接口作为参数,计算两个 Long 类型参数的和;在计算两个 Long 类型参数的乘积

4. 函数式接口

4.0 概念

image-20210814174254025

4.1 四大核心函数式接口

Java内置四大核心函数式接口:

函数式接口参数类型返回类型用途
Consumer 消费型接口Tvoid对类型为T的对象应用操作:void accept(T t)
Supplier 提供型接口T返回类型为T的对象:T get()
Function<T, R> 函数型接口TR对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t)
Predicate 断言型接口Tboolean确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t)

image-20210813104652519

4.1 消费型接口

	/**
     * 测试消费型接口
     */
    @Test
    public void test01() {
        happy(1000, com -> System.out.println("yrl大保健花费" + com + "元"));
    }

    public void happy(double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }

4.2 提供型接口

   /**
     * 测试供给型接口
     */
    @Test
    public void test02(){
        ArrayList<Integer> suppliertest = suppliertest(20, ()->{
            Random random=new Random();
            return random.nextInt();
        });
        System.out.println(suppliertest.toString());
    }

    public ArrayList<Integer> suppliertest(int sum, Supplier supplier){
        ArrayList<Integer> arrayList=new ArrayList<>();
        for (int i = 0; i < sum; i++) {
            Integer temp= (Integer) supplier.get();
            arrayList.add(temp);
        }
        return arrayList;
    }

4.3 函数型接口

    /**
     * 测试函数型接口
     */
    @Test
    public void test03(){
        String newStr=FunctionTest("DadasDSaddsad",str->str.substring(0,6));
        System.out.println(newStr);
    }

    public String FunctionTest(String str, Function<String,String> function){
        return function.apply(str);
    }

4.4 断言型接口

    /**
     * 测试断言型接口
     */
    @Test
    public void test04(){

        List<String> strings=Arrays.asList("dasda","dawwas","ca","cdw","cewww","cderf","11sdawasda","ccc");
        List<String> ans = PredicateTest(strings,pre->{
            System.out.println("he");
            return pre.length()>3;
        });
        System.out.println(ans.toString());
    }

    public List<String> PredicateTest(List<String> list, Predicate<String> predicate){
        List<String> list1=new ArrayList<>();

        for (String s : list) {
            if (predicate.test(s)){
                list1.add(s);
            }
        }
        return list1;
    }

4.5 其他接口

image-20210813104832534

5.引用

5.1 概念一览

image-20210814174433781

image-20210813105218249

5.2 方法引用

**定义:**若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”

语法格式:

  • 对象 :: 实例方法
  • 类 :: 静态方法
  • 类 :: 实例方法

对象::实例方法

   /**
     * 对象::实例方法名
     */
    @Test
    public void test01(){
        PrintStream ps1=System.out;
        Consumer<String> con=x->ps1.println(x);
        con.accept("hello1");

        PrintStream ps2=System.out;
        Consumer<String> con2=ps2::println;
        con2.accept("hello2");

        Consumer<String> con3=System.out::println;
        con3.accept("hello3");
    }


    /**
     * 对象::实例方法名
     */
    @Test
    public void test02(){
        Employee emp=new Employee();
        Supplier<String> sup=()->emp.getName();
        String str=sup.get();
        System.out.println(str);

        Supplier<String> sup2= emp::getName;
        String str2=sup2.get();
        System.out.println(str2);
    }

**注意:**Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致

类::静态方法

    /**
     * 类::静态方法名
     */
    public void test03(){
        Comparator<Integer> com=(x,y)->Integer.compare(x,y);
        System.out.println(com.compare(10, 18));

        Comparator<Integer> com2= Integer::compare;
        System.out.println(com2.compare(10, 18));
    }

类::实例方法

    /**
     * 类::实例方法名
     */
    @Test
    public void test04(){
        BiPredicate<String,String> bp=(x,y)->x.equals(y);
        BiPredicate<String,String> bp2= String::equals;
    }

**条件:**Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method

5.3 构造器引用

格式:

  • ClassName :: new
    /**
     * 构造器引用
     */
    @Test
    public void test05(){

        Supplier<Employee> sup=()->new Employee();

        Supplier<Employee> sup2= Employee::new;
        //无参构造
        Employee employee = sup2.get();
        System.out.println(employee);
    }


    /**
     * 构造器引用
     */
    public void test06(){
        Function<Integer,Employee> fun=(x)->new Employee();
        Function<Integer,Employee> fun2=Employee::new;
        //一个参数的构造器
        Employee emp=fun2.apply(100);
        System.out.println(emp);

        BiFunction<Integer,Integer,Employee> bf=Employee::new;
        //两个参数的构造器
    }

**注意:**需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致

代码补充:employee

package com.example.methodTest;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * @Author: GengKY
 * @Date: 2021/8/12 19:09
 */
@Getter
@Setter
@ToString
public class Employee {

    private Integer id;
    private String name;
    private Integer age;
    private Double salary;

    public Employee() {
    }

    public Employee(Integer id) {
        this.id = id;
    }

    public Employee(Integer id, Integer age) {
        this.id = id;
        this.age=age;
    }

    public Employee(Integer id, String name, Integer age, Double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
}

5.4 数组引用

语法:

  • Type :: new
    /**
     * 数组引用
     */
    @Test
    public void test07(){
        Function<Integer,String[]> fun=(x)->new String[x];
        String[] strs=fun.apply(10);
        System.out.println(strs.length);

        Function<Integer,String[]> fun2=String[]::new;
        String[] str2=fun2.apply(20);
        System.out.println(str2.length);
    }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值