分享

Java|Java8 新特性

 算法与编程之美 2020-08-08

前言

Java8已经出现了很久了,但是呢,还是有很多人都还在习惯使用以前的来版本JDK,或者是以前老版本的编程方式。通过一段时间对Java8新特性的学习之后,自己也深受感触,原来java8版本写代码可以这么简洁。Java8最主要的就是给我们编程的过程带来了很多便利,不仅仅是代码量少。更多的还是让程序更简洁,减少代码冗余。Java8新特性中有几个比较主要的特性,Lambda 表达式、函数式接口、方法引用和几个新增库Stream API、Optional类等。

接下来就是对自己的学习内容做一个总结。

 Lambda 表达式

2.1 Lambda表达式简介

Lambda 表达式其实就是一种匿名函数,在这之前大家可能还接触过匿名内部类,虽然也是一种匿名函数,但是它并没有Lambda表达式那么简洁,Lambda表达式的特点就是可以传递,在使用Lambda表达式的时候我们可以将代码像数据一样传递。使用Lambda表达式能够极大的减少我们代码的冗余,而且使用也相当的方便。熟练之后会大大加快我们写代码的速度。

2.2 java8的方法与老版本的对比

首先我们来尝试着找出公司员工年龄大于35岁的所有员工,这个问题都不是很难吧。我们用不同的方法来实现一下,我先给出几个不同的方法直接看看效果。
首先我们应该做的是创建员工的数据,先创建一个Staff类来代表员工,并且使用构造函数来创建员工的集合。第一种方法来看看我使用的最常规的方法:

public  List<Staff> getStaffs(List<Staff> staffs) {

        List<Staff> stas=new  ArrayList<>();

        for(Staff sta:staffs){

            if (sta.getAge()>=35){

                stas.add(sta);

            }

        }

        return stas;

    }

    @Test

    public void test1(){

        List<Staff> list =  getStaffs(staffs);

        for (Staff staf:list) {

            System.out.println(staf);

        }

    }

只看一种方法可能没有什么感觉,那么再看看第二种方法试试呢。
这种方法也叫策略设计模式,我们需要写一个接口,然后实行这个接口,最后是调用接口。这种方法看起来就比前面的那种更有层次,而且假如又需要查找员工工资大于5000的员工的话,我们就可以重新写一个类来实现接口就好了,下面来看看代码。

public  List<Staff> filterStaff(List<Staff> list,MyPredicate<Staff>  mp){

        List<Staff> staffs=new  ArrayList<>();

        for (Staff staff:list) {

            if (mp.test(staff)){

                staffs.add(staff);

            }

        }

        return staffs;

    }

    @Test

    public void test2(){

        List<Staff> list =  filterStaff(staffs,new filterStaffByAge());

        for (Staff staf:list) {

            System.out.println(staf);

        }

    }

第三种方法是使用匿名内部类的方法,其实也是和第二种方法是差不多的。

public  void test3(){

        List<Staff>  list=filterStaff(staffs, new MyPredicate<Staff>() {

            @Override

            public boolean test(Staff staff)  {

                return staff.getAge()>=35;

            }

        });

        for (Staff staf:list) {

            System.out.println(staf);

        }

    }

这些方法都是java8之前可以使用的一些方法,那么java8以后有哪些方法呢?前面的方法看了之后有没有觉得很麻烦呢?要么是重新写方法,要么就是重新实现接口,这样就感觉很繁琐。
Java8之后就不用再重新实现接口啦。

public  void test4(){

        List<Staff>  list=filterStaff(staffs,(e)->e.getAge()>=35);

        list.forEach(System.out::println);

    }

不知道你有没有发现什么问题呢?是不是有一部分代码直接代替了接口实现的类啊。那就是我们的Lambda表达式,代替接口实现的代码如下所示,非常的简短。

(e)->e.getAge()>=35)

有没有发现我们前面的方法都建立在了同一个filterStaff函数上面呢?
那么如果我们现在只有员工数据,没有filterStaff函数java8可不可以实现找出年龄大于35的员工呢?那答案是肯定的,那就要用到java8中的Stream API啦。
代码如下:

public  void test5(){

        staffs.stream()

                 .filter((e)->e.getAge()>=35)

                .forEach(System.out::println);}

没错,就短短的4行代码就能实现我们前面写的一大堆代码。
看到这里我相信大家已经提起学习java8新特性的兴趣来了吧。

2.3 Lambda表达式语法

Lambda表达式的语法基础有哪些?

(1) Lambda 表达式的语法基础:

Java8中引入了一个新的操作符“->”该操作符称为箭头操作符或Lambda 操作符。

箭头操作符将Lambda表达式拆分成两部分:

左侧:Lambda 表达式参数列表。

右侧:Lambda 表达式中所需要执行的功能,即Lambda 体。

语法格式一:无参数,无返回值。

       () ->System.out.println("Hello");

语法格式二:有一个参数,并且无返回值。

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

语法格式三:若只有一个参数,参数的小括号可以省略。

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

语法格式四:有两个以上的参数,又返回值,有多条执行语句。

       (x,y)-> {

           System.out.println("Lambda");

            return Integer.compare(x,y);

        };

语法格式五:如果有两个以上的参数,又返回值,只有一条执行语句,Lambda 体的大括号和return可以省略。

       Comparator<Integer>con=(x,y)->Integer.compare(x,y);

