Java8新特性 ~ Lambda表达式相关知识

目录

一、前言~写给初学Java8人的文章

二、内容简介

三、较Java7而言,更加推荐使用Java8 ==》为什么要使用Java8

四、Lambda表达式

五、函数式接口

六、方法引用和构造器引用

至此,Lambda表达式、函数式接口、方法引用我们已经学会了。


一、前言~写给初学Java8人的文章

本文综合了下述资料,整理成一份关于Java 8新特性的参考教材,希望你有所收获。

https://www.runoob.com/java/java8-new-features.html

https://blog.csdn.net/yczz/article/details/50896975

尚硅谷Java8视频

本人不是照抄照搬,而是结合自己在学习中的感受和理解,以更易懂、简介的方式去诠释Java8新特性,也算是记录自己的学习过程。

希望对你有帮助!

二、内容简介

本篇文章将从以下八个方面,从最简单的Lambda表达式到其他特性的学习

1、Lambda表达式

2、函数式接口

3、方法引用和构造器引用

4、Stream API

5、接口中的默认方法与静态方法

6、Optional类

7、新时间日期API

8、其他新特性

【提醒】在记得刚毕业一年时,去企业面试会被问到这样的几个问题?

1)有没有使用过JAVA8?

2)你觉得java8的优点是什么?

3)java的某个特性的用法?例如:Lambda、Stream、Option

4)什么是内部迭代或者外部迭代?.....

学完这篇文章你就会知道这些问题的答案。对这些问题更加深刻的理解,深有感受!

三、较Java7而言,更加推荐使用Java8 ==》为什么要使用Java8

说明:我没接触过java9,乃至更高版本的新特性,就java7和java8对比而已,代码整洁,开发更快,并行,空指针异常,这几个方面的感受最深。 

原因如下:(面试题答案)

1、速度更快 --》 增加了红黑树,优化了hash,java7时,哈希表是有数组+链表组成,经由hash计算数组的位置,然后调用equal判断是否存在,如果不存在,将是以链表的形式存储。在java8时,如果链表的长度 > 8,以及数组的长度大于64时,将不再以链表的形式存储,而变成了红黑树,这样在查找的时候速度更快 ==》 这段话,有兴趣的可以看看,如果面试遇到的话,可以讲一下,java8中增加了红黑树,红黑树的用途在哪,快在哪

2、代码更少 -》 使用Lambda表达式

3、强大的Stram API -》提供了对数据源、数组、集合的,类似于SQL的数据操作API

4、便于并行 -》在java7时支持并行开发,Fork/Join框架,拆分合并,需要使用实现RecursiveTask接口,编程很复杂,而且开发代码难以被理解,所以很少用,而在java8中提供了parallel()和sequential()方法,很容易的对数据进行并行操作

5、最大化减少空指针异常 Optional -》 下面介绍Optional类

以上1~5点的红色部分,可以稍微了解一下,如果在面试时,问到类似的问题,可以用得上哦。

四、Lambda表达式

1、概念

在Java8中引入一个新的操作符号“->”,该符号称之为:箭头符号或Lambda操作符,改操作符将Lambda表达式拆分成两部分。

左侧是Lambda表达式的参数列表

右侧是Lambda表达式的执行体,具体的操作实现部分

2、基本语法

(parameters) -> expression
或
(parameters) ->{ statements; }

3、Lambda表达式的特点

说明parameters表示参数列表,->表示连接符,{}表示内部是方法体

3.1、= 右边的类型会根据左边的函数式接口类型自动推断;

3.2、如果形参列表为空,只需保留();

3.3、如果形参只有1个,()可以省略,只需要参数的名称即可;

3.4、如果执行语句只有1句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句

3.5、形参列表的数据类型会自动推断;

3.6、lambda不会生成一个单独的内部类文件;

3.7、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错。

【备注】上述的7点描述了Lambda表达式各种简写的方式

4、Lambda格式 ~ 对上述的特点进行解读

语法格式一:无参数,无返回值
		() -> System.out.println("Hello Lambda!");

语法格式二:有一个参数,并且无返回值
		(x) -> System.out.println(x)

语法格式三:若只有一个参数,小括号可以省略不写
		x -> System.out.println(x)

语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
	Comparator<Integer> com = (x, y) -> {
		System.out.println("函数式接口");
		return Integer.compare(x, y);
	};

语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

语法格式六:支持指定参数的数据类型
 		(Integer x, Integer y) -> Integer.compare(x, y);

5、入门案例

5.1 demo1

当某些函数,只被使用一次的,则推荐使用Lambda表达式,无须声明一个函数,这就是Java8的好处

