java8新特性

1. lambda表达式

 

接口实现
    第一种方式:创建实现类 ,用实现类进行接口的实现
    第二种方式:通过匿名内部类进行进行实现

1.1  内部类 与 lambda表达式的区别

基于匿名内部类实现的方式,特点:
内部类:
    1 代码比较复杂,比较繁琐
    2 匿名内部类,类,执行过程中,需要进行编译,------》花费时间比较长
    3 需要编译,形成class文件,在硬盘上占用空间
    4匿名内部类 实现接口,接口中可以有多个抽象方法
lambda表达式:
    1 代码简化,不形成类,不需要编译,不需要占用硬盘空间(不生成.class文件)    
    2 lambda表达式,只能有一个抽象方法

1.2 lambda表达式使用

lambda表达式:
    格式:
    实现的接口中方法的形参  指向    方法体(方法逻辑)
        ()     ->       {}

对lambda表达式简化

    格式:()->{}
    1 参数部分的简化:
        如果方法是无参方法,()不能省略
        如果方法是有参数的方法,参数数据类型可以省略
        注意:如果参数有多个,数据类型同时省略
        注意:如果参数有一个,数据类型可以省略,并且在数据类型省略的同时,()可以省略
    
    2 方法的方法体简化
        有返回值的方法,无返回值方法
        无返回值方法:如果方法中只有一句方法的逻辑代码,{}可以省略
        有返回值的方法:如果方法只有一句代码,省略return关键词,
        在return省略的前提下,{}可以省略的

