Java 8 新特性 + 实例

从实用的角度出发,总结一下经常能用到的一些特性!

1、Lambda 表达式

网上解释众说纷纭,以下也是我师傅的个人理解,是理解这个表达式来的过程。

1.1 是什么?

Lambda表达式是一个 有且只有一个实例抽象方法的接口 的 匿名实现类 的 简洁替身

其中 有且只有一个实例抽象方法的接口 就是所谓的 “函数式接口”

什么是函数式接口?

1、接口中有且仅有一个 实例抽象方法
2、允许定义default实例抽象方法与静态非抽象方法
3、允许重写Object中的public方法
4、@FunctionInterface注解不是必须的,它的存在只为了编译器更好的检查,含有该注解,但是不符合上述要求时会编译错误在这里插入图片描述

简化一下就是:

Lambda表达式是一个函数式接口 的 匿名实现类 的 简洁替身

还是拗口是吗?不要急,下面有个例子就能理解这个“定义”的来由了

场景是这样,有一个学生类,需要根据名字,年龄,分数进行一个排序
在这里插入图片描述
可以看到我们需要实现自定义的排序规则,需要,定义类,最简洁也得定义一个匿名内部类,来重写compare方法来实现我们的需求。然后去调用我们自己重写的方法

在这里我默认大家是知晓Lambda表达式的基本语法!当引入 Lambda表达式的概念以后,就可以根据其语法来进行 简化 我们的上述操作。对于下面的run方法,我们直接使用Lambda表达式来将上面具体的实现(函数)进行“传递”(Lambda 允许把函数作为一个方法的参数)
在这里插入图片描述
这样,回头再看,这个概念

Lambda表达式是一个函数式接口匿名实现类简洁替身

是不是清晰一点?我们暂时不需要明白它为什么可以这么做,只要明白,这么做,不需要定义匿名内部类,直接传递,这样可以少些一些代码。尽管这样理解有一点浅薄,但确是一个深入理解>Lambda表达式,不错的出发方向。

1.2 Lambda表达式基本格式 / 语法

(parameters) -> expression(parameters) ->{ statements; }
也就是说,形如以上规则,将参数列表,通过特定符号->传入一个表格式亦或是代码块,这样的一个表达式,我们就称其为 Lambda表达式。

同理,我们也不需要知道为什么是这样,只需要知道,这样可以用,Java8认识它就好。

其他:

  • 类型可推断时,可以省略类型

    Test<String> t = (String s) -> {System.out.println(s);}
    =>
    Test<String> t = (s) -> {System.out.println(s);}

  • 有且只有一个参数时,无需定义圆括号,但无参和多个参数需要定义圆括号

    Test<String> t = (String s) -> {System.out.println(s);}
    =>
    Test<String> t = s -> {System.out.println(s);}

  • 如果主体只包含一个语句,就不需要使用大括号,包括语句后的分号。

    Test<String> t = (String s) -> {System.out.println(s);}
    =>
    Test<String> t = s -> System.out.println(s)

  • 如果主体只包含一个带返回值的表达式,则可以省略return关键字

    s -> rerutn func(s)
    =>
    s -> func(s)

2、方法引用

双冒号语法

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

  • 构造器引用:它的语法是Class::new,或者更一般的Class< T >::new

    需要注意的是,构造方法必须是函数式接口的参数

  • 静态方法引用:它的语法是Class::static_method

  • 特定类的任意对象的方法引用:它的语法是Class::method

  • 特定对象的方法引用:它的语法是instance::method

public class Car {
   public static Car create( final Supplier< Car > supplier ) {
        return supplier.get();
    }

    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() );
    }
    public static void main(String[] args) {
        Car car = Car.create(Car::new);
        List< Car > cars = Arrays.asList(car);
        cars.forEach(Car::collide);
        //...
    }
    
}

通过方法引用,可以将方法的引用赋值给一个变量,说明方法引用也是一种函数式接口的实现

3、默认方法

默认方法就是一个在接口里面有了一个有实现的方法。

1.8之前接口中的方法是不能有方法实现的,在1.8及以后打破了这个规则,一个接口,可以拥有默认方法,也可以拥有静态方法,并拥有他们的实现

类或者接口,去实现接口时,可以重写默认方法,这样做的原因说白了,其实也是为了Lambda表达式的使用
在这里插入图片描述

4、Stream API

新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

何谓 函数式编程风格

指令性语言 => 描述性语言
从手动处理数据 => 给我需要的数据

很直白的对比就是,java语言本身就是指令性语言,每一步操作,你都需要告诉虚拟机,虚拟机在和机器去做 “交涉”!而SQL语言是典型的描述性语言,sql他不需要关注这些数据的来源,以及怎么一步步“细化”处理(分组,条件过滤),我们写的sql本身表达的意思就是,我要一个什么条件的数据,然后,执行这个sql,数据就会按照你的条件返回。

而Stream API 就是描述性语言的体现,下面看个例子~

情景:获取班级男生中,分数最高前十名的平均年龄!

普通实现:
1、找出男生,filter(man)
2、分数倒排,sort(desc)
3、取前十名,limit(1-10)
4、获取年龄,get(age)
5、计算平均值,average()

以上步骤,我们是不是得写代码一步步的去实现?这就是所谓的指令性语言

而有了 Stream API 之后,我们可以这样做

//伪代码如下
List<Student> list = new ArrayList<Student>();
double ave = Arrays.stream(list)
        .filter()
        .sorted()
        .limit(10)
        .mapToDouble()
        .average().orElse(0);

是不是很像sql,我们需要是告诉一个“数据集合”我要什么样的数据,最后,他会把你想要的东西返回给你~

Stream和普通集合的区别?

  • stream不存储元素,只是提供了操作集合的各种方法
  • 适配函数式接口
  • 延迟计算
  • 链式调用,上面的各个链式调用方法,我们称为一个操作的话,在Steam中就有“中间操作”,和“终止操作”

我们找到源码,发现,前面的方法返回都还是一个Stream,也就是可以继续 下去,而average返回就已经不是Steam了
在这里插入图片描述
在这里插入图片描述
为什么不推荐用Stream<T>操作原生数据类型?

  • 产生装箱,拆箱,影响性能
  • Stream<T>不提供数值类型的特殊方法

常用方法

  • 遍历:forEach()、peek()、map(),三个方法都是遍历所有元素,中途无法优雅中止。(除非抛出异常,宕机等)
  • 去重:distinct(),去重后,保持 顺序稳定
  • 数据类型转换
    Stream<Student> stream = Arrays.stream(stu);
    List<Student> list = stream.collect(Collectors.toList());
    Set<Student> set = stream.collect(Collectors.toSet());
    Map<String, Student> map = stream.collect(Collectors.toMap(t -> t.getName(), t -> t));
    
    Student[] arr = stream.toArray(t -> new Student[t]);
    Student[] arr = stream.toArray(Student[]::new);
    

5、Date Time API

加强对日期与时间的处理。
Clock,LocalDate,LocalTime

6、Optional 类

Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
Optional是个容器,提供各种方法,获取内部是否有值,或执行各种快捷操作。
Optional.empty()返回一个空的Optional对象,对其执行的各种操作均为空操作,无任何效果

消除箭头代码的实例
在这里插入图片描述
在这里插入图片描述
Optional常用方法
在这里插入图片描述

7、并发集合,并行数组

8、JVM的改变

使用Metaspace(JEP 122)代替持久代(PermGen space)。

在JVM参数方面:

使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize

9、锁的优化

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值