Java8常用的新特性详解

一、Java 8 新特性的简介

  • 速度更快
  • 代码更少(增加了新的语法:Lambda表达式)强大的Stream API
  • 便于并行
  • 最大化减少空指针异常:Optional
  • Nashorn引擎,允许在JVM上运行JS应用

二、Lambda表达式

Lambda表达式:特殊的匿名内部类,语法更简洁。

Lanbda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递。

基本语法:

	<函数式接口> <变量名> = (参数1,参数2...) ->{
				//方法体
		}

Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分

  • 左侧:(参数1,参数2…)表示参数列表。
  • 右侧:{}内部是方法体

Lambda需要注意的事项:

  • 形参列表的数据类型会自动推断
  • 如果形参列表为空,只需保留()
  • 如果形参只有1个,()可以省略,只需要参数的名称即可
  • 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有一句
  • Lambda不会生成一个单独的内部类文件

2.1 lambda代码说明

语法格式一

lambda 无参,无返回值

//如何使用lambda表达式
public class Demo1 {
    public static void main(String[] args) {
         //线程//匿名实现类对象---以前的写法
//        Runnable runnable=new Runnable() {
//            @Override
//            public void run() {
//                System.out.println("hello world!");
//            }
//        };
        //lambda表达式写法
        Runnable runnable1= ()->System.out.println("hello world!");
        new Thread(runnable1).start();
        //更简单的写法
        new Thread(()->System.out.println("hello world!")).start();
	}
}

语法格式二

lambda 有一个参,无返回值

@Test
    public void test02(){
 		Consumer<String> con = new Consumer<String>() {
 			@Override
 			public void accept(String s){
 				System.out.println(s);
 			}
 		};
 		con.accept("hello world!");
 		System.out.println("*********************");
        //Lambda表达式
        Consumer<String> con1 = (String str)-> System.out.println(str);
        con1.accept("hello world");
    }

语法格式三

数据类型可以省略,因为可由编译器推断得出,称为“类型推断”

@Test
    public void test03(){
		Consumer<String> con1 = (str) -> System.out.println(str);
        con1.accept("hello world");
    }

三、函数式接口

如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上。

@FunctionalInterface注解检测接口是否符合函数式接口

public class Demo1 {
    public static void main(String[] args) {
        USB usb = new USB() {
            @Override
            public void show() {
                System.out.println("我是函数式接口");
            }
        };
        USB usb1=()-> System.out.println("你好");

        info(()-> System.out.println("你好嘿嘿嘿"));
    }
    public static void info(USB usb){
        usb.show();
    }
}
//函数式接口
interface USB{
    public void show();
}

lambda新增了四个重要的函数式接口:

  • 函数形接口
  • 供给形接口
  • 消费型接口
  • 判断型接口

函数式接口说明

public class Demo2 {
    public static void main(String[] args) {
//        Consumer<Double> consumer=t-> System.out.println("吃饭消费掉:"+t);

//        Consumer<Double> consumer=new Consumer<Double>() {
//            @Override
//            public void accept(Double aDouble) {
//                System.out.println("你消费得金额:"+aDouble);
//            }
//        };
        Consumer<Double> consumer1=t ->System.out.println("吃饭消费掉:"+t);
        Consumer<Double> consumer2=t ->System.out.println("唱歌消费掉:"+t);
        Consumer<Double> consumer3=t ->System.out.println("洗脚消费掉:"+t);

        hobby(consumer1,1000);
        hobby(consumer2,2000);
        hobby(consumer3,4000);

        Supplier<Integer> supplier=new Supplier<Integer>() {
            @Override
            public Integer get() {
                return new Random().nextInt(10);
            }
        };
        Supplier<Integer> supplier1=()->new Random().nextInt(10);

        int[] arr = getArr(supplier1, 5);
        System.out.println(Arrays.toString(arr));

        System.out.println("~~~~~~~~~~~~~~~function~~~~~~~~~~~~~~~~~~~~~~~");
//        Function<String,String> function1=new Function<String, String>() {
//            @Override
//            public String apply(String s) {
//                return s.toUpperCase();
//            }
//        };
//        Function<String,String> function=s->s.toUpperCase();
//        System.out.println(toUpper(function, "hello"));
        List<String> list=new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");
        list.add("tianqi");
//        Predicate<String> predicate=new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.length()>5;
//            }
//        };
        Predicate<String> predicate1=s->s.length()>5;
        List<String> list1 = predicate(predicate1, list);
        System.out.println(list1);
    }

    //断言型接口   返回true/false经常用于判断
    public static List<String> predicate(Predicate<String> predicate,List<String> list){
           List<String> newList=new ArrayList<>();
           for(int i=0;i<list.size();i++){
                 if(predicate.test(list.get(i))){
                     newList.add(list.get(i));
                 }
           }
           return newList;
    }