package com.dgut.edu.cn.zhenghongbin.jdk8.TestMethodRef;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class TestLambda001 {
    public static void main(String[] args) {
        // 第一种格式: 无参数、无返回值
        Runnable r =  () -> System.out.print("Hello World");
        r.run();
        System.out.println("\n格式语法1end...");

        List<String> stringList = Arrays.asList("a", "b", "c");
        // 语法格式二:有一个参数,并且无返回值
        stringList.forEach((str) -> System.out.print(str));
        System.out.println("\n格式语法2end...");

        // 语法格式三:若只有一个参数,小括号可以省略不写
        stringList.forEach( str -> System.out.print(str));
        System.out.println("\n格式语法3end...");

        // 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
        Comparator<Integer> com = (x, y) -> {
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
        System.out.println(com.compare(1, 2));
        System.out.println("\n格式语法4end...");
        // 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
        stringList.forEach( str -> System.out.print(str));
        System.out.println("\n格式语法5end...");

        // 语法格式六:支持指定参数的数据类型
        System.out.println("格式语法6end...");
        stringList.forEach( (String str) -> System.out.print(str));
    }
}

执行结果:

自此,Lambda表达式基本就学完,特别的简单。

5.1 demo2 ~ 匿名内部内到Lambda表达式

5.1.1 线程实现到Lambda表达式

         // 匿名内部类
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("tttttttttttttttttttt");
            }
        };
        runnable.run();

        // lambda表达式
        Runnable r1 = () ->System.out.println("dddddd");
        r1.run();

5.1.2 数据类型比较接口到Lambda表达式

         // 匿名内部类
        Comparator comparatorUtil = new Comparator<String>() {
            @Override
            public int compare( String a, String b) {
                return Integer.compare(a.length(), b.length());
            }
        };
        System.out.println(comparatorUtil.compare("aaa", "bbb"));
        // lambda表达式
        Comparator<String> comparator2 = (x, y) -> Integer.compare(x.length(), y.length());
        System.out.println(comparator2.compare("ccc","ddd"));

从上述的两个案例中,可以得到:函数接口

Lambda的设计者们为了让现有的功能与Lambda表达式良好兼容,考虑了很多方法,于是产生了函数接口这个概念。函数接口指的是只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式。java.lang.Runnablejava.util.concurrent.Callable是函数式接口的最佳例子。在实践中,函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface(Java 库中的所有相关接口都已经带有这个注解了):

5.1 demo3 练习Lambda表达式

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String show() {
        return "测试方法引用!";
    }

    public void getLength(Employee e){
        System.out.println(e.getName().length());
    }

    public void getLength2(){
        System.out.println(2);
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        long temp;
        temp = Double.doubleToLongBits(salary);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (age != other.age)
            return false;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }

}

练习题目

//需求:获取公司中年龄小于 35 的员工信息
    @Test
    public void test2(){
        List<Employee> filterEmployee = new ArrayList<>();
        emps.forEach( (e) -> {
            if(e.getAge() < 35){
                filterEmployee.add(e);
            }
        });
        System.out.println("=========打印=============");
        filterEmployee.forEach(System.out::println);
    }

    //需求:获取公司中工资大于 5000 的员工信息
    @Test
    public void test3(){
        List<Employee> filterEmployee = new ArrayList<>();
        emps.forEach(e -> {
            if( e.getSalary() > 5000){
                filterEmployee.add(e);
            }
        });
        System.out.println("=========打印=============");
        filterEmployee.forEach(System.out::println);
    }

6、Lambda表达式注意要点

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

一开始在学习Lambda表达式的时候,会有一个想法,就是如何去改变Lambda表达式外部的一个值。

在Lambda表示中声明,只能去修改定义在域外的使用final修饰的局部变量,而final修饰的局部变量不可变,因此是不推荐的。

如果需要改变,应该使用以下的方式。

// 需求:判断员工的工资是否大于5000,返回boolean

这个需求需要

下面注释的代码是错误的,错误提示如下图。

@Test
    public void test5(){
        boolean isMoreThan = false;
        List<Employee> moreThanEmployee = new ArrayList<>();
        emps.forEach( e -> {
            if(e.getSalary() > 5000) {
                moreThanEmployee.add(e);
            }
        });
        if(moreThanEmployee.size() == emps.size()){
            isMoreThan = true;
        }
        System.out.println("isMoreThan is : " +  isMoreThan);
    }

错误代码:

总结来说:一般使用Lambda表达式不来改变外部局部变量的值,这点是要注意的。

这是我学习Lambda表达式来最大的疑问,希望对你们有帮助

