Java8的新特性(一)

主要内容

  1. Lambda表达式
  2. 函数式接口
  3. 方法引用和构造器引用
  4. Stream API
  5. 接口中默认的方法与静态方法
  6. 新时间API

Java8新特性的简介

  • 速度更快
  • 代码更少(增加了新的语法Lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针的异常(Optional)

– 其中最重要的就是Lambda表达式和Stream API

1-Lambda表达式

为什么要使用Lambda表达式

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


Java8新特性的初体验:利用Lambda表达式可以解决匿名内部类的问题;

  • 原来的比较两个数的大小可以这样来写:
    /**
     * 原来的匿名内部类
     */
    @Test
    public void test1() {
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        TreeSet<Integer> ts = new TreeSet<>(com);
    }

上面的代码实际上最关键的代码就只有一句:

Integer.compare(o1,o2);

现在有了Lambda表达式就可以这样来写:

    /**
     * Java8的Lambda表达式就可以解决匿名内部类的问题
     */
    @Test
    public void test2() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> ts = new TreeSet<>(com);
    }

  • 我们还可以再举一个栗子:
    • 之前我们写上一个类去实现Runnable接口,然后重写run方法,我们可以用匿名内部类去写:
      代码如下:
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    };

现在我们有了Lambda表达式了,我们就可以这样来写,非常的简便:

	Runnable runnable = () -> System.out.println("Hello World");

我们还可以举一个栗子:

  • 我们经常会遇到这样的需求:获取当前公司中员工年龄大于35的员工的信息
    原来我们可以这样来写:
    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.55),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    /** 需求:获取当前公司中员工年龄大于35的员工的信息 */
    @Test
    public List<Employee> filterEmployees(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

我们对上面的代码进行单元测试,如下图,我们已经对当前公司中员工年龄大于35的员工的信息进行了过滤:
在这里插入图片描述
假如这个时候,又来了一个需求:获取当前公司员工工资大于5000的员工的信息,这个时候,我又得重新写个方法,其实我们对比一下方法,要改动的地方就一句,因为这一句又得重新写上一个方法,非常的麻烦;
我们心里的第一想法就是对这个方法进行优化,我们可以利用一些设计模式来对其进行优化;

  • 优化方式一:利用策略设计模式来进行优化
    我们可以写上一个接口:
public interface MyPredicate<T> {
    public boolean test(T t);
}

我们写上一个类来实现这个接口,如果员工的年龄大于35的话,我们就返回true:

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

我们就可以这样来用了:

    public List<Employee> filterEmployees(List<Employee> list,MyPredicate<Employee> mp) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (mp.test(emp)) {
                emps.add(emp);
            }
        }
        return emps;
    }

然后,我们就可以对其进行测试了:
测试结果是正确的
这个时候,我们对接口的实现类进行扩展就可以了,给它什么策略进行过滤,就可以按照什么方式来进行过滤;但是这种方式不好的地方就是:每次实现一个策略的时候,还要单独的写上一个类;

  • 优化方式二:利用匿名内部类,利用匿名内部类来实现接口的方式来进行实现
    在这里插入图片描述
    这个时候,又回到了匿名内部类的代码冗余的问题。
  • 优化方式三:Lambda表达式
    代码如下:
List<Employee> list = filterEmployees(this.employees, (e) -> e.getSalary() <= 5000)
list.forEach(System.out::println);

测试结果如下图:
在这里插入图片描述

  • 优化方式四:利用Stream API
    代码如下:
    /** 仅仅就是对上面数据进行过滤,其他的什么之前的接口都没有 */
    @Test
    public void test7() {
        employees.stream()
                .filter((e) -> e.getSalary()>=5000)
                .forEach(System.out::println);
    }

测试结果如下:
在这里插入图片描述
如果我们只想取这四条数据的前面两条:我们还可以这样来写:
在这里插入图片描述
我们还可以举一个常见的操作的栗子:把员工信息里面的所有的员工的名字给提取出了,我们就可以这样来写:

    /** 把员工信息里面的所有的名字给提取出来 */
    @Test
    public void test8() {
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

效果如下:
在这里插入图片描述


Lambda基础语法

一、Lambda表达式的基本语法:
Java8中引入了一个新的操作符"->"该操作符也称为箭头操作符或Lambda操作符;箭头操作符将Lambda表达式拆分成两部分;

  • 左侧:Lambda表达式的参数列表;
  • 右侧:Lambda表达式所需要执行的功能:即Lambda体;

  1. 语法格式一:无参,无返回值,Lambda体只需要一条语句
 () -> System.out.println("Hello World!");

举例如下:这是两种不同的实现方式:

    @Test
    public void test1() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        runnable.run();

        Runnable runnable1 = () -> System.out.println("Hello World!");
        runnable1.run();
    }

有一个小的注意事项:在局部内部类中,引用了一个同级别的局部变量时,在Jdk1.7的时候是会报错的,但在Jdk1.8是可以的,实际上它是省略了final关键字,默认给我们加上了:
在这里插入图片描述

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

举例,代码如下:

    @Test
    public void test2() {
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("我爱Java");
    }

分析如下:对接口中抽象方法的实现
在这里插入图片描述
3. 语法格式三:若只有一个参数的话,小括号可以省略不写,但是一般我们通常都给它写上。

x -> System.out.println(x);

举例代码如下:

    @Test
    public void test3() {
        Consumer<String> con = x -> System.out.println(x);
        con.accept("我爱Java");
    }
  1. 语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句,如果有多条语句,那么大括号是不能省略的;
    @Test
    public void test4() {
        Comparator<Integer> com  = (x,y)->{
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
    }
  1. 语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写
    @Test
    public void test5() {
        Comparator<Integer> com  = (x,y)->Integer.compare(x, y);
    }
  1. 语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,我们称为类型推断;
  • 这里的参数列表的数据类型是可以不写的:
    @Test
    public void test6() {
        Comparator<Integer> com  = (Integer x,Integer y)->Integer.compare(x, y);
    }

总结
(1)左右遇一括号省:"->“左边只有一个参数时,括号可以省;”->"右边只有一条语句时,大括号可以省;
(2)左侧推断类型省:左侧的参数列表里面的参数可以根据上下文进行类型的推断,可以省略数据类型不写;


二、Lambda表达式需要函数式接口的支持:
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。可以使用一个注解:@FunctionalInterface来进行修饰一下,可以检查当前的接口是否为函数式接口;


简单使用一下:
需求:对一个数进行运算
1)定义一个接口:

	@FunctionalInterface
	public interface MyFunction {
	    public Integer getValue(Integer num);
	}

2)定义一个方法:传入一个数和一个接口,接口在调用的时候利用Lambda进行实现:

    public Integer operation(Integer num, MyFunction myFun) {
        return myFun.getValue(num);
    }

3)利用Lambda表达式对接口进行实现,并且重写里面的方法:

    @Test
    public void test(){
        Integer num = operation(100, x -> x * x);
        System.out.println(num);
    }

如果我们想要进行加法的运算,我们就可以这样来写:

    @Test
    public void test9(){
        System.out.println(operation(200, (y)->y+200));
    }

猜你喜欢

转载自blog.csdn.net/weixin_37778801/article/details/83897055