    //函数型接口  有参数,且需要返回值
    public static String toUpper(Function<String,String> function,String str){
        return function.apply(str);
    }

    //消费性函数式接口   不需要返回值,有参数,经常用于迭代
    public static void hobby(Consumer<Double> consumer,double money){
           consumer.accept(money);
    }

    //供给型函数式接口   无参数,指定返回值类型,经常用于只注重过程的代码
    public static int[] getArr(Supplier<Integer> supplier,int count){
         int [] arr=new int[count];
         for(int i=0;i<count;i++){
              arr[i]=supplier.get();
         }
         return arr;
    }
}

四、方法引用

方法引用式Lambda表达式的一种简写形式。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。

常见的方法引用:

(1)构造器引用

**格式:**类名::new

@Test
    public void test04(){
	  //普通写法
      Supplier<Emp> supplier=new Supplier<Emp>() {
          @Override
          public Emp get() {
              return new Emp("刘德华");
          }
       };
      //类名::new  方法引用
      Supplier<Emp> supplier=Emp::new; //必须该类中存在无参构造函数。
      System.out.println(supplier.get());
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp{
    private String name;
}

(2)静态方法引用

**格式:**类名::静态方法名

 //类::静态方法  int compare(T o1, T o2);
   		//Lambda表达式方法引用
        Comparator<Integer> comparator=(o1,o2)->Integer.compare(o1,o2);
        //静态方法引用Demo
        Comparator<Integer> comparator=Integer::compare;
        int compare = comparator.compare(18, 18);
        System.out.println(compare);

(3)类的方法引用

**格式:**类名::实例方法名

	//lambda匿名方法引用
        Function<Emp,String> function=e->{
            return e.getName();
        };
        //类名::实例方法 R apply(T t);
        Function<Emp,String> function=Emp::getName;
        System.out.println(function.apply(new Emp("刘德华")));

(4)实例对象的方法引用

**格式:**对象::实例方法名

	//对象::实例方法
  		//这里System.out就是一个对象
        Consumer<String> consumer2=System.out::println;
        consumer2.accept("你是刘德华吗?");

五、Stream API

(Stream)中保存对集合或数组数据的操作。和集合类似,但集合中保存的是数据。

特点:

  • Stream自己不会存储元素。
  • Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

步骤:

创建

创建一个流。

中间操作

在一个或多个步骤中,将初始化Stream转化到另一个Stream的中间操作。

终止操作

使用一个终止操作来产生一个结果。该操作会强制它之前的延迟操作立即执行。在这之后,该Stream就不能使用了。

要注意的是,对流的操作完成后需要进行关闭操作(或者用JAVA7的try-with-resources)。

获取Stream对象的方式

  • 通过Collection对象的stream()或parallelStream()方法
  • 通过Arrays类的stream()方法
  • 通过Stream接口的of()、iterate()、generate()方法
  • 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法
public class Demo1 {
    public static void main(String[] args) {
        //1.通过Collection得对象中stream方法或parallelStream
        List<String> list=new ArrayList<>();
        list.add("apple");
        list.add("huawei");
        list.add("xiaomi");
        list.add("vivo");
        Stream<String> stream = list.stream();//串行流
        //遍历集合(T t)-{};  方法引用
        stream.forEach(e->{
            System.out.println(e);
        });
        //方法引用
        stream.forEach(System.out::println);
        Stream<String> stringStream = list.parallelStream(); //并行流

        //2.通过Arrays转化为流
        int[] arr={2,34,5,6,7,2};
        IntStream stream = Arrays.stream(arr);
        stream.forEach(System.out::println);

        //3.通过Stream中of,iterator,generator();
        Stream<String> list = Stream.of("java01", "java02", "java03");

        static <T> UnaryOperator<T> identity() {
                return t -> t;
            }
        UnaryOperator<Integer> unaryOperator=new UnaryOperator<Integer>() {
            @Override
            public Integer apply(Integer x) {
               return x+5;
           }
        };
        UnaryOperator<Integer> unaryOperator=x->x+5;
        Stream<Integer> list = Stream.iterate(0,unaryOperator);//0,5,10,15,20,25,30
        list.limit(10).forEach(System.out::println);
        Supplier<T> s
        Supplier<Integer> s=new Supplier() {
            @Override
           public Integer get() {
                return new Random().nextInt(50);
            }
       };
        Supplier<Integer> s=()->new Random().nextInt(50);
        Stream<Integer> stream = Stream.generate(s);
        Stream<Integer> stream = Stream.generate(()->new Random().nextInt(50));
        stream.limit(10).forEach(System.out::println);

          //IntStream LongStream  DoubleStream
        IntStream stream = IntStream.rangeClosed(0, 90);
        stream.forEach(System.out::println);
    }
}

5.1 中间操作

中间操作.

filter、limit、skip、distinct、sorted
map
parallel
  • filter:接收Lambda,从流中排除某些操作;
  • limit:截断流,使其元素不超过给定对象
  • skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
  • distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。

代码操作

public class test02 {

    public static void main(String[] args) {
        //personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",20,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("小梅",20,"中国",'F'));
        personList.add(new Person("何雪",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));
        //找出大于21岁的人
        personList.stream().filter((person) -> person.getAge()>21).forEach(System.out::println);
        System.out.println("----------------------");
        //查询中国人有几个
        long num = personList.stream().filter(p ->p.getCountry().equals("中国")).count();
        System.out.println("中国人有:"+num+"个");
        System.out.println("---------------------------");
        //从Person列表中取出两个女性。
        personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
        System.out.println("----------------------------");
        //从Person列表中从第2个女性开始,取出所有的女性。
        personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);
        System.out.println("-------------------------------");
        //去除掉了一个重复的数据
        personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);
        }

