欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 Java语言基础之JDK1.8新特性(Lambda表达式、函数式接口、Stream流、新的日期API)

程序员文章站 2022-09-14 09:40:36
Java8概述是Java语言开发的一个主要版本。Oracle公司于2014年3月18日发布Java8版本。支持Lambda表达式函数式接口新的Stream API新的日期 API其他特性Lambda表达式特殊的内部类,语法更加简洁。lambda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递。基本语法<函数式接口> <变量名> = (参数1, 参数2...)->{ //方法体};Lambda表达式引入了新的...

Java8概述

是Java语言开发的一个主要版本。Oracle公司于2014年3月18日发布Java8版本。

  • 支持Lambda表达式
  • 函数式接口
  • 新的Stream API
  • 新的日期 API
  • 其他特性

Lambda表达式

特殊的内部类,语法更加简洁。

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

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

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

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

注意事项

  1. 形参列表的数据类型会自动判断
  2. 如果形参列表为空,只需保留()
  3. 如果形参只有1个,()可以省略,只需要参数的名称即可
  4. 如果方法体中只有一句代码,或者无返回值,{}可以省略,若有返回值,则若想去掉{},则必须同时省略return,且执行语句也保证只有一句
  5. Lambda表达式不会生成单独的内部类文件

代码演示1

package cn.itcast.demo;
public class LambdaDemo {
    public static void main(String[] args) {
        //匿名内部类
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程1执行了........");
            }
        };
        new Thread(runnable).start();

        //使用lambda表达式,如果方法体中只有一行代码的话,可以将大括号省略掉
        Runnable runnable2 = ()->System.out.println("子线程2执行了.......");
        new Thread(runnable2).start();

        //最简形式
        new Thread(()->System.out.println("子线程3执行了.......")).start();
    }
}

代码演示2

package cn.itcast.demo;
import java.util.Comparator;
import java.util.TreeSet;
public class LambdaDemo2 {
    public static void main(String[] args) {
        //匿名内部类
        Comparator<String> com = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        };
        TreeSet<String> ts = new TreeSet<String>(com);

        //Lambda表达式
        Comparator<String> com2 = (String o1, String o2)->{
            return o1.length()-o2.length();
        };
        TreeSet<String> ts2 = new TreeSet<String>(com2);

        Comparator<String> com3 = (o1, o2) -> o1.length()-o2.length();
        TreeSet<String> ts3 = new TreeSet<String>(com3);
    }
}

函数式接口

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

那么如何判断一个接口是否是函数式接口呢?
可以使用这个注解

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

代码演示

package cn.itcast.demo;
//函数式接口
@FunctionalInterface
public interface Usb {
    void service();
}

LambdaDemo3

package cn.itcast.demo;
public class LambdaDemo3 {
    public static void main(String[] args) {
        //匿名内部类
        Usb mouse = new Usb() {
            @Override
            public void service() {
                System.out.println("鼠标开始工作了........");
            }
        };
        run(mouse);

        //Lambda表达式
        Usb fan = ()->System.out.println("风扇开始工作了.......");
        run(fan);
    }
    public static void run(Usb usb){
        usb.service();
    }
}

常见的函数式接口

函数式接口 参数类型 返回类型 说明
Consumer T void void accept(T t); 对类型T的对象应用操作
Suppiler T T get(); 返回类型为T的对象
Function<T,R> T R R apply(T t); 对类型为T的对象应用操作,并返回类型为R类型的对象
Predicate T boolean boolean test(T t); 确定类型为T的对象是否满足条件,并返回boolean类型

除了表格中的这几个函数式接口,Runnable、Comparator等也属于函数式接口。

package cn.itcast.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class LambdaDemo4 {
    public static void main(String[] args) {
        //匿名内部类
        Consumer<Double> consumer = new Consumer<Double>() {
            @Override
            public void accept(Double t) {
                System.out.println("聚餐消费"+t);
            }
        };
        happy(consumer, 1000);

        //Lambda表达式
        Consumer<Double> consumer2 = t->System.out.println("聚餐消费"+t);
        happy(consumer2, 1200);
        happy(t->System.out.println("唱歌消费"+t),2000);
        System.out.println("--------------------------");

        int[] arr = getNums(() -> new Random().nextInt(100), 5);
        System.out.println(Arrays.toString(arr));
        System.out.println("--------------------------");

        String str = handlerString(s -> s.toUpperCase(), "hello");
        System.out.println(str);
        System.out.println("--------------------------");

        List<String> names = new ArrayList<String>();
        names.add("zhangsan");
        names.add("zhangxiaohua");
        names.add("lisi");
        names.add("wangwu");
        List<String> result = filterNames(s -> s.startsWith("zhang"), names);
        System.out.println(result.toString());

        List<String> result2 = filterNames(s -> s.length()>5, names);
        System.out.println(result2.toString());
    }
    //Consumer 消费型接口
    public static void happy(Consumer<Double> consumer, double money){
        consumer.accept(money);
    }
    //Supplier 供给型接口
    public static int[] getNums(Supplier<Integer> supplier, int count){
        int[] arr = new int[count];
        for(int i = 0; i < count; i++ ){
            arr[i] = supplier.get();
        }
        return arr;
    }
    //Function 函数型接口
    public static String handlerString(Function<String, String> function, String str){
        return function.apply(str);
    }
    //Predicate 断言型接口
    public static List<String> filterNames(Predicate<String> predicate, List<String> list){
        List<String> resultList = new ArrayList<String>();
        for(String str : list){
            if(predicate.test(str)){
                resultList.add(str);
            }
        }
        return resultList;
    }
}