语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”。

       (Integer x,Integery)->Integer.compare(x,y);

(2) Lambda 表达式需要“函数式接口”的支持。

函数式接口:接口中只有一个抽象方法,称为函数式接口。可以使用注解@FunctionalInterface修饰,可以检测是否为函数式接口。

2.4 Java8 四大内置核心函数式接口

上面有提到,Lambda表达式需要“函数式表达式”的支持,我们都已经知道,函数式接口只能有一个抽象方法。是不是每次我们都需要创建一个接口呢?当然不是,其实java8给我们准备了现成的函数式接口让我们使用,除了很特殊的时候需要自己写函数式接口外,大部分地方我们都可以使用内置的函数式接口。来看看四大内置接口及其使用方法。

(1) Consumer<T>:消费型接口

       void accep(T t);

(2) Supplier<T>:供给型接口

       T get();

(3) Function<T,R>:函数型接口

       R apply(T t);

(4) Predicate<T>:断言型接口

       boolean Test(T t);

List<Integer>  numlist=getNumList(10,()-> (int)Math.random()100);

        for (Integer num:numlist) {

            System.out.println(num);

        }

    }

    public List<Integer> getNumList(int  num, Supplier<Integer> sup){

        List<Integer> list=new  ArrayList<>();

        for (int i = 0; i < num; i++) {

            Integer n=sup.get();

            list.add(n);

        }

        return list;

    }

    //Function<T,R>:函数型接口

    //用于处理字符串

    public String StrHandler(String str,  Function<String,String> fun){

        return fun.apply(str);

    }

    @Test

    public void test3(){

        String str=StrHandler("\t\t\t 就是对快睡觉",(x)->x.trim());

        System.out.println(str);

        String uper=StrHandler("\t\t\t 就是对快睡觉",(x)->x.toUpperCase());

        System.out.println(uper);

        String newStr=StrHandler("\t\t\t  就是对快睡觉",(x)->x.substring(2,5));

        System.out.println(newStr);

    }

    //Predicate<T>:断言型接口

    //将满足条件的字符串放入集合中

    public List<String>  FilterStr(List<String> list, Predicate<String> pre){

        List<String> strList=new  ArrayList<>();

        for (String str:list) {

            if (pre.test(str)){

                strList.add(str);

            }

        }

        return strList;

    }

    @Test

    public void test4(){

        List<String> list=  Arrays.asList("aaa","bbb","ccc","bbbb","jahdgja");

        List<String>  strList=FilterStr(list,s -> s.length()>3);

        for (String str:strList) {

            System.out.println(str);

        }

    }

2.5 使用”::”方法引用

(1) 方法引用:若Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用是Lambda表达式的另外一种表现形式)

主要有三种语法格式:

①对象::实例方法名

②类::静态方法名

③类::实例方法名

注意:Lambda 体中调用方法的参数列表和返回值类型要与函数式接口中抽象方法的参数列表的返回值一致。若Lambda 参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassNane::nethod

(2) 构造器引用

格式:ClassName::new

注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!

(3) 数组引用

 Type::new

Stream API

3.1简介

Java8中有两大最为重要的改变。第-一个是Lambda表达式;另外一个则是Stream API(java. util. stream. *)。
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,StreamAPI提供了一种高效且易于使用的处理数据的方式。说实话,Stream学起来要比Lambda表达式简单多了,毕竟这只是API。

3.2 创建Stream

创建Stream其实就几种,我直接上代码吧。

public  void test1(){

        //1.可以通过Collection系列集合提供的stream()parallelStream()

        List<String> list=new  ArrayList<>();

        Stream<String> stream1 =  list.stream();

        //2.通过Arrays中的静态方法stream()获取数组流

        Staff[] staffs=new Staff[10];

        Stream<Staff> stream2 =  Arrays.stream(staffs);

        //3.通过Steam类中的静态方法of()

        Stream<String> stream3 =  Stream.of("aaa","bbb","ccc");

        //4.创建无限流

        //迭代

        Stream<Integer> stream4 =  Stream.iterate(0,(x)->x+2);

        stream4.forEach(System.out::println);

        //生成

         Stream.generate(()->Math.random());

    }

3.3 Stream的使用

Stream的作用主要就是对数据的操作,了解了他下面的一些方法之后用起来就很方便。其中重要的有以下几个:

(1)  forEach:Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。

(2)  map:map 方法用于映射每个元素到对应的结果。

(3)  filter:filter 方法用于通过设置的条件过滤出元素。

(4)  limit:limit 方法用于获取指定数量的流。

(5)  sorted:sorted 方法用于对流进行排序。

(6)  并行(parallel)程序:parallelStream 是流并行处理程序的代替方法。

(7)  Collectors:Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors可用于返回列表或字符串。

(8)  另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上。如:getMax()、getMin()、getSum()、getAverage()等等。

结语

总的来说java8新特性的难点还是在Lambda表达式上,并且这也是重点。因此,我建议在学习java8新特性时应该在Lambda表达式上多下功夫,这是一个全新的编程方式,因此会出现很多错误。起初需要很细心才能慢慢学好。不得不承认Java8的魅力确实很大。



END

主  编   |   张祯悦

责  编   |   陶兴池

 where2go 团队


微信号:算法与编程之美          

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多