测试结果

在这个例子中,personList.stream()是创建流,filter()、limit()、skip()、distinct()属于中间操作,forEach、count()是终止操作。

5.2 Stream中间操作–映射

  • map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

map举例

例:比如,我们用一个PersonCountry类来接收所有的国家信息:

System.out.println("**************************");
        personList.stream().map((p) -> {
            Person personName = new Person();
            personName.setCountry(p.getCountry());
            return personName;
        }).distinct().forEach(System.out::println);

测试结果

5.3 Stream中间操作–排序

  • sorted()–自然排序(Comparable)
  • sorted(Comparator com)–定制排序(Comparator)

自然排序比较好理解,这里只讲一下定制排序,对前面的personList按年龄从小到大排序,年龄相同,则再按姓名排序:

final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {

    if (p1.getAge().equals(p2.getAge())) {
        return p1.getName().compareTo(p2.getName());
    } else {
        return p1.getAge().compareTo(p2.getAge());
    }
});
sorted.forEach(System.out::println);

运行结果

4.4.终止操作

forEach、min、max、count
reduce、collect
  • allMatch–检查是否匹配所有元素
  • anyMatch–检查是否至少匹配一个元素
  • noneMatch–检查是否没有匹配所有元素
  • findFirst–返回第一个元素
  • findAny–返回当前流中的任意元素
  • count–返回流中元素的总个数
  • max–返回流中最大值
  • min–返回流中最小值

allMatch

判断personList中的人是否都是成年人:

 final boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
 System.out.println("是否都是成年人:" + adult);

 final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
 System.out.println("是否都是中国人:" + chinaese);

运行结果

max min

判断最大、最小的人信息

 final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
 System.out.println("年龄最大的人信息:" + maxAge.get());

 final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
 System.out.println("年龄最小的人信息:" + minAge.get());

运行结果

求一个1到100的和

List<Integer> integerList = new ArrayList<>(100);
for(int i = 1;i <= 100;i++) {
	integerList.add(i);
}
final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
System.out.println("结果为:" + reduce);

运行结果

求所有人的年龄之和

final Optional<Integer> reduce =
personList.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println("年龄总和:" + reduce);

年龄总和:193

改写map举例中的的例子,将国家收集起来转换成List

final List<String> collect = personList.stream().map(p ->
p.getCountry()).distinct().collect(Collectors.toList());
System.out.println(collect);

输出结果:[中国, 美国, 英国]

计算出平均年龄

final Double collect1 =
 personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
 System.out.println("平均年龄为:" + collect1);

平均年龄为:21.444444444444443

注意流的关闭

 try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
   final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
   System.out.println(minAge.get());
}