运行结果
荐
                                                        Java语言基础之JDK1.8新特性(Lambda表达式、函数式接口、Stream流、新的日期API)

方法引用

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

常见形式:

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法
  • 类::new

代码演示

package cn.itcast.demo;
import java.util.Comparator;
import java.util.function.Consumer;
//方法引用
public class LambdaDemo5 {
    public static void main(String[] args) {
        //对象::实例方法
        Consumer<String> consumer = s->System.out.println(s);
        consumer.accept("hello");
        //使用方法引用
        Consumer<String> consumer2 = System.out::println;
        consumer2.accept("world");

        //类::静态方法
        Comparator<Integer> com = (o1, o2)->Integer.compare(o1, o2);
        Comparator<Integer> com2 = Integer::compare;

        //.....
    }
}

流(Stream)

流中保存对集合或数组数据的操作。和集合类似,但是集合中保存的是数据,而流中保存的是操作。
荐
                                                        Java语言基础之JDK1.8新特性(Lambda表达式、函数式接口、Stream流、新的日期API)
Stream的特点

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

Stream的使用步骤

  1. 创建:新建一个流
  2. 中间操作:在一个或多个步骤中,将初始Stream转换到另一个Stream的中间操作。
  3. 终止操作:使用一个终止操作来产生一个结果,该操作会强制它之前的延迟操作立即执行,在这之后,该Stream就不能使用了。

创建Stream

  • 通过Collection对象的stream()方法或parallelStream()方法
  • 通过Arrays的stram()方法
  • 通过Stream接口的of()、iterator()、generate()方法

代码演示

package cn.itcast.demo2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.Stream;
public class StreamDemo {
    public static void main(String[] args) {

        //通过Collection对象的stream()方法或parallelStream()方法
        ArrayList<String> al = new ArrayList<String>();
        al.add("apple");
        al.add("huawei");
        al.add("xiaomi");
        Stream<String> stream = al.stream();
        //遍历
        stream.forEach(s->System.out.println(s));
        System.out.println("----------------------");

        //通过Arrays的stram()方法
        String[] arr = {"aaa", "bbb", "ccc"};
        Stream<String> stream2 = Arrays.stream(arr);
        stream2.forEach(s->System.out.println(s));
        System.out.println("----------------------");

        //通过Stream接口的of()、iterator()、generate()方法
        Stream<Integer> stream3 = Stream.of(10, 20, 30, 40);
        stream3.forEach(System.out::println);
        System.out.println("-----------迭代流-----------");
        Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
        iterate.limit(10).forEach(System.out::println);
        System.out.println("-----------生成流-----------");
        Stream<Integer> generate = Stream.generate(() -> new Random().nextInt(100));
        generate.limit(10).forEach(System.out::println);
    }
}

中间操作

  • filter、limit、skip、distinct、sorted
  • map
  • parallel

代码演示

package cn.itcast.demo2;
import java.util.ArrayList;
public class StreamDemo2 {
    public static void main(String[] args) {
        ArrayList<Student> al = new ArrayList<Student>();
        al.add(new Student("zhangsan",8000));
        al.add(new Student("lisi",12000));
        al.add(new Student("wangwu",25000));
        al.add(new Student("zhouqi",5000));
        al.add(new Student("zhouqi",5000));
        //中间操作1
        //filter 过滤
        System.out.println("----------filter---------");
        al.stream().filter(stu->stu.getSalary()>10000).forEach(System.out::println);
        //limit 限制
        System.out.println("----------limit---------");
        al.stream().limit(2).forEach(System.out::println);
        //skip 跳过
        System.out.println("----------skip---------");
        al.stream().skip(2).forEach(System.out::println);
        //distinct 去重复
        System.out.println("----------distinct---------");
        al.stream().distinct().forEach(System.out::println);

        //sorted 排序
        al.stream().sorted((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()))
                .forEach(System.out::println);

        //中间操作2 map
        System.out.println("----------map---------");
        al.stream().map(stu->stu.getName()).forEach(System.out::println);

        //中间操作3 parallel 采用多线程效率
        System.out.println("----------parallel---------");
        al.parallelStream().forEach(System.out::println);
    }
}

串行流和并行流的区别

