Java基础扩展提升总结:Java 8 新特性之(一)Lambda表达式

 Lambda表达式

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。

一、Java 8新特性简介

(1)Lambda表达式

Lambda允许把函数作为一个方法的参数(函数作为一个参数传递进方法中)。

(2)方法引用

方法引用提供了非常有用的语法,可以直接引用已有Java类或者对象(实例)的方法或构造器。与Lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。方法引用使用一对冒号 “::”

(3)函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为Lambda表达式。

(4)默认方法

简单的说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需要在方法名前面加个 default 关键字即可实现默认方法。

(5)Stream API

Java 8 API 添加了一个新的抽象称为流 Stream ,可以让你以一种声明的方式处理数据。Stream 使用一种雷士用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达式的高阶抽象。Stream API 可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的集合看作一种流,流在管道中传输,并且可以在管道中的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(Intermediate operation)的处理,最后由最终操作(Terminal operation)得到前面处理的结果。

(6)Optional类

Optional 类是一个可以为空的容器对象。如果值存在则isPresent()方法返回 true ,调用get() 方法返回该对象。Optional 是一个容器:它可以保存类型 T 的值,或者仅仅保存 null 。Optional 提供了很多有用的方法,这样我们就不用显示地进行空值检测。Optional 类的引入很好的解决空指针异常。

(7)Nashorn,JavaScript引擎

Nashorn 一个 javascript 引擎。

从JDK 1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码。

与先前的Rhino实现相比,这带来了2到10倍的性能提升。

(8)新的日期时间API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

  • 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

  • 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

  • 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。

  • Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

(9)Base64

在Java 8中,Base64编码已经成为Java类库的标准。

Java 8 内置了 Base64 编码的编码器和解码器。

Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

  • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
  • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
  • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。

Lambda表达式和Stream API 是重点,方法引用和新日期API要求会用,函数式接口、默认方法、Optional类为了解内容,Nashorn和Base64见过就好。

二、Lambda表达式

 1、Lambda表达式简化匿名内部类

package lambda.StuLambda1;
public class StuLambda1 {
    public static void main(String[] args) {
        //匿名内部类
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类的run方法执行了。");
            }
        });
        thread.start();

        //Lambda表达式
        Thread thread1 = new Thread(()-> System.out.println("Lambda表达式的run方法执行了。"));
        thread1.start();
    }
}

2、使用Lambda表达式自定义比较器完成ThreSet集合排序

package lambda.StuLambda1;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class StuLambda2 {
    public static void main(String[] args) {
        //自定义比较器
        Comparator<Integer> comparator=new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        };
        //Lambda表达式
        Comparator<Integer> comparator2= (o1, o2)->o1-o2;

        TreeSet<Integer> treeSet=new TreeSet<>(comparator2);
        //TreeSet<Integer> treeSet=new TreeSet<>((o1, o2)->o1-o2);

        treeSet.add(12);
        treeSet.add(10);
        treeSet.add(9);

        Iterator<Integer> iterator = treeSet.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

三、实用案例 

有这样一个需求:有一个员工集合,获取年龄大于25的员工信息 。

1、传统模式

(1)员工类Employee

package lambda.StuLambda1;

import java.util.Objects;

/*
 * wgy 2019/8/17 9:49
 */
public class Employee {
    private String name;
    private int age;
    private double salary;
    private String gender;

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

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

    public Employee() {

    }

    //省略getterhe和setter

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", gender='" + gender + '\'' +
                '}';
    }

}

(2)主类(测试类)

package lambda.StuLambda1;

import java.util.ArrayList;
import java.util.List;

public class StuLambda3 {
    public static void main(String[] args) {
        List<Employee> employees=new ArrayList<>();
        employees.add(new Employee("少泊", 26, 32000));
        employees.add(new Employee("发海", 28, 30000));
        employees.add(new Employee("真真", 18, 28000));
        employees.add(new Employee("本伟", 27, 25000));
        employees.add(new Employee("坤坤", 21, 18000));

        System.out.println("--------获取年龄大于25的员工信息----------");
        List<Employee> list = filterByAge(employees, 25);
        for (Employee e : list) {
            System.out.println(e.toString());
        }
        System.out.println("--------获取工资大于20000的员工信息----------");

        List<Employee> list2 = filterBySalary(employees, 20000);
        for (Employee e : list2) {
            System.out.println(e.toString());
        }

    }
    //(1)获取年龄大于25的员工信息