1.2.1 对接口抽象方法实现使用

  • 无参数列表,无返回值
    public interface Cat {
        void eat();
    }
    -------------->
    public class Test1 {
        public static void main(String[] args) {
            /**
             * 方式1:无参数列表,无返回值
             */
            Cat cat = () -> {
                System.out.println("我是eat实现");
            };
            cat.eat();
            /**
             * 简单写法:
             * 如果方法体中只有一句代码,可以省略{}
             * 注意:如果没有参数列表,不能省略()
             */
            Cat cat1 = () -> System.out.println("我是eat实现");
            cat1.eat();
         }

        /**
             * 简单写法:
             * 如果方法体中只有一句代码,可以省略{}
             * 注意:如果没有参数列表,不能省略()
             */
      Cat cat1 = () -> System.out.println("我是eat实现");
      cat1.eat();

  • 参数列表,无返回值
     /**
             * 参数列表,无返回值
             */
            Person p = (String name, int age) -> {
                System.out.println("名字是:"+name + "年龄是:" + age);
            };
            p.call("张三", 12);
    
            /**
             * 简单写法:
             * 如果有多个参数,可以省略参数数据类型;
             * 如果方法体中只有一句代码,可以省略{};
             */
            Person p1 = (name, age) ->{
                System.out.println("名字是:"+name + "年龄是:" + age);
            };
            p1.call("张三", 12);
    
            //最终写法
            Person p2 = (name, age) -> System.out.println("名字是:"+name + "年龄是:" + age);
            p2.call("张三", 12);

     如果有多个参数,可以省略参数数据类型;
     如果方法体中只有一句代码,可以省略{};

  • 有参数列表,有返回值
            /**
             * 有参数列表,有返回值
             */
            shape shape1 = (String b)->{
                return "面积为" +b;
            };
            String area = shape1.area("12平方公里");
            System.out.println(area);
            /**
             * 简单写法:
             * 如果只有一个参数,可以省略参数数据类型,并且可以省略参数列表括号;
             * 如果方法体只有一句代码,可以省略{},如果含有return,也可以省略retren
             */
            shape shape2 = b -> {
                return "面积为" +b;
            };
            shape2.area("12平方公里");
    
            //最终写法
            shape shape3 = b -> "面积为"+b;
            shape2.area("12平方公里");
    

    如果只有一个参数,可以省略参数数据类型,并且可以省略参数列表括号;
    如果方法体只有一句代码,可以省略{},如果含有return,也可以省略retren 

1.2.2 方法的引用

  • 静态方法的引用

                在lambda表达式中,进行方法引用,逻辑实现中的方法的参数和接口中要实现的方法的参数保持一致

    对静态方法的引用:
    格式:类名::方法名

public interface Sum {
    /**
     * 接口抽象方法
     * @param a
     * @param b
     * @return
     */
    int add(int a, int b);
}
------------------------------->
public class SumUtil {
    /**
     * 静态方法
     * @param a
     * @param b
     * @return
     */
    public static int addMethod(int a, int b){
        return a + b;
    }
}
------------------------------------------>
/**
* 静态方法调用
*/
Sum sum1 = (a, b) -> SumUtil.addMethod(a,b);
System.out.println("计算结果为:" + sum1.add(10,20));
//简化写法
Sum sum = SumUtil::addMethod;
System.out.println("计算结果为:" + sum.add(10,20));
  • 实例方法的引用 

lambda表达式对实例方法的引用:
格式:new 构造方法 ::实例方法名
         对象::实例方法名

public interface Change {
    /**
     * 接口抽象方法
     * @param s
     * @return
     */
    String toUpper(String s);
}
-------------------------------------->
public class ChangeUtil {
    /**
     * 自己写的将小写转换成大写
     * @param str
     * @return
     */
    public String upper(String str){
        String result = "";
        byte[] chars = str.getBytes();
        for (byte c:chars) {
            result += (char)(c-32);
        }
        return result;
    }
}
-------------------------------------->
        /**
         * 实例方法调用
         */
        Change change1 = s -> new ChangeUtil().upper(s);
        System.out.println(change1.toUpper("hello"));
        //简化写法
        Change change = new ChangeUtil()::upper;
        System.out.println(change.toUpper("hello"));
  •  类对实例方法引用

方法的引用
    类对实例方法引用
        类名::实例方法

/**
 * Dog类
 */
public class Dog {
    private String name;
    private Integer age;
    private String type;

    public Dog(String name, Integer age, String type) {
        this.name = name;
        this.age = age;
        this.type = type;
    }

    public Dog() {
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", type='" + type + '\'' +
                '}';
    }
}
------------------------------------------------>
/**
 * 创建Dog抽象类
 */
public interface CreateDog {
    //创建Dog对象
    Dog creDog();
}
------------------------------------------------->
        /**
         * 构造方法引用
         */
        CreateDog cc = () -> new Dog();
        System.out.println(cc.creDog());
        //简化写法
        CreateDog cc1 = Dog :: new;
        System.out.println(cc1.creDog());
  •  数组方法引用
/**
 * 创建数组抽象类
 */
public interface CreateArray {
    int[] create(int length);
}
--------------------------------->
        /**
         * 数组方法引用
         */
        CreateArray ca = (int length) -> new int[length];
        System.out.println(ca.create(5));
        //简化写法
        CreateArray ca1 = int[]::new;
        System.out.println(ca1.create(5));

2.常用接口

java8 新特性
    接口中默认是抽象方法和常量 
    接口中可以出现方法体
    1 方法有方法体,需要使用default关键词进行修饰
        default void a(){
            接口中default修饰的方法,可以有方法体,并且实现类可以有选择性的重写
        }
    2 接口中可以有静态方法
     static void a(){
        }

2.1 Predicate (条件判断的接口)

import java.util.function.Predicate;

public class Test02 {
    public static void main(String[] args) {
        /**
         * Predicate 条件判断的接口
         */
        Predicate<Integer> pre = new Predicate<Integer>(){
            @Override
            public boolean test(Integer integer) {
                return integer > 100;
            }
        };
        System.out.println(pre.test(150));

        /**
         * 使用lambda表达式
         */
        Predicate<Integer> pre1 = integer -> integer > 100;
        System.out.println(pre1.test(150));

        /**
         * 并且条件判断
         */
        Predicate<Integer> pre2 = integer -> integer > 10;
        Predicate<Integer> pre3 = integer -> integer > 90;
        //50是否大于10 并且 50是否大于90
        /**
         * 我们lambda表达式只适合含有一个抽象方法的类,我们这边发现还可以掉and方法,是不是有问题呢,不是的,and是default修饰的方法不是抽象方法
         */
        System.out.println(pre2.and(pre3).test(50));

        /**
         * 或者条件判断
         */
        Predicate<Integer> pre4 = integer -> integer > 10;
        Predicate<Integer> pre5 = integer -> integer > 90;
        //50是否大于10 或者 50是否大于90
        System.out.println(pre4.or(pre5).test(50));

        /**
         * 取反
         */
        Predicate<Integer> pre6 = integer -> integer > 10;
        //20是否大于10,在进行取反
        System.out.println(pre6.negate().test(20));
    }
}

2.2 Comparator(比较接口)

        /**
         * Comparator 比较接口
         * o1 - o2 升序
         * o2 - o1 降序
         */

        TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

        TreeSet<Integer> set1 = new TreeSet<>( (o1,o2) -> o1-o2);
        set1.add(20);
        set1.add(10);
        set1.add(35);
        set1.add(30);
        System.out.println(set1);

 

2.3 Function(类型转换接口)

        第一个泛型类型的数据转成第二个泛型类型的功能 

        /**
         * Function 类型转换接口
         */
        Function<String, Integer> fun = new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        Integer apply = fun.apply("123");
        System.out.println(apply instanceof Integer); // true

//        Function<String, Integer> fun1 = (String s) -> {return Integer.parseInt(s);};
//        Function<String, Integer> fun1 = s ->  Integer.parseInt(s);
        Function<String, Integer> fun1 = Integer::parseInt;
        Integer apply1 = fun1.apply("234");
        System.out.println(apply1 instanceof Integer); // true

3 lambda表达式+接口应用场景

3.1 stream(集合中的流)

Java8 中添加了一个新的接口类 Stream,相当于高级版的 Iterator,它可以通过 Lambda 表达式对集合进行大批量数据操作,或 者各种非常便利、高效的聚合数据操作。 

在 Java8 之前,我们通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,又或者通过重新定义 Collections.sorts 的 Comparator 方法来实现,这两种方式对于大数据量系统来说,效率并不是很理想。Stream 的聚合操作与数据库 SQL 的聚合操作 sorted、filter、map 等类似。我们在应用层就可以高效地实现类似数据库 SQL 的 聚合操作了,而在数据操作方面,Stream 不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据 的处理效率。
 


    水开始流的位置:开始流
    水流流过的位置:中间流
    水流到山脚下结束:结束流
注意:流都是一次性

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(100);
        list.add(25);
        list.add(63);
        list.add(5);
        list.add(105);
        list.add(98);
        list.add(69);
        list.add(25);
}
  •  filter 判断数据是否满足条件
     /**
         * 1.  获取stream流的方法 ------> 开始流的方法
         */
        Stream<Integer> stream = list.stream();

        /**
         * 2. 对流进行操作的方法 =========> 中间流的方法
         */

        /**
         * 2.1 filter 判断数据是否满足条件
         */

        stream.filter(integer -> integer>50 ).forEach(System.out::println);

  •  toArray() 将数据保存到数组中
        /**
         * 2.2 .toArray() 将数据保存到数组中
         */

        Object[] objects = stream.filter(integer -> integer > 50).toArray();
        System.out.println(Arrays.toString(objects)); //[100, 63, 105, 98, 69]

  • distinct 去重
     /**
         * distinct 数据去重
         */

        Object[] objects = stream.distinct().toArray();
        System.out.println(Arrays.toString(objects));

  •  sorted 排序,默认降序
        /**
         * sorted 排序  默认升序排列
         */
        Object[] objects = stream.sorted().toArray();
        System.out.println(Arrays.toString(objects));

 自己指定排序条件

        /**
         * 自定义顺序,自定义降序
         */
        Object[] objects = stream.sorted((o1, o2) -> o2 - o1).toArray();
        System.out.println(Arrays.toString(objects));
  • 中间流进行多次操作,排序 去重 指定条件的数据的显示
        /**
         * 指定条件的数据的显示
         * 去重后升序过滤出大于25的数据
         */
        Object[] objects = stream.distinct().sorted().filter(integer -> integer >25).toArray();
        System.out.println(Arrays.toString(objects)); //[63, 69, 98, 100, 105]

  •   数据 指定条数数据的展示
        /**
         * limit 数据指定条数的数据进行展示
         * 去重后升序过滤出前三条数据
         */
        Object[] objects = stream.distinct().sorted().limit(3).toArray();
        System.out.println(Arrays.toString(objects));

  •  max() 最大值、min() 最小值 
        /**
         * max() 最大值、min() 最小值
         */
