java8 新特性

16

1.java8概述

Java8 (又称JKD1.8) 是Java 语言开发的一个主要版本。

Oracle公司于2014年3月18日发布Java8 。

2.Lambda表达式

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

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

基本语法:

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

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

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

注意事项:

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

测试代码1

public class Demo1 {
    public static void main(String[] args) {
        //创建Runnable对象
        new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("asd");
            }
        }).start();
        //优化后
        new Thread(() -> System.out.println("asd")).start();
        
        //创建Comparator
        new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        //优化后
        Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
    }
}

测试代码2

class Employee{
    private int age;
    private int money;

    public Employee() {
    }

    public Employee(int age, int money) {
        this.age = age;
        this.money = money;
    }

    //setter , getter

    @Override
    public String toString() {
    }
}
interface MyInterface<T> {
    boolean test(T t);
}
public class Demo2 {
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee(20, 200));
        employees.add(new Employee(25, 400));
        employees.add(new Employee(30, 600));
       //内涵方法的回调
        List<Employee> filter = filter(employees, e -> e.getMoney() > 300);
        for (Employee o : filter) {
            System.out.println(o.toString());
        }
    }

    public static List<Employee> filter(List<Employee> list, MyInterface<Employee> e) {
        ArrayList<Employee> employees = new ArrayList<>();
        for (Employee employee : list) {
            if (e.test(employee)) {
                employees.add(employee);
            }
        }
        return employees;
    }
}

[wppay]

3.函数式接口

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

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

常见函数式接口

//演示四个函数式接口
public class FunctionalDemo {
    public static void main(String[] args) {
        //1.
        happy(2000, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println(aDouble);
            }
        });
        happy(1000, money -> System.out.println(money));

        //2.
        int[] nums = getNums(3, () -> new Random().nextInt(900) + 100);
        for (int num : nums) {
            System.out.print(num + " ");
        }

        //3.
        String hello = handler("hello", s -> s.toUpperCase());
        System.out.println(hello);

        //4.
        List<String> list = new ArrayList<>();
        list.add("liuzhiu");
        list.add("asdliu");
        list.add("liuasd");
        /* List<String> strings = filter2(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                if (s.startsWith("liu")) {
                    return true;
                }
                return false;
            }
        });*/
        //优化后
        List<String> strings = filter2(list, s -> s.startsWith("liu"));
        for (String string : strings) {
            System.out.print(string + " ");
        }
    }

    //Consumer 消费型接口 有参无返回值
    public static void happy(double money, Consumer<Double> con) {
        con.accept(money);
    }

    //Supplier : 供给型 无参有返回值
    public static int[] getNums(int count, Supplier<Integer> sup) {
        int[] ints = new int[count];
        for (int i = 0; i < count; i++) {
            ints[i] = sup.get();
        }
        return ints;
    }

    //Function : 函数型接口 有参有返回值
    public static String handler(String str, Function<String, String> f) {
        return f.apply(str);
    }

    //Predicate : 断言型 判断
    public static List<String> filter2(List<String> list, Predicate<String> p) {
        ArrayList<String> list1 = new ArrayList<String>();
        for (String s : list) {
            if (p.test(s)) {
                list1.add(s);
            }
        }
        return list1;
    }
}

4.方法引用

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

常见形式

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法
  • 类::new
  • 元素类型[]::new
public class MethodRef {
    public static void main(String[] args) {
        //了解
        //方法引用是Lambda表达式的一种简写形式。
        //如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
        ///第一种形式,对象::实例方法
        //(1)Lambda表达式方法体中只是调用一个特定的已经存在的方法。
        //(2)方法的参数和返回值和接口中的方法一致。
        Consumer<String> consumer = s -> System.out.println(s);
        Consumer<String> consumer2 = System.out::println;
        consumer2.accept("hello");
        consumer2.accept("你好");

        //第二种形式,类::静态方法
        //(1).Lambda表达式方法体中只是调用一个特定的已经存在的方法。
        //(2)方法的参数和返回值和接口中的方法一致。
        Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
        Comparator<Integer> comparator2 = Integer::compare;

        //第三种:类::实例方法
        //(1).Lambda表达式方法体中只是调用一个特定的已经存在的方法。
        //(2) 有一个参数作为方法调用者,其他的和接口的方法一致
        Comparator<String> comparator3 = (o1, o2) -> o1.compareTo(o2);
        Comparator<String> comparator4 = String::compareTo;

        //第四章形式:类::new
        //调用的无参构造方法创建对象
        //构造方法参数和接口类型一致
        Supplier<Employee> sup = () -> new Employee();
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());