到此这篇关于Java8常用的新特性详解的文章就介绍到这了,更多相关Java8常用的新特性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java8新特性之默认方法和静态方法

    前言 在Java 8之前,默认情况下,接口中的所有方法都是公共的和抽象的.但是这一限制在Java 8中被打破了,Java 8允许开发人员在接口中添加新方法,而无需在实现这些接口的类中进行任何更改. 为什么会有默认方法? 主要是为了方便扩展已有接口:如果没有默认方法,假如给Java中的某个接口添加一个新的抽象方法,那么所有实现了该接口的类都得修改,影响将非常大. 举个例子,Sortable <T>接口以及实现该接口的类SortableNumberCollection和SortableString

  • java8新特性之stream的collect实战教程

    1.list转换成list 不带return方式 List<Long> ids=wrongTmpList.stream().map(c->c.getId()).collect(Collectors.toList()); 带return方式 // spu集合转化成spubo集合//java8的新特性 List<SpuBo> spuBos=spuList.stream().map(spu -> { SpuBo spuBo = new SpuBo(); BeanUtils.c

  • java8新特性之接口默认方法示例详解

    前言 JAVA8 已经发布很久,而且毫无疑问,java8 是自 java5(2004年发布)之后的最重要的版本.其中包括语言.编译器.库.工具和 JVM 等诸多方面的新特性.Java8 新特性列表如下: 接口默认方法 函数式接口 Lambda 表达式 方法引用 Stream Optional 类 Date API Base64 重复注解与类型注解 接口默认方法 1.什么是接口默认方法 从 Java8 开始,程序允许在接口中包含带有具体实现的方法,使用 default 修饰,这类方法就是默认方法.

  • Java8新特性之Stream API详解

    一.前言 StreamAPI在Java8版本中使用,关注的是对数据的筛选.查找.存储等 它可以做的事情有:过滤.排序.映射.归约 二.使用流程 Stream实例化中间操作(过滤.排序.映射.规约)终止操作(匹配查找.归约.收集) 三.案例演示 public class EmployeeData { public static List<Employee> getEmployees(){ List<Employee> list = new ArrayList<>(); l

  • Java8新特性之线程安全日期类

    LocalDateTime Java8新特性之一,新增日期类. 在项目开发过程中经常遇到时间处理,但是你真的用对了吗,理解阿里巴巴开发手册中禁用static修饰SimpleDateFormat吗 通过阅读本篇文章你将了解到: 为什么需要LocalDate.LocalTime.LocalDateTime[java8新提供的类] Java8新的时间API的使用方式,包括创建.格式化.解析.计算.修改 可以使用Instant代替 Date,LocalDateTime代替 Calendar,DateTi

  • Java8新特性之Lambda表达式的使用

    1. lambda表达式介绍 lambda表达式是Java8提供的新特性之一,也可以称之为闭包:它支持Java能够进行简单的函数式编程,也就是说可以把一个匿名函数作为一个方法的参数进行传递:其格式分为三部分,第一部分为入参列表,第二部由->固定组成,第三部分为方法体: public class LambdaTest { public static void main(String[] args) { // 使用lambda表达式创建线程 Thread thread = new Thread(()

  • Java8 新特性之日期时间对象及一些其他特性

    日期时间对象 关于日期时间的操作可以分为两种: 转换:与字符串的互相转换,与时间戳的互相转换 计算:计算两个时间点之间的间隔.时间点与时间段的计算(计算下周N.下个月D日.去年M月D日等等) Java8 提供了三个类:LocalDate.LocalTime.LocalDateTime,它们的形式如 2020-01-01.12:30:00.2020-01-01 12:30:00 创建对象 获取类对象的方法非常非常简单 LocalDate now = LocalDate.now(); LocalDa

  • java8新特性 获取list某一列的操作

    提取某一列(以name为例) //从对象列表中提取一列(以name为例) List<String> nameList = studentList.stream().map(StudentInfo::getName).collect(Collectors.toList()); //提取后输出name nameList.forEach(s-> System.out.println(s)); 补充:java8新特性:lambda表达式:直接获得某个list/array/对象里面的字段集合 ja

  • Java8新特性之方法引用的实践指南

    一 前言 日常开发中,经常使用到Lambda表达式,例如: public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 5, 10, 4, 2); // 打印列表中的每一个数字 list.forEach((x) -> System.out.println(x)); } 其中(x) -> System.out.println(x)就是使用的Lambda表达式.Lambda表达式可以分为三

  • java8新特性之日期时间API

    jdk8之前 一.java.lang.System long times = System.currentTimeMillis(); //返回的是当前时间与1970年1月1月1日0分0秒之间以毫秒为单位的时间差 //称为时间戳 System.out.println(times); 二.java.util.Date And java.sql.Date 将java.util.Date 对象转换为java.sql.Date对象: //将java.util.Date 对象转换为java.sql.Date

  • java8新特性 stream流的方式遍历集合和数组操作

    前言: 在没有接触java8的时候,我们遍历一个集合都是用循环的方式,从第一条数据遍历到最后一条数据,现在思考一个问题,为什么要使用循环,因为要进行遍历,但是遍历不是唯一的方式,遍历是指每一个元素逐一进行处理(目的),而并不是从第一个到最后一个顺次处理的循环,前者是目的,后者是方式. 所以为了让遍历的方式更加优雅,出现了流(stream)! 1.流的目的在于强掉做什么 假设一个案例:将集合A根据条件1过滤为子集B,然后根据条件2过滤为子集C 在没有引入流之前我们的做法可能为: public cl

随机推荐