//        Integer integer = stream.distinct().max((o1, o2) -> o1 - o2).get();
//        System.out.println(integer); //105
        Integer integer = stream.distinct().min((o1, o2) -> o1 - o2).get();
        System.out.println(integer); //5

 

  •  map 转成其他数据类型
        /**
         * map 转成其他数据类型
         */
        //将所有数据都拼接abc
        Object[] objects = stream.distinct().map(s -> s + "abc").toArray();
        System.out.println(Arrays.toString(objects)); //[100abc, 25abc, 63abc, 5abc, 105abc, 98abc, 69abc]

doubleValue(全部转换成double类型),longValue(全部转换成long类型),intValue(全部转换成int类型)

        /**
         * 只进行某一个类型转换
         * doubleValue(全部转换成double类型),longValue(全部转换成long类型),intValue(全部转换成int类型)
         */
//        double[] doubles = stream.mapToDouble(Integer::doubleValue).toArray();
//        System.out.println(Arrays.toString(doubles)); //[100.0, 25.0, 63.0, 5.0, 105.0, 98.0, 69.0, 25.0]
//        long[] longs = stream.mapToLong(Integer::longValue).toArray();
//        System.out.println(Arrays.toString(longs)); //[100, 25, 63, 5, 105, 98, 69, 25]
        int[] ints = stream.mapToInt(Integer::intValue).toArray();
        System.out.println(Arrays.toString(ints)); //[100, 25, 63, 5, 105, 98, 69, 25]
  • reduce 数据运算
            /**
             *  reduce 数据运算
             */
            //将数组中所有数据相加
            Integer integer = stream.reduce((integer1, integer2) -> integer1 + integer2).get();
            System.out.println(integer);

  • 对流中数据进行判断,是否所有的都满足条件  (allMatch)、对流中数据进行判断,是否任意一条数据的都满足条件(anyMatch)、 据进行判断,是否所有数据的都不满足条件  (noneMatch)

        /**
         * 对流中数据进行判断,是否所有的都满足条件(allMatch)、
         * 对流中数据进行判断,是否任意一条数据的都满足条件(anyMatch)、
         * 据进行判断,是否所有数据的都不满足条件(noneMatch)
         */