在上述中,提到一个概念叫函数式接口,接下来就来讲解一下什么是函数式接口!

五、函数式接口

定义:只包含一个抽象方法的接口,成为函数式接口

特点:在接口上有注解@FunctionInterfaca注解

其实在jdk8之前已经有很多函数式接口

java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口

这里举例四个最常用的函数式接口

1、为什么学习函数式接口呢?

1)为了让现有的功能与Lambda表达更好的兼容,代码更少

2)更好的学习Stream API ,在Stream API中,有大量的函数式接口入参,如果不明白这些概念的话,将很难使用Stream API

所以,了解常用的函数式接口非常之有用

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

除此之外,还有20来个其他接口,具体使用到再看API即可

https://www.runoob.com/java/java8-functional-interfaces.html

3、入门案例

3.1 Consumer<T> : 消费型接口、接口只有一个参数,且无返回值

      void accept(T t);


    List<String> str = Arrays.asList("aa", "bb", "cc", "dd", "ee");

    @Test
    public void test1(){
        happy(str, (m)-> m.forEach(System.out::println));

        happy2("a", (m)-> System.out.println(m));
    }

    /**
     * 消费型接口
     */
    public void happy(List<String> stringList, Consumer<List<String>> consumer){
        consumer.accept(stringList);
    }


    public void happy2(String str, Consumer<String> consumer){
        consumer.accept(str);
    }

【理解】在一开始接触消费型接口的话,非常不能理解。就上面的例子来说,如果说,要实现对参数的打印的话,为什么不直接在方法里面操作,而定义一个消费型接口函数,将具体的实现放入到具体的调用中呢。

其实,最主要的目的还是实现和定义分开,在happy方法中的第二个参数,说白了就是一个接口定义,没有具体的实现,而将具体的实现(多样化)放入到调用中,所以称之为内置函数式接口。

换一种理解来说,如果说我定义一个操作的接口,然后有两个具体操作的类型去实现这个接口,然后在调用的时候传入具体的实现类,也可以实现上述的效果,这样的话会多增加两个实现类。而采用函数式方法编程,代码更加整洁。

如果上述代码,不能理解的话,个人建议,从lambda表达式的匿名内部类到lambda表达式、还有上述的两个案例,手动敲一遍,就会知道它的好处的。

【扩展】学习这个是为了更好的去使用Java8的API,更好的使用Lambda表达式

例如:在Stream().generate()的入参中要求传入一个Suppiler参数,按照以前的编程思想,需要写一个类去实现Supplier接口传入,这样就很麻烦,如果使用Lambda就很方法了,直接写实现即可。

下面的四个方法按照上面的理解

3.2 Supplier<T> 供给型接口,接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

      T get()

 @Test
    public void test22(){
        List<Integer> numList = getNumList2(5,  ()->(int)(Math.random() * 100));
        numList.forEach(System.out::println);
    }
    public List<Integer> getNumList2(int num,Supplier<Integer> supplier){
        List<Integer> integersList = new ArrayList<>();
        for (int i = 0; i < num; i++){
            integersList.add(supplier.get());
        }
        return integersList;
    }

案例2

  @Test
    public void test222(){
        List<Integer> numList = getNumList(5, 10 ,  ()->(int)(Math.random() * 100));
        numList.forEach(System.out::println);
    }
   
    /**
     * Supplier<T> 供给型接口
     *      T get()
     */
    //需求:产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int min, int max, Supplier<Integer> supplier){
        List<Integer> integersList = new ArrayList<>();
        for (; min < max; min++){
            integersList.add(supplier.get());
        }
        return integersList;
    }

复杂Demo,结合业务

 
    List<Employee> emps = Arrays.asList(
            new Employee(101, "张三", 18, 9999.99),
            new Employee(102, "李四", 59, 6666.66),
            new Employee(103, "王五", 28, 3333.33),
            new Employee(104, "赵六", 8, 7777.77),
            new Employee(105, "田七", 38, 5555.55)
    );

   
    @Test
    public void test2222(){
        List<Integer> numList = getNumList(5, 10, ()-> emps.get(0).getId());
        numList.forEach(System.out::println);
    }
    /**
     * Supplier<T> 供给型接口
     *      T get()
     */
    //需求:产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int min, int max, Supplier<Integer> supplier){
        List<Integer> integersList = new ArrayList<>();
        for (; min < max; min++){
            integersList.add(supplier.get());
        }
        return integersList;
    }

    

结合Stream API更好

 @Test
    public void test2(){
        List<Integer> numList = getNumList(5, 10, ()-> emps.stream().limit(1).map(Employee::getId).collect(Collectors.toList()).get(0));
        numList.forEach(System.out::println);
    }