    public static List<Employee> filterByAge(List<Employee>employees,int age){
        List<Employee> list=new ArrayList<>();
        for (Employee employee : employees) {
            if(employee.getAge()>=25){
                list.add(employee);
            }
        }
        return list;
    }
    //(2)获取工资大于20000的员工信息
    public static List<Employee> filterBySalary(List<Employee>employees,double salary){
        List<Employee> list=new ArrayList<>();
        for (Employee employee : employees) {
            if(employee.getSalary()>=salary){
                list.add(employee);
            }
        }
        return list;
    }
}

 如果再添加类似需求,就需要再添加一个方法,而且这些方法都是一些相似的方法,代码重复很严重。如何解决解决这个问题?那就是策略模式。策略模式会在设计模式学习系列详细介绍。

2、策略模式:

(1)首先定义一个断言接口:用于比较

package lambda.StuLambda1;

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

(2)Employee类不变,测试类如下

我们在测试类中定义一个过滤器方法,传入需要过滤的集合和我们自定义的断言类型的对象,在调用断言方法时,实现我们自定义的断言接口即可。

package lambda.StuLambda1;

import java.util.ArrayList;
import java.util.List;

public class StuLambda3 {
    public static void main(String[] args) {
        List<Employee> employees=new ArrayList<>();
        employees.add(new Employee("少泊", 26, 32000));
        employees.add(new Employee("发海", 28, 30000));
        employees.add(new Employee("真真", 18, 28000));
        employees.add(new Employee("本伟", 27, 25000));
        employees.add(new Employee("坤坤", 21, 18000));

        System.out.println("-------------工资大于20000-----------------");

        List<Employee> list3= filter(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee e) {
                return e.getSalary()>=20000;
            }
        });
        for (Employee e : list3) {
            System.out.println(e.toString());
        }

        System.out.println("-------------年龄大于25-----------------");
        List<Employee> list4= filter(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee e) {
                return e.getAge()>=25;
            }
        });
        for (Employee e : list4) {
            System.out.println(e.toString());
        }

    }

    //(3)过滤员工信息的方法
    public static List<Employee> filter(List<Employee> employees, MyPredicate<Employee> p){
        List<Employee> list=new ArrayList<>();
        for (Employee e : employees) {
            if(p.test(e)){
                list.add(e);
            }
        }
        return list;
    }
}

3、使用Lambda表达式化简策略模式

package lambda.StuLambda1;

import java.util.ArrayList;
import java.util.List;

public class StuLambda3 {
    public static void main(String[] args) {
        List<Employee> employees=new ArrayList<>();
        employees.add(new Employee("少泊", 26, 32000));
        employees.add(new Employee("发海", 28, 30000));
        employees.add(new Employee("真真", 18, 28000));
        employees.add(new Employee("本伟", 27, 25000));
        employees.add(new Employee("坤坤", 21, 18000));

        System.out.println("------------策略模式(lambda表达式,工资大于25000)-------------------");

        List<Employee> list4=filter(employees, e->e.getSalary()>=25000);
        for (Employee employee : list4) {
            System.out.println(employee.toString());
        }
        
        System.out.println("------------策略模式(lambda表达式吗,年龄大于25)-------------------");

        List<Employee> list3=filter(employees, e->e.getAge()>=25);
        for (Employee employee : list3) {
            System.out.println(employee.toString());
        }
    }

    //(3)过滤员工信息的方法
    public static List<Employee> filter(List<Employee> employees, MyPredicate<Employee> p){
        List<Employee> list=new ArrayList<>();
        for (Employee e : employees) {
            if(p.test(e)){
                list.add(e);
            }
        }
        return list;
    }
}

(3)事实上,我们还可以使用Stream API 对Lambda表达式进行优化。

其他类不变,修改主类(测试类)如下:

package lambda.StuLambda1;

import java.util.ArrayList;
import java.util.List;

public class StuLambda3 {
    public static void main(String[] args) {
        List<Employee> employees=new ArrayList<>();
        employees.add(new Employee("少泊", 26, 32000));
        employees.add(new Employee("发海", 28, 30000));
        employees.add(new Employee("真真", 18, 28000));
        employees.add(new Employee("本伟", 27, 25000));
        employees.add(new Employee("坤坤", 21, 18000));

       System.out.println("---------Stream API优化Lambda(工资大于25000)-------------");
        employees.stream()
                .filter(e->e.getSalary()>=25000)
                .forEach(System.out::println);
    }
}

使用Stream API 我们无需自己定义 断言接口并实现,Stream API 为我们提供很多方法简化我们的开发,有关Stream API的内容,后续会详细介绍。

四、注意事项

使用Lambda表达式需要注意以下几点:

  • Lambda引入了新的操作符:“->“ (箭头操作符),->将表达式分成两部分:
    • 左侧:(参数1,参数2…)表示参数列表;
    • 右侧:{}内部是方法体 ;
  • 形参列表的数据类型会自动推断; 
  • 如果形参列表为空,只需保留(); 
  • 如果形参只有1个,()可以省略,只需要参数的名称即可; 
  • 如果执行语句只有1句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句; 
  • lambda不会生成一个单独的内部类文件; 
  • lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值