//        boolean b = stream.allMatch(integer -> integer > 26);
//        System.out.println(b); // false

//        boolean b = stream.anyMatch(integer -> integer > 26);
//        System.out.println(b); // true

        boolean b = stream.noneMatch(integer -> integer > 26);
        System.out.println(b); // false
  •  count()数据个数的统计
        /**
         * count()数据个数的统计
         */
        long count = stream.distinct().count();
        System.out.println(count);

3.2、stream流Collectors.toMap用法 

3.2.1、使用规则

toMap(Function, Function) 返回一个 Collector,它将元素累积到一个 Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项,则在执行收集操作时会抛出IllegalStateException。如果映射的键可能有重复项,请改用  toMap(Function, Function, BinaryOperator)。

3.2.2、代码演示

1、创建一个实体类

public class Person {
    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

2、收集list中的peson对象,将数据转换成 {id:person对象}的形式

    public static void main(String[] args) {
        List<Person> list = new ArrayList();
        list.add(new Person(1, "1"));
        //list.add(new Person(1, "4"));
        list.add(new Person(2, "2"));
        list.add(new Person(3, "3"));

        //收集list中的peson对象,将数据转换成 {id:person对象}的形式。Person::getId与 e->e.getId()一样
        Map<Integer, Person> collect = list.stream().collect(Collectors.toMap(Person::getId, i -> i));
        System.out.println(collect);

    }