3.3 Function<T, R> 函数式接口。接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

     R applay(T)

 @Test
    public void test3(){
        int bbb = strHandler("bbb", str -> str.length() + 100);
        System.out.println(bbb);

        String s = strList("aaa", "bbb", (x, y) -> x + y);
        System.out.println(s);

        strList2("aaa", 10, (x, y) ->{
            List<String> stringList = new ArrayList<>();
            for (int i = 0 ; i < 10; i++){
                stringList.add(x + i);
            }
            return stringList;
        }).forEach(System.out::println);

    }


    /**
     * 函数式接口
     *  Function<T, R>
     *       applay
     */
    //需求:用于处理字符串
    public int strHandler(String str, Function<String, Integer> function){
        return function.apply(str);
    }

【注意】如果说是:需要两个入参的话,这时候可以参考一下子类 

    @Test
    public void test3(){
        int bbb = strHandler("bbb", str -> str.length() + 100);
        System.out.println(bbb);

        String s = strList("aaa", "bbb", (x, y) -> x + y);
        System.out.println(s);

        strList2("aaa", 10, (x, y) ->{
            List<String> stringList = new ArrayList<>();
            for (int i = 0 ; i < 10; i++){
                stringList.add(x + i);
            }
            return stringList;
        }).forEach(System.out::println);

    }




    public int strHandler2(String str, String str2, BiFunction<String, String, Integer> function){
        return function.apply(str, str2);
    }
    /**
     * 函数式接口
     *  Function<T, R>
     *       applay
     */
    //需求:用于处理字符串
    public int strHandler(String str, Function<String, Integer> function){
        return function.apply(str);
    }


    public  List<String> strList2(String str, int number, BiFunction<String , Integer ,  List<String>> biFunction){
        return biFunction.apply(str, number);
    }


    public String strList(String str, String str2, BiFunction<String , String , String > biFunction){
        return biFunction.apply(str, str2);
    }

4.4 Predicate<T> 断言型接口。接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

      boolean test(T t);

    @Test
    public void test4(){
        filterStr(emps, (e)-> e.getAge() > 55).forEach(System.out::println);
    }
    /**
     * 断言型接口
     *  Predicate<T>
     *      boolean test(T t)
     */
    //需求:将满足条件的字符串,放入集合中
    public  List<Employee> filterStr(List<Employee> employees, Predicate<Employee> predicate){
        List<Employee> employeeList = new ArrayList<>();
        employees.forEach(e->{
            if(predicate.test(e)){
                employeeList.add(e);
            }
        });
        return employeeList;
    }

自此,Java8四大内置核心函数式接口已经学会了,其他的子接口看具体的API,参考上面的案例写写一些Demo

【学习心得】

初学习上述四大内置接口时,觉得很绕,不习惯。这时不妨多敲几遍上述的代码,自己模拟上述的案例,不看源码,写出来,慢慢就能体会到里面的内涵了。

等你深有体会之后,在去使用Stream API更能理解

例如:Stream.generate(Supplier<T> s):参数:提供型接口,因此需要写入提供型接口的实现。例如:取员工中年龄大于30岁的、

emp.stream().filter(Predicate<T> predicate):参数断言型接口,引入需要写入返回boolean的接口实现,例如:emp.getId() == 101

emp.steam().limit(1).map(Function f):参数函数型接口,因此就知道需要写入一个函数式接口实现

......

六、方法引用和构造器引用

使用操作符“::”将方法名和对象或类的名字分割开来

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用

6.1 为什么使用方法引用和构造器引用?

方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

用代码演示:

静态方法不说,如果是要使用非静态方法的话,那么需要new一个实例,然后用该实例.invoke某个方法。为了让代码更加简洁,就不需要创建改对象了,直接使用方法引用。

6.2 方法引用格式
 

1、对象:: 实例方法名  

2、类:: 静态方法名 

3、类: 实例方法名

【特别特别提醒一点】格式1和2很好理解,唯一很难理解的是:3

查了很多资料,看到这句话:

详细看看下面的案例

6.3 入门学习案例

1、对象::实例方法格式

在Lambda表达式中。

对象调用实例方法 ==》 (x, y) -> 对象.invokemethodName (param1, param2)

简写为: 对象::methodName 

特备注意的一点是:lambda表达式中的左侧参数列表,必须跟对象方法的参数列表匹配上,因此可简写成方法引用

public class TestMethodRef {
    /**
     * 内部接口定义
     */
    public interface FunctionOperate{
        String toUpperCase(String str);
        int sum(int a , int b);
    }