        //第五种形式:元素类型[]::new
        //创建一个数组
        //方法的参数作为数组的个数
        Function<Integer, String[]> function = s -> new String[s];
        Function<Integer, String[]> function2 = String[]::new;
        String[] apply = function2.apply(5);
    }
}

5.* StreamAPI

什么是Stream

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

Stream特点

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

Stream使用步骤

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

创建Stream

  • 通过Collection集合的stream()或parallelStream()方法。
  • 通过Arrays类的stream()方法。
  • 通过Stream接口的of()、iterate()、generate()方法。
  • 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法。
//1.创建一个流(四种方法)
public class StreamDemo1 {
    public static void main(String[] args) {
        //(1).* 对集合:通过Collection集合的stream()或parallelStream()方法
        ArrayList<String> list = new ArrayList<>();
        list.add("aad");
        list.add("ba");
        list.add("casddd");
        list.add("dddd");
        Stream<String> stream = list.stream();
        stream.filter(s -> s.length() >= 5).forEach(s -> System.out.println(s));
        //(2)* 对数组:Arrays.stream
        IntStream intStream = Arrays.stream(new int[] {12, 23, 34, 12, 23});
        intStream.forEach(n -> System.out.print(n + " "));
        //(3)Stream的方法 of,iterate()迭代流,generate() 生成流
        Stream<String> stringStream = Stream.of(new String[] {"a", "b", "c"});
        Stream.iterate(0, n -> n + 2).limit(5).forEach(System.out::println);//以0为基础,每次加2
        System.out.println("---------");
        Stream.generate(() -> new Random().nextInt(100))
            .limit(5)
            .forEach(System.out::println);
        //(4)使用IntStream,LongStream,DoubleStream
        //of range() 左闭右开    rangeClose() 左闭右闭
        IntStream.of(100, 200, 300).forEach(System.out::println);

        //生产0-99的数字
        IntStream.range(0, 100).forEach(System.out::println);
        IntStream.rangeClosed(0, 100).forEach(System.out::println);
    }
}

中间操作

  • 中间操作:
    • filter、limit、skip、distinct、sorted
    • map
    • parallel
//添加中间操作  filter limit skip distinct sorted
class StreamDemo2 {
    public static void main(String[] args) {
        ArrayList<Employee> e = new ArrayList<>();
        e.add(new Employee("20", 800));
        e.add(new Employee("25", 300));
        e.add(new Employee("30", 600));
        e.add(new Employee("30", 600));
        e.stream().filter(s -> s.getMonth() >= 300).forEach(System.out::println);
        System.out.println("---------------");
        e.stream().limit(2).forEach(System.out::println);
        System.out.println("---------------");
        e.stream().skip(2).limit(2).forEach(System.out::println);
        System.out.println("---------------");
        //distinct  需要重写hashCode()和equals()
        e.stream().distinct().forEach(System.out::println);
        System.out.println("---------------");
        //sorted
        e.stream().sorted(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getMonth() - o2.getMonth();
            }
        }).forEach(System.out::println);
        System.out.println("---------------");
        //map映射 没有产生集合  案例:打印所有员工的姓名
        e.stream().map(list -> list.getName()).forEach(System.out::println);
        System.out.println("---------------");
        //parallel 并行流(多线程)
        e.stream().parallel().forEach(System.out::println);
        //e.parallelStream().forEach(System.out::println);

    }
}

验证并行流 串行流 , UUID 随机生成ID

public class StreamDemo3 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        for (int i = 0; i < 999999; i++) {
            list.add(UUID.randomUUID().toString());
        }
        // 2100  2900  1831
        long start=System.currentTimeMillis();
        //串行流 (单线程)2100  2900  1831
        //并行流(多线程) 1618 2350 1313
        long count=list.parallelStream()
                .sorted()
                .count();
        System.out.println("元素个数:"+count);
        long end=System.currentTimeMillis();
        System.out.println("用时:"+(end-start));
    }
}

终止操作

  • 终止操作:
    • forEach、min、max、count
    • reduce、collect
