java8新特性

    xiaoxiao2024-11-08  81

    一.lambda表达式

    1.为什么使用lambda表达式

    lambda是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁,更灵活的代码。作为一种更紧凑的代码风格,是java语言的表达能力得到了提升。

    2.lambda 基础语法

    java8中引入了一个新的操作符 “->” 该操作符成为箭头操作符或lambda操作符

    左侧:lambda 表达式的参数列表右侧:lambda 表达式所需执行的功能,即lambda体lambda表达式需要函数式接口的支持(见下图)
    语法格式一:无参数,无返回值
    @Test public void test1(){ // 匿名内部类方式 Runnable r1 = new Runnable() { @Override public void run() { System.out.println("hello world"); } }; r1.run(); System.out.println("-------------------"); // lambda方式 Runnable r2 = () -> System.out.println("hello world"); r2.run(); }
    语法格式二:有一个参数,并且无返回值
    @Test public void test2(){ Consumer<String> con = (x) -> System.out.println(x); con.accept("miracle"); }
    语法格式三:有二个以上的参数,有回值,并且lambda 体中有多条语句,语句要使用{}
    @Test public void test3(){ Comparator<Integer> comparator = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); }; }
    语法格式四:有二个以上的参数,有回值,并且lambda 体中有一条语句,语句可以省略{}和return
    @Test public void test4(){ Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); }

    3.方法引用

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

    主要有三种语法格式:
    对象::实例方法名类::静态方法名类::实例方法名
    注意 lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
    对象::实例方法名 示例 @Test public void test1(){ // System.out 返回打印流 PrintStream 对象 Consumer<String> consumer = System.out::println; consumer.accept("miracle"); } 类::静态方法名 @Test public void test3(){ // Integer类的静态方法compare Comparator<Integer> com = Integer::compare; int compare1 = com.compare(1, 2); int compare2 = com.compare(2, 2); int compare3 = com.compare(3, 2); System.out.println(compare1); System.out.println(compare2); System.out.println(compare3); }

    二.Stream

    1.了解Stream

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

    2.Stream是什么

    是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。集合讲的是数据,流讲的是计算

    注意:

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

    3.Stream的操作三个步骤

    1.创建 Stream

    一个数据源(如:集合,数组),获取一个流

    示例

    @Test public void test1(){ // 创建 Stream 流,可以通过 Collection 系列集合接口提供的 stream() 或 parallelStream() // 创建 Stream 流,可以通过 Arrays 的 stream 将数组转换成流 // 创建 Stream 流,可以通过 Stream.of() 方法获取流 // 创建流的第一种方式 List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); // 创建流的第二种方式 Employee[] emps = new Employee[10]; Stream<Employee> stream2 = Arrays.stream(emps); // 创建流的第三种方式 Stream<String> stream3 = Stream.of("aa", "bb", "cc"); // 创建流的第四种方式(创建无限流) // 1.迭代方式 Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2); stream4.limit(10).forEach(System.out::println); // 2.生成方式 Stream<Double> stream5 = Stream.generate(() -> Math.random()); stream5.limit(10).forEach(System.out::println); }
    2.中间操作

    一个中间操作链,对数据源的数据进行处理

    (1)筛选与切片 filter 接收lambda,从流中排除某些元素。 limit 截断流,使其元素不超过给定数量。 skip 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 distinct 筛选,通过流所生成的元素的hashCode() 和 equals() 去除重复元素

    filter List<Employee> employees = Arrays.asList( new Employee("张三",18, 9999.99), new Employee("李四",38, 5555.99), new Employee("王五",50, 6666.99), new Employee("赵六",16, 3333.99), new Employee("田七",8, 7777.99) ); @Test public void test1(){ Stream<Employee> employeeStream = employees.stream().filter((e) -> { System.out.println("Stream API 的中间操作"); return e.getAge() > 35; }); employeeStream.forEach(System.out::println); } limit // 返回流中前两个 Stream<Employee> employeeStream = employees.stream().limit(2); employeeStream.forEach(System.out::println); skip @Test public void test3(){ // 返回丢掉前两个元素的流 Stream<Employee> employeeStream = employees.stream().skip(2); employeeStream.forEach(System.out::println); } distinct @Test public void test4(){ // 这里去重是根据元素的 hashCode 和 equals 方法去除重复元素 Stream<Employee> employeeStream = employees.stream().distinct(); employeeStream.forEach(System.out::println); }

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

    示例1:元素转换成其他形式

    @Test public void test5(){ List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee"); // 这里会返回一个新流 Stream<String> stream = list.stream().map((str) -> str.toUpperCase()); stream.forEach(System.out::println); }

    示例2:提取流中的元素

    @Test public void test6(){ // 这里会返回一个新流 Stream<String> stream = employees.stream().map(Employee::getName); stream.forEach(System.out::println); }

    (3)排序 sorted() 自然排序 sorted(Comparator com) 定制排序

    示例1:自然排序

    @Test public void test7(){ List<String> list = Arrays.asList("ccc", "aaa", "bbb", "ddd", "eee"); list.stream().sorted().forEach(System.out::println); }

    示例2:定制排序

    @Test public void test8(){ Stream<Employee> stream = employees.stream().sorted((x, y) -> (int) (x.getSalary() - y.getSalary())); stream.forEach(System.out::println); }
    3.终止操作(终端操作)

    一个终止操作,执行中间操作链,并产生结果

    Employee.java

    package com.miracle.java8.test1; import java.util.Objects; public class Employee { private String name; private int age; private double salary; private Status status; public Employee() { } public Employee(String name, int age, double salary, Status status) { this.name = name; this.age = age; this.salary = salary; this.status = status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name); } @Override public int hashCode() { return Objects.hash(name, age, salary); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", salary=" + salary + ", status=" + status + '}'; } public enum Status{ FREE, BUSY, VOCATION } }

    (1)查找与匹配

    allMatch 检查是否匹配所有元素 @Test public void test9(){ boolean b = employees.stream().allMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } anyMatch 检查是否至少匹配一个元素 @Test public void test10(){ boolean b = employees.stream().anyMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } noneMatch 检查是否没有匹配所有元素 @Test public void test11(){ boolean b = employees.stream().noneMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b); } findFirst 返回第一个元素findAny 返回当前流中的任意元素count 返回流中元素的总个数max 返回流中最大值min 返回流中最小值

    (2)规约 reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值

    实例一:将集合中元素求和

    @Test public void test12(){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 这里reduce把集合里面数据相加,起始值为0 Integer sum = list.stream().reduce(0, (x, y) -> x + y); System.out.println(sum); }

    4.Stream的并发流

    Stream流操作默认产生的是串行流(单线程,速度慢),Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流直接进行转换

    串行流示例

    @Test public void test13(){ // 产生一个long型数字组成的流,是串行流(单线程,速度慢) LongStream longStream = LongStream.rangeClosed(0, 100000000000L); // 进行加和操作 long result = longStream.reduce(0, Long::sum); System.out.println(result); }

    并发流示例

    @Test public void test14(){ // 产生一个long型数字组成的流,是并发流(多线程,速度快) LongStream longStream = LongStream.rangeClosed(0, 100000000000L).parallel(); // 进行加和操作 long result = longStream.reduce(0, Long::sum); System.out.println(result); }

    三.Optional类

    1.介绍

    Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

    四.接口中的默认方法与静态方法

    五.新时间日期API

    1.LocalDate,LocalTime,LocalDateTime(获取的是当前操作系统时间)

    LocalDate,LocalTime,LocalDateTime类的实例是不可变的对象(线程安全),分别表示使用ISO-8601日历系统的日期,时间,日期和时间,三者API相同用法一致,下面以 LocalDateTime 为例

    @Test public void test1(){ // 获取当前系统时间 LocalDateTime now = LocalDateTime.now(); System.out.println(now); // 获取年 int year = now.getYear(); System.out.println(year); // 获取月 int month = now.getMonthValue(); System.out.println(month); // 获取天 int day = now.getDayOfMonth(); System.out.println(day); // 时 int hour = now.getHour(); System.out.println(hour); // 构造时间 LocalDateTime localDateTime = LocalDateTime.of(2015, 10, 19, 13, 22, 33); System.out.println(localDateTime); // 日期运算 LocalDateTime nowTime = LocalDateTime.now(); // 时间相加(plus系列方法,年月日时分秒),加两年 LocalDateTime plusYears = nowTime.plusYears(2); System.out.println(plusYears); // 时间相减(minus系列方法,年月日时分秒) 减两..年 LocalDateTime minusYears = plusYears.minusYears(2); System.out.println(minusYears); }

    2.Instant :时间戳(以 unix 元年:1970年1月1日 00:00:00 到某个时间之间的毫秒值)

    // 获取的是UTC时间,和北京时间有时差(8小时) Instant now = Instant.now(); // 这里打印的是正常的时间格式 System.out.println(now); // 获取时间戳(毫秒) long timeStamp = now.toEpochMilli(); System.out.println(timeStamp);

    3.时间间隔计算

    Duration :计算两个“时间”之间的间隔
    @Test public void test3(){ Instant instant1 = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant instant2 = Instant.now(); Duration between1 = Duration.between(instant1, instant2); // 求两个时间戳之间的间隔 System.out.println(between1.toMillis()); LocalTime localTime1 = LocalTime.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } LocalTime localTime2 = LocalTime.now(); // 两个时间间隔 Duration between2 = Duration.between(localTime1, localTime2); System.out.println(between2.toMillis()); }
    Period:计算两个“日期”之间的间隔
    @Test public void test4(){ LocalDate date1 = LocalDate.of(2015, 1, 1); LocalDate date2 = LocalDate.now(); Period period = Period.between(date1, date2); // 获取两个日期间,间隔多少年 System.out.println(period.getYears()); // 获取两个日期间,的间隔多少月 System.out.println(period.getMonths()); // 获取两个日期间,间隔多少日 System.out.println(period.getDays()); }

    4.日期的操纵

    TemporalAdjuster:时间校正器。有时我们可能需要获取例如,将日期调整到“下个周日”等操作。TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现 @Test public void test5(){ LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt); // 通过with方法,将日期指定为当月的某一天 LocalDateTime ldt2 = ldt.withDayOfMonth(1); System.out.println(ldt2); /** * 可以传入一个 TemporalAdjuster 接口的实现类 * 也可以使用 JDK 自带的 TemporalAdjusters中提供的实现 */ // 计算下周一的时间 LocalDateTime nextMonday = ldt.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); System.out.println(nextMonday); } DateTimeFormatter:格式化时间/日期 // 指定格式化标准 DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME; LocalDateTime ldt = LocalDateTime.now(); // 格式化 String formatDateTime = ldt.format(dtf); System.out.println(formatDateTime); // 也可以指定自己格式化标准 DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒"); String formatDateTime2 = ldt.format(dtf2); System.out.println(formatDateTime2);
    最新回复(0)