    /**
     * 实现类
     */
    public class BizFunctionOperate implements FunctionOperate{
        @Override
        public int sum(int a, int b) {
            return a + b;
        }

        @Override
        public String toUpperCase(String str) {
            System.out.println(str.toUpperCase());
            return str.toUpperCase();
        }

    }

    @Test
    public void test001(){
        List<String> stringList = Arrays.asList("aa", "bb", "cc", "dd");

        TestMethodRef.BizFunctionOperate bizFunctionOperate  = new TestMethodRef.BizFunctionOperate();
        // 如果不使用方法引用
        stringList.forEach(x -> bizFunctionOperate.toUpperCase(x));
        // 如果使用方法引用
        stringList.forEach(bizFunctionOperate::toUpperCase);

    }
}

2、类::静态方法名

同样:若lambda表达式中的左侧参数列表,必须跟对象方法的参数列表匹配上,因此可简写成方法引用

  @Test
    public void test002(){
        List<String> stringList = Arrays.asList("aa", "bb", "cc", "dd");
        
        // 如果不使用方法引用
        stringList.forEach((x)-> System.out.println(x));
        // 如果使用方法引用
        stringList.forEach(System.out::println);

    }

说白了,跟实例::实例方法格式一样

3、 类:: 实例方方

【注意】特定条件:

当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引 用方法的第二个参数(或无参数)时:ClassName::methodName

也就是:它不是瞎用的

最长犯的一个错

举个例子:


class Car {
    //Supplier是jdk1.8的接口,这里和lamda一起使用了
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }

    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }

    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
}

 @Test
    public void test001(){
        List<String> stringList = Arrays.asList("aa", "bb", "cc", "dd");

        TestMethodRef.BizFunctionOperate bizFunctionOperate  = new TestMethodRef.BizFunctionOperate();
        // 如果不使用方法引用
        stringList.forEach(x -> bizFunctionOperate.toUpperCase(x));
        // 如果使用方法引用
        stringList.forEach(bizFunctionOperate::toUpperCase);
        
        
        List<Employee> employeeLists = Arrays.asList(new Employee("小郑")
                , new Employee("小杰"), new Employee("莎莎"));
        // 如果不使用方法引用
        employeeLists.forEach(employee -> employee.getLength2());
        // 如果使用方法引用
        employeeLists.forEach(Employee::getLength2);
    }

从上述的例子中可以看出,如果第一个参数是调用对象,并且第二个参数是需要引 用方法的第二个参数(或无参数)时 才可以引用

细细品上面这句话

【注意】这些概念一定要好好理解,无论是自己写代码还是理解别人的代码都很有帮助的哦

6.4 构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!

格式

类名::new

案例

/**
     * 构造器引用
     */
    @Test
    public void test2(){
        Function<String, Employee> fun = Employee::new;
        fun.apply("a");
        fun.apply("b");
        BiFunction<String, Integer, Employee> fun2 = Employee::new;
    }
public interface LambdaTest5 {

    abstract String creatString(char[] c);
}
public class Main {

    public static void main(String[] args) {
        LambdaTest5 lt5 = String::new;
        System.out.println(lt5.creatString(new char[]{'1','2','3','a'}));
    }
}

项目举例

6.5 数据引用

//数组引用
	@Test
	public void test8(){
		Function<Integer, String[]> fun = (args) -> new String[args];
		String[] strs = fun.apply(10);
		System.out.println(strs.length);
		
		System.out.println("--------------------------");
		
		Function<Integer, Employee[]> fun2 = Employee[] :: new;
		Employee[] emps = fun2.apply(20);
		System.out.println(emps.length);
	}

其实跟创建一个数据一样的。只是更加简洁罢了

 

至此,Lambda表达式、函数式接口、方法引用我们已经学会了。

个人学习建议:

      看完整篇文章,可能会觉得很多概念性的东西,很多人抱着就是要学习一下Java8的新语法、看看Stream流时如何操作数据的、Optional如何使用案例。

      其实不然,个人觉得更重要的是概念性的东西,而不是API如何使用,况且你去阅读大神的源码时,很多代码都会看到方法引用和函数式接口编程和lambda表达式,只有理解了它的本质概念性的精华部分,才能更好的用好API。

接下来更多的是API的案例,不会有这么多的概念咯

接下来学习Stream API,由于篇幅过长,另起一篇文章,有兴趣可以看看哦,希望对你们有帮助,谢谢

流的创建

中间操作

终止操作

....

https://blog.csdn.net/xiaozhegaa/article/details/105907130

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值