//终止操作
//forEach 遍历 min 最小值 max 最大值 count 元素个数
//reduce 归约:统计  collect 收集
class StreamDemo4 {
    public static void main(String[] args) {
        ArrayList<Employee> e = new ArrayList<>();
        e.add(new Employee("20", 800));
        e.add(new Employee("25", 300));
        e.add(new Employee("30", 600));
        e.add(new Employee("30", 600));
        //查找月份最低的
        //Optional 防止空指针异常,返回的数据封装Optional对象
        Optional<Employee> min = e.stream().min(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return Integer.compare(o1.getMonth(), o2.getMonth());
            }
        });
        System.out.println(min.get().getMonth());
        //count 元素个数
        System.out.println(e.stream().count());
        //reduce 归约:统计
        //统计所有人的工资的和   二元运算符
        Optional<Integer> sum = e.stream()
            .map(s -> s.getMonth())
            .reduce((s1, s2) -> s1 + s2);
        System.out.println(sum.get());
        System.out.println("--------------");
        //collect : 收集
        //获取所有人的姓名
        List<String> collect = e.stream()
            .map(s -> s.getName())
            .collect(Collectors.toList());
        for (String s : collect) {
            System.out.println(s);
        }

        //延迟操作,添加所有的中间操作都不会立即执行,直到有终止操作才会执行

    }
}

6.新的时间API

  • 之前时间API存在问题:线程安全问题、设计混乱。
  • 本地化日期时间API:
    • LocalDate
    • LocalTime
    • LocalDateTime:类似于Calendar
  • Instant:时间戳,类似于Date。
  • ZoneId:时区。
  • Date、Instant、LocalDateTime的转换。
  • DateTimeFormatter:格式化类。

LocalDateTime

//LocalDateTime 日期不可修改 相当于Calendar
public class Demo1 {
    public static void main(String[] args) {
        //当前时间
        LocalDateTime localDate = LocalDateTime.now();
        System.out.println(localDate);

        //昨天
        LocalDateTime localDate1 = localDate.minusDays(1);
        LocalDateTime localDate2 = localDate.plusDays(-1);

        //明天
        LocalDateTime localDate3 = localDate.plusDays(1);

        System.out.println("-----------------");
        //指定日期和时间的对象
        LocalDateTime of = LocalDateTime.of(1997, 7, 1, 1, 1, 1);
        System.out.println(of);

        System.out.println("-----------------");
        //获取日期和时间信息
        localDate.getYear();
        localDate.getMonthValue();
        localDate.getDayOfMonth();
        localDate.getHour();
        localDate.getMinute();
        System.out.println("-------------------");


        //设置日期和时间(不可变)
        LocalDateTime localDate4 = localDate.withMonth(2);
        System.out.println(localDate);
        System.out.println(localDate4);
        System.out.println("--------------------");

        Date date = new Date();
        System.out.println(date);
        date.setTime(System.currentTimeMillis() - 60 * 60 * 24 * 1000);
        System.out.println(date);
    }
}

Instant

//Instant 时间戳(1970-现在) 相当于Date
//毫秒值没有问题,时区显示不同
class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Instant now = Instant.now();
        System.out.println(now);
        System.out.println("秒" + now.getEpochSecond());
        System.out.println("毫秒" + now.toEpochMilli());
        //昨天
        System.out.println(now.minusMillis(60 * 60 * 24 * 1000));
        //明天
        System.out.println(now.plusMillis(60 * 60 * 24 * 1000));

        //可以用来计时
        Instant start = Instant.now();
        Thread.sleep(3000);
        Instant end = Instant.now();
        System.out.println(end.toEpochMilli() - start.toEpochMilli());
    }
}

ZoneId

class Demo3 {
    public static void main(String[] args) {
        //ZoneId 时区类
        Set<String> zoneIds = ZoneId.getAvailableZoneIds();
        for (String zoneId : zoneIds) {
            System.out.println(zoneId);
        }
        //获取默认时区
        System.out.println(ZoneId.systemDefault());
    }
}

Date、Instant、LocalDateTime的转换

class Demo4 {
    public static void main(String[] args) {
        //演示转换
        //(1) LocalDateTime -> Instant -> Date
        LocalDateTime now = LocalDateTime.now();
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();

        Date from = Date.from(instant);

        //(2) Date -> Instant -> LocalDateTime
        Instant instant1 = from.toInstant();
        LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}

DateTimeFormatter

class Demo5{
    public static void main(String[] args) {
        //时间格式化类 DateTimeFormatter
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //日期 -> 字符串
        LocalDateTime now = LocalDateTime.now();
        System.out.println(formatter.format(now));

        //字符串 ——> 日期
        LocalDateTime parse = LocalDateTime.parse("1882-11-10 21:12:12", formatter);
        System.out.println(parse);
    }
}

[/wppay]