package cn.itcast.demo2;
import java.util.ArrayList;
import java.util.UUID;
public class StreamTest {
    public static void main(String[] args) {

        //串行流和并行流的区别
        ArrayList<String> list = new ArrayList<String>();
        for(int i = 0; i < 5000000; i++){
            list.add(UUID.randomUUID().toString());
        }
        //并行流 10s 并行
        long start = System.currentTimeMillis();
        long count = list.parallelStream().sorted().count();
        System.out.println(count);
        long end = System.currentTimeMillis();
        System.out.println("用时:"+(start-end));
    }
}

终止操作

  • forEach、min、max、count
  • reduce、collect

代码演示

package cn.itcast.demo2;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamDemo3 {
    public static void main(String[] args) {
        ArrayList<Student> al = new ArrayList<Student>();
        al.add(new Student("zhangsan",8000));
        al.add(new Student("lisi",12000));
        al.add(new Student("wangwu",25000));
        al.add(new Student("zhouqi",5000));
        al.add(new Student("zhouqi",5000));

        //终止操作 forEach
        al.stream().filter(e->{
            System.out.println("过滤了......");
            return e.getSalary()>10000;
        }).forEach(System.out::println);
        //min max count
        System.out.println("---------min---------");
        Optional<Student> min = al.stream()
                .min((stu1, stu2) -> Double.compare(stu1.getSalary(), stu2.getSalary()));
        System.out.println(min.get());

        System.out.println("---------max---------");
        Optional<Student> max = al.stream()
                .min((stu1, stu2) -> Double.compare(stu1.getSalary(), stu2.getSalary()));
        System.out.println(max.get());

        System.out.println("---------count---------");
        long count = al.stream().count();
        System.out.println("学生人数:"+count);

        //终止操作 reduce 规约
        //计算所有工资
        System.out.println("---------reduce---------");
        Optional<Double> sum = al.stream().map(stu -> stu.getSalary()).reduce((x, y) -> x + y);
        System.out.println(sum.get());

        //终止操作 collect 收集
        //获取所有学生姓名,封装一个list集合
        System.out.println("---------collect---------");
        List<String> names = al.stream().map(stu -> stu.getName()).collect(Collectors.toList());
        for(String name : names){
            System.out.println(name);
        }
    }
}

新的日期 API

之前的日期API存在问题:线程安全问题、设计混乱

演示线程安全问题

package cn.itcast.demo3;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
//演示线程安全问题
public class DateDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Callable<Date> callable = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("20200425");
            }
        };
        List<Future<Date>> list = new ArrayList<>();
        for(int i = 0; i < 10; i++){
            Future<Date> future = pool.submit(callable);
            list.add(future);
        }
        for(Future<Date> future : list){
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}

本地化时间 API

  • LocalDate
  • LocatTime
  • LocalDateTime
  • Instant:时间戳
  • ZoneId:时区

代码演示

package cn.itcast.demo3;
import java.time.LocalDateTime;
public class LocalDateTimeDemo {
    public static void main(String[] args) {

         //创建本地时间
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        System.out.println(ldt.getYear());
        System.out.println(ldt.getMonthValue());
        System.out.println(ldt.getDayOfMonth());

        //添加两天
        LocalDateTime newDate = ldt.plusDays(2);
        System.out.println(newDate);

        //减少一个月
        LocalDateTime newDate2 = ldt.minusMinutes(1);
        System.out.println(newDate2);
    }
}

代码演示2

package cn.itcast.demo3;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Set;
public class InstantDemo {
    public static void main(String[] args) {

         //创建
        Instant instant = Instant.now();
        System.out.println(instant.toString());

        System.out.println(instant.toEpochMilli());
        System.out.println(System.currentTimeMillis());

        //添加、减少时间
        Instant instant2 = instant.plusSeconds(10);
        System.out.println(Duration.between(instant, instant2).toMillis());

        //3.ZoneId 时区
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        for(String str : availableZoneIds){
            System.out.println(str);
        }
    }
}

Date、Instant、LocalDateTime的转换

package cn.itcast.demo3;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class TranferDemo {
    public static void main(String[] args) {

        //Date--->Instant--->LocalDateTime
        Date date = new Date();
        Instant instant = date.toInstant();
        System.out.println(instant);
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        System.out.println(localDateTime);
        System.out.println("----------------------------");

        //LocalDateTime--->Instant--->Date
        Instant instant2 = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        System.out.println(instant2);
        Date date2 = Date.from(instant2);
        System.out.println(date2);
    }
}

DateTimeFormatter:格式化类

package cn.itcast.demo3;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DataTimeFormatterDemo {
    public static void main(String[] args) {

        //创建DateTimeFormatter对象
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        //格式化时间字符串
        String format = dtf.format(LocalDateTime.now());
        System.out.println(format);
        //将字符串解析成时间
        LocalDateTime localDateTime = LocalDateTime.parse("2020-03-01 10:20:35", dtf);
        System.out.println(localDateTime);
    }
}

本文地址:https://blog.csdn.net/weixin_45620489/article/details/107287417