 使用toMap()函数之后,返回的就是一个Map了,自然会需要key和value。

toMap()函数:参数1:就是用来生成key值的,参数2:用来生成value值的
参数3:用在key值冲突的情况下,如果新元素产生的key在Map中已经出现过了,第三个参数就会定义解决的办法。

 我们看到上面的list中person对象的id都是不同的,因此不需要第三个参数。

如果我们list中存储相同id的person对象

    public static void main(String[] args) {
        List<Person> list = new ArrayList();
        list.add(new Person(1, "1"));
        list.add(new Person(1, "4"));
        list.add(new Person(2, "2"));
        list.add(new Person(3, "3"));

        //收集list中的peson对象,将数据转换成 {id:person对象}的形式
        Map<Integer, Person> collect = list.stream().collect(Collectors.toMap(Person::getId, i -> i));
        System.out.println(collect);

    }

 运行报错,因为map集合key唯一

 解决办法,我们就需要使用第三个参数了,(a,b)->a :表示使用第一次出现的,(a,b)->b:表示使用最后一次出现的

    public static void main(String[] args) {
        List<Person> list = new ArrayList();
        list.add(new Person(1, "1"));
        list.add(new Person(1, "4"));
        list.add(new Person(2, "2"));
        list.add(new Person(3, "3"));

        //(a,b)->b 表示如果list中存在id相同的person对象,后面的person对象覆盖前面的map中已存在的。
        Map<Integer, Person> collect2 = list.stream().collect(Collectors.toMap(Person::getId, v -> v, (a,b)->b));
        System.out.println("collect2" + collect2);
        //(a,b)->b 表示如果list中存在id相同的person对象,后面的person对象不覆盖前面的map中已存在的,因此使用第一次出现的person对象。
        Map<Integer, Person> collect3 = list.stream().collect(Collectors.toMap(Person::getId, v -> v, (a,b)->a));
        System.out.println("collect3" + collect3);

    }

 3、Function.identity()

偶然之间发现的这个函数,感觉还是很有用的,尤其实在返回map的时候,value还为本身,用起来就很方便。
Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式,底层就是t->t

 实例代码

 public static void main(String[] args) {
        List<Person> list = new ArrayList();
        list.add(new Person(1, "1"));
        list.add(new Person(1, "4"));
        list.add(new Person(2, "2"));
        list.add(new Person(3, "3"));
        //第二个参数Function.identity(),底层就是t -> t,就是将当前对象返回
        Map<Integer, Person> collect3 = list.stream().collect(Collectors.toMap(e -> e.getId(), Function.identity(), (a, b) -> a));
        System.out.println("collect3"+collect3);

        //.values(),获取Map集合中所有的value值,放到collection集合中
        Collection<Person> values = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (a, b) -> a)).values();
        System.out.println(values);
    }

  

4. 可变长参数

        方法定义时,可以给方法进行参数个数的不固定个数的指定

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        System.out.println(list);
}

我们从上面的例子可以看出,可变长参数,可以传到多个参数,如果我们不清楚传递多个少个参数,我们可以使用可变长参数。
注意
1. 一个方法中只能有一个可变长参数,并且可变长参数必须在参数列表最后位置 ;
2. 可变长参数,传入到方法中,它是将所有参数都放到数组中,因此我们方法获取到的可变长参数是一个数组,数组里面存放的我们传入的数据;

/**
 * 可变长参数
 */
public class demo02 {
    /**
     * Arrays.asList
     */
    public static void main(String[] args) {
        me(1,2,3,4,5);
        me("a","b","c","d");
        me(new Student("zs",12), new Student("ls",36),  new Student("ww",43));

    }

    /**
     * 可变长参数
     * 格式:T... T(泛型)
     * @param arr
     * @param <T>
     */
    public static <T> void me(T... arr){
        System.out.println(Arrays.toString(arr));
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值