Java8新特性
Posted 我永远信仰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8新特性相关的知识,希望对你有一定的参考价值。
文章目录
Java8新特性
面试问到你Java8的新特性,你能答上来吗
Java现在更新到了很高的版本,不过不建议它一更新我们就去了解、就去学。因为很少概率能用的到,现在很多公司或企业使用的版本都是比较低的。新版本需要经过岁月的考验,才能知道哪些特性是好用的
哪些是不好用的。
Java8可以看成自Java5以来最具有革命性的版本,**非常推荐**学习Java8的新特性。
在学习的时候,会遇到的非常多听着、看着都一头雾水的概念。不要被劝退,看代码,带着疑问去学习。怎么去实现?它能用来干嘛?
第一遍学习完过后,只是给自己了解到,“哦,原来有这个东西,可以这么样子用”,想进一步掌握,还是需要多敲代码,多思考。
Java8新特性简介
说一说Java8的新特性?
1.lambda表达式:
- 速度更快
- 代码更少(增加了新的语法 )
2.强大的Stream API
-
并行流和串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率
Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过parallel() 与 sequential()在并行流与顺序流之间进行切换。
-
便于并行
3.Optional:
- 最大化减少空指针异常
- Nashorn引擎,允许在JVM上运行JS应用
4.时间和日期API
1、Lambda 表达式(重点)
关于Lambda 表达式和函数式接口,参考我的另一篇文章 Lambda表达式(重量级新特性)
2、函数式接口
1、使用lambda必须要具有接口,且要求接口中有且只有一个抽象方法。
无论是JDK内置的Runnable,Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才能使用lambda。
2、使用lambda必须具有上下文推断。
也就是方法的参数或者局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个的抽象方法的接口,称为“函数式接口”。
//添加函数式接口的注解,表示限定该接口只能有一个方法体
@FunctionalInterface
public interface Test01 {
void test01();//方法体1
//void test02();
//方法体2,如果添加了函数式接口注解,该接口有超过一个方法,将会编译报错
}
如何理解函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,java不但可以支持OOP还可以支持OOF(面向函数编程)!
在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
2.1、Java内置四大函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法: T get() |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法: R apply(T t) |
Predicate 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t) |
下面用代码举两个例子
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class Test02 {
//1.消费型接口
public void shopping(double money, Consumer<Double> con) {
con.accept(money);
}
//2.断定型。根据给定的规则,过滤集合中的字符串。规则在Predicate制定
public List<String> filterString(List<String> list, Predicate<String> pre) {
List<String> filterString = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) { //这里只是判断,在使用的时候再创建规则传过来,灵活。
filterString.add(s);
}
}
return filterString;
}
//main方法
public static void main(String[] args) {
Test02 test02 = new Test02();
//1.测试消费型接口
//普通写法
test02.shopping(400, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("买了一个鸡肉卷,花了" + aDouble);
}
});
//Lambda表达式
test02.shopping(500, (money) -> System.out.println("买了一个汉堡包,花了" + money));
//2.测试断定型接口
List<String> list = Arrays.asList("冬瓜", "西瓜", "南瓜", "北瓜", "种豆", "得瓜");
//普通写法
List<String> list1 = test02.filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
//制定规则,如果s里面存在 瓜 ,返回true
return s.contains("瓜");
}
});
System.out.println(list1);
//Lambda表达式
List<String> list2 = test02.filterString(list, (s) -> !s.contains("瓜"));//这里是不存在瓜,返回true
System.out.println(list2);
}
}
还有非常多的内置函数接口,这只是用得比较多的四个。
学习这里的时候,觉得,哎好像我直接定义一个方法就能实现同样的功能了吖,为什么还要这么复杂,还要多写这些函数式接口。
后面发现,使用这些接口,能非常的灵活。比如断定型,假如有多种过滤规则,列如上面的,一个过滤含有“瓜”的字符串,一个过滤不含有的。如果我们不使用函数式接口的话,那就是写两个方法,而且代码大部分都是一样的,造成了冗余,如果是有几十个几百个规则呢?
而函数式接口就是提供了一个模板,我们只需要在使用的时候,顺便将规则传进来就可以了。并不需要修改源码。
3、方法引用和构造器引用(Stream API中用到)
3.1、方法引用
方法引用(Metod References)
- 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
- 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法。
- 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
- 格式:使用操作符
::
将类(或对象)与方法名分隔开来。 - 如下三种主要使用情况:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
看不懂。往下看代码,待会再回来看。
用代码实现三种使用情况,将会用到上面的四个内置函数式接口。
先建一个实体类Employee
import java.util.Objects;
//定义一个员工实体类
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
System.out.println("调用了无参构造");
}
public Employee(int id) {
this.id = id;
System.out.println("调用了id参数构造");
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
System.out.println("调用了双参构造");
}
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;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\\'' +
", age=" + age +
", salary=" + salary +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id &&
age == employee.age &&
Double.compare(employee.salary, salary) == 0 &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, salary);
}
}
提供测试数据类:
package com.yong.method;
import java.util.ArrayList;
import java.util.List;
/**
* @Author cyh
* @Date 2021/8/23 16:52
*/
/**
* 提供测试数据
*/
public class EmployeeData {
public static List<Employee> getEmployees() {
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001,"貂蝉",30,9808));
list.add(new Employee(1002,"西施",32,5132));
list.add(new Employee(1003,"杨玉环",18,4567));
list.add(new Employee(1004,"王昭君",32,7845));
list.add(new Employee(1005,"西门庆",22,6954));
list.add(new Employee(1006,"菠萝吹雪",62,4521));
list.add(new Employee(1007,"韩信",27,9874));
list.add(new Employee(1008,"王大锤",48,2135));
return list;
}
}
测试:
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用的使用
* 1.对象::实例方法名
* 2.类::静态方法名
* 3.类::实例方法名
*/
public class Demo01 {
public static void main(String[] args) {
Demo01 method = new Demo01();
//1
method.test01();
method.test01_1();
//2
method.test02();
//3
method.test03();
method.test03_01();
}
/**
* 1.当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
* 对象::实例方法
*/
public void test01() {
//lambda写法
Consumer<String> con1 = s -> System.out.println(s);//打印s的值,s的值哪里来
con1.accept("乌鸦坐飞机");//s的值从这里来,accept方法接收一个值给s
/**方法引用写法
*
* System是一个类
* System.out: out是PrintStream类型的一个常量
* void println:println是PrintStream的一个方法
* 如果使用这个,那么这种情况就是lambda已经有实现的方法了,下面使用方法引用
* 对象::实例方法名
*/
PrintStream ps = System.out;
Consumer<String> con2 = ps::println; //Consumer无返回值
con2.accept("老鹰捉小鸡");
/**运行结果
* 乌鸦坐飞机
* 老鹰捉小鸡
*/
}
public void test01_1() {
//再举一个例子说明,带返回值的使用Supplier
Employee employee = new Employee(1100, "张三", 45, 2255);
//lambda
Supplier<String> sup1 = () -> employee.getName(); //注意泛型和返回类型保持一致
System.out.println(sup1.get()); //输出:张三
//方法引用
Supplier<String> sup2 = employee::getName;
System.out.println(sup2.get()); //输出:张三
}
/**
* 2.类::静态方法名
* Integer中的compare是静态方法,小于返回-1,等于返回0,大于返回1
* static int compare(int x, int y)
*/
public void test02(){
//lambda
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(33,44)); // -1
//类::静态方法名
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(63,44)); // 1
}
/**
* 3.类::实例方法名
* String中的compareTo是一个实例方法
* int compareTo(String anotherString)
*/
public void test03(){
//lambda
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abc"));//返回的是比较的第一个不同的ASCII值的差值,相同返回0
//类::实例方法名
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("ccc", "cbb"));
}
public void test03_01(){
Employee employee = new Employee(1001, "李四", 22, 8888);
//lambda
Function<Employee, String> fun1 = employee1 -> employee.getName();
System.out.println(fun1.apply(employee));//李四
//类::实例方法名
Function<Employee, Integer> fun2 = Employee::getId; //注意这里是类Employee,而不是对象employee
System.out.println(fun2.apply(employee));//1001
}
}
3.2、构造器引用
一、构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。
直接看代码
import java.util.function.BiFunction;
import java.util.function.Function;
import 以上是关于Java8新特性的主要内容,如果未能解决你的问题,请参考以下文章