JAVA8的了解和使用

 

java8最重要的是实现函数式编程。即方法本身就是一种数据类型,可作为参数传入。

 

函数式接口

只有一个抽象方法的接口,就叫函数式接口。实现类只需要实现这一个方法即可。只需要实现一个方法,可以写成lambdal表达式。(以往,是可以使用匿名内部类,但匿名内部类的不同之处,要实现接口的所有抽象方法,因此匿名内部类不完全可以用lambdal表达式来代替)

public class TestLambda {
    
    public static void main(String[] args) {
        
        Runnable task = () -> {for (int i = 0; i < 10; i++) {
            System.out.println("it is :" + Thread.currentThread().getName());
        }};
        Thread thread = new Thread(task);
        thread.start();
        
        
        for (int i = 0; i < 10; i++) {
            System.out.println("it is :" + Thread.currentThread().getName());
        }
        
    }

}

以上这种写法,函数式编程。

JDK 1.8 新增加的函数接口:java.util.function,包含了很多类,用来支持 Java的 函数式编程;

Function<T,R>   输入T,返回R

Consumer<T>    输入一个值,没有返回值

Supplier<T>   没有输入值,返回一个值

Predicate<T>   输入一个值,返回一个布尔

 

Lambda表达式

闭包???  java中的体现,内部类,闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的*变量。

 就是一个方法。入参->出参。关键在于,lambda可以直接作为方法参数。也就是一个方法【一定有返回值的方法】作为另一个方法的参数。

 

以下是lambda表达式的重要特征:   ()-> { }

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号【例如stream对map进行遍历时】。  如果是空参也可以使用()
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。 -> {}
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。如果有返回值,就要return进行声明

 

Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

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

public class Java8Tester {

    public static void main(String args[]) {

        final int num = 1;

        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));

        s.convert(2);  // 输出结果为 3

    }

 

    public interface Converter<T1, T2> {

        void convert(int i);

    }

}

说明:

Lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义), 匿名内部类也是如此;

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量;

跟匿名内部类很像,但范围要小,只能实现函数式接口;

能写成lambda表达式的,都是对于函数式接口,写的都是其实现类,例如stream的操作,方法里写的lamdal都是函数式接口的实现类;

局部的,不能用static修饰,可以调用局部变量(隐式final)和成员变量(无论实例或者静态)

 

方法引用和构造器引用

::两个英文冒号

当lambda表达式,代码块只有一行代码,不仅可以省略花括号,还可以写成方法引用。

 

 

默认方法

Java 8 新增了接口的默认方法。

简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

我们只需在方法名前面加个default关键字即可实现默认方法。  不能加static【就是给实现类用的,不是接口本身用的】,不能private【否则不能继承了】

为什么要有这个特性? 如果修改接口就要修改所有的实现类;所以直接将要接口要修改的部分,即增加的功能,直接在接口进行实现,实现类不需要修改进行实现。此时这个接口类似抽象类了,不仅有抽象方法也有非抽象方法。

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类【接口定义好规范之后不能随便再修改,否则所有的实现类都要进行修改】,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

 

 

流式编程stream

参考:

 https://blog.csdn.net/qq_20989105/article/details/81234175 

 

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

Stream API 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。

同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。

通常编写并行代码很难而且容易出错, 但使用Stream API无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。

所以说,Java 8 中首次出现的java.util.stream 是一个函数式语言+多核时代综合影响的产物。 

什么是流【其实是迭代器】
Stream 不是集合元素,它不是数据结构并不保存数据【集合是用来保存数据,是一个容器,是有数据结构的,数组,链表,队列等等】,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。【隐性的迭代器,流的形式】

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

 

构造:

因为是迭代器,所以针对的是集合和数组而言,集合和数组都可以构造stream流;

 

类型:

对于基本数据类型,有三种对应的包装类型Stream,IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>,但是boxing和 unboxing会很耗时,所以特别为这三种基本数值型提供了对应的 Stream;

 

转换:

流可以转换成数据结构,集合

 

常见操作:

Intermediate:

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

Terminal:

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

Short-circuiting:

anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

 

Optional  类

从 Java 8 引入的一个很有趣的特性是 Optional  类。Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)  NEP

本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。

Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。

 

在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:

if (user != null) {

    Address address = user.getAddress();

    if (address != null) {

        Country country = address.getCountry();

        if (country != null) {

            String isocode = country.getIsocode();

            if (isocode != null) {

                isocode = isocode.toUpperCase();

            }

        }

    }

}

变得冗长,难以维护。

 

String result = Optional.ofNullable(user)

  .flatMap(User::getAddress)

  .flatMap(Address::getCountry)

  .map(Country::getIsocode)

  .orElse("default");

 

构建

使用  of() 和 ofNullable() 方法创建包含值的 Optional;明确对象不为 null  的时候使用 of()。

如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法:

 

获取

get,可能会空,所以获取前判断使用ifPresent此外如果存在可以lamdal对元素进行操作

Optional<ExamProduct> mayExists = this.findByVidAndProductCode(examProduct.getVid(),
    examProduct.getProductCode());
mayExists.ifPresent(product -> examProduct.setId(product.getId()));

 

默认值

如果为空时,就是返默认值。

 orElse(),它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值;

 orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果;

除了 orElse() 和 orElseGet() 方法,Optional 还定义了 orElseThrow() API —— 它会在对象为空的时候抛出异常,而不是返回备选的值;

 

时间类

之前的问题:Date非线程安全,可变;时区问题麻烦

 

Base64

引入作为标准类库

Base64.Decoder和Encoder

 

上一篇:ts 中新特性--枚举


下一篇:Java8新特性-Optional