JDK8新特性


JDK8新特性

  • 主要内容

    • lambda 表达式

    • 函数式接口

    • 方法引用与构造器引用

    • Stream API

    • 接口中的默认方法与静态方法

    • 新时间日期API

    • 其它新特性

  • 新特性简介

    • 速度更快
    • 代码更少(增加了新的语法Lambda表达式)
    • 强大的StreamAPI
    • 便于并行
    • 最大化减少空指针异常 Optional

Lambda表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码 像数据一样进行传递)。可以写出更简洁、更 灵活的代码。

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称 为 Lambda 操作符或箭头操作符。它将 Lambda 分为 两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行 的功能。

语法格式一: 无参,无返回值,Lambda 体只需一条语句
    Runnable r1 = () -> System.out.println("hello lombok");
    
语法格式二: Lambda 需要一个参数 (Lambda 只需要一个参数时,参数的小括号可省略)
    Consumer con = ags -> System.out.println(ags);

语法格式三: Lambda 需要两个参数,并且有返回值(当 Lambda体 只有一条语句时,可省略 {} 和 return)
    BinaryOperator bo = (x, y) -> x + y;

语法格式四: 完整版 参数数据类型可以省略,因为可由编译器推断得出,称为 “类型推断”
    BinaryOperator bo = (Long x, Long y) -> {
            Long z = x + y;
            return z;
        };

函数式接口

  • 只包含一个抽象方法的接口,称为函数式接口。
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
// 指定为函数式接口,编译器会在编译时判断当前接口是否仅且仅有一个方法,如果不是,则无法通过编译
@FunctionalInterface
public interface Func {
	// 唯一方法
    void theOnlyWay();
}

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接 收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
  • Java内置四大函数式接口

    // 消费型接口
    Consumer   参数:T   返回类型:void   
        对类型为T的对象应用操作,包含方法:void accept(T t)
    
    // 供给型接口
    Supplier   参数:无   返回类型:T   
        返回类型为T的对象,包含方法:T get();
        
    // 函数型接口
    Function   参数:T   返回类型:R   
        对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t);
     
    // 断定型接口
    Predicate   参数:T   返回类型:boolean   
        确定类型为T的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t);
    
    • 其它接口

      BiFunction   参数:T, U   返回:R  
          对类型为 T, U 参数应用操作,返回 R 类型的结果。包含方法为 R apply(T t, U u);
      
      UnaryOperator  (Function子接口)   参数:T   返回:T
          对类型为 T 的对象进行一元运算,并返回 T 类型的结果包含方法为 T apply(T t);
      
      BinaryOperator(BiFunction 子接口)   参数:T, T   返回:T   
          对类型为T的对象进行二元运算,并返回 T 类型的结果。包含方法为 T apply(T t1, T t2);
          
      BiConsumer   参数:T, U   返回:void   
          对类型为 T, U 参数应用操作。包含方法为 void accept(T t, U u)
          
      ToIntFunction   参数:T   返回: int
      ToLongFunction   参数:T   返回: long 
      ToDoubleFunction   参数:T   返回: double 
          分别计算int、long、double值的函数
          
      IntFunction   参数:int   返回: 
      LongFunction   参数:long   返回:  
      DoubleFunction   参数:double   返回: 
          参数分别为 int、long、double 类型的函数
      

方法引用与构造器引用

  • 方法引用

    当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!

    (实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)

    方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。

    • 对象::实例方法

    • 类::静态方法

    • 类::实例方法

      注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)

      时:ClassName::methodName

    (x) -> System.out.println(x)
        等价于
    System.out::println
        
    BinaryOperator bOpe = (x, y) -> Math.pow(x, y)
        等价于
    BinaryOperator bOpe = Math::pow;
    
    Arrays.sort(is, (o1, o2) -> Integer.compare(o1, o2));
        等价于
    Arrays.sort(is, Integer::compare);
    注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName 
    
  • 构造器引用

    格式:ClassName::new

    与函数式接口相结合,自动与函数式接口中方法兼容。
    可以把构造器引用赋值给定义的方法,与构造器参数 列表要与接口中抽象方法的参数列表一致!

    Function function = (i) -> new MyClass(i);
    	等价于
    Function function = MyClass::new;
    
  • 数组引用

    格式:type[] :: new

    Function types = (s) -> new Integer[s];
    	等价于
    Function types = Integer[]::new;
    
  • 强大的Stream API

    java.util.stream.*

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

    “集合讲的是数据,流讲的是计算!”

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

新时间日期API

  • 使用LocalDate、LocalTime、LocalDateTime

    LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

    注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法

    方法 描述 示例
    now() 静态方法,根据当前时间创建对象 LocalDate localDate = LocalDate.now();
    LocalTime localTime = LocalTime.now();
    LocalDateTime localDateTime = LocalDateTime.now();
    of() 静态方法,根据指定日期/时间创建对象 LocalDate localDate = LocalDate.of(2021, 10, 26);
    LocalTime localTime = LocalTime.of(02, 22, 56);
    LocalDateTime localDateTime = LocalDateTime.of(2021, 10, 26, 12, 10, 55);
    plusDays, plusWeeks, plusMonths, plusYears 向当前 LocalDate 对象添加几天、 几周、几个月、几年
    minusDays, minusWeeks, minusMonths, minusYears 从当前 LocalDate 对象减去几天、 几周、几个月、几年
    plus, minus 添加或减少一个Duration 或 Period
    withDayOfMonth,
    withDayOfYear,
    withMonth,
    withYear
    将月份天数、年份天数、月份、年 份修改为指定 的 值 并 返 回 新 的 LocalDate 对象
    getDayOfMonth 获得月份天数(1-31)
    getDayOfYear 获得年份天数(1-366)
    getDayOfWeek 获得星期 几 (返 回 一个 DayOfWeek 枚举值)
    getMonth 获得月份, 返回一个Month 枚举值
    getMonthValue 获得月份(1-12)
    getYear 获得年份
    until 获得两个日期之间的 Period 对象,或者指定ChronoUnits 的数字
    isBefore, isAfter 比较两个 LocalDate
    isLeapYear 判断是否是闰年
  • Instant 时间戳

    用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算

  • Duration 和 Period

    • Duration:用于计算两个“时间”间隔
    • Period:用于计算两个“日期”间隔
  • 日期的操纵

    • TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
    • emporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
  • 解析与格式化

    java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

    • 预定义的标准格式
    • 语言环境相关的格式
    • 自定义的格式
  • 时区的处理

    Java8 中加入了对时区的支持,带时区的时间为分别为:
    ZonedDate、ZonedTime、ZonedDateTime其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
    例如 :Asia/Shanghai 等

    • ZoneId:该类中包含了所有的时区信息
      getAvailableZoneIds() : 可以获取所有时区时区信息
      of(id) : 用指定的时区信息获取ZoneId 对象

    • 与传统日期处理的转换

      To 遗留类 From 遗留类
      java.time.Instant
      java.util.Date
      Date.from(instant) date.toInstant()
      java.time.Instant
      java.sql.Timestamp
      Timestamp.from(instant) timestamp.toInstant()
      ava.time.Instant
      java.sql.Timestamp
      GregorianCalendar.from(zonedDateTim
      e)
      cal.toZonedDateTime()
      ava.time.LocalDate
      java.sql.Time
      Date.valueOf(localDate) date.toLocalDate()
      java.time.LocalTime
      java.sql.Time
      Date.valueOf(localDate) date.toLocalTime()
      java.time.LocalDateTime
      java.sql.Timestamp
      Timestamp.valueOf(localDateTime)timestamp.toLocalDateTime() timestamp.toLocalDateTime()
      ava.time.ZoneId
      java.util.TimeZone
      Timezone.getTimeZone(id) timeZone.toZoneId()
      java.time.format.DateTimeFormatter
      java.text.DateFormat
      formatter.toFormat()

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

  • 接口中的默认方法

    Java 8中允许接口中包含具有具体实现的方法,该方法称为 “默认方法”,默认方法使用 default 关键字修饰。


    接口默认方法的”类优先”原则
    若一个接口中定义了一个默认方法,而另外一个父类或接口中 又定义了一个同名的方法时

    1. 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

    2. 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

  • 接口中的静态方法

其他新特性

  • Optional 类

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

    • 常用方法:

      Optional.of(T t) : 创建一个 Optional 实例
      Optional.empty() : 创建一个空的 Optional 实例
      Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
      isPresent() : 判断是否包含值
      orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
      orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
      map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
      flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

  • 重复注解与类型注解

    Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

    @Target({ElementType.TYPE})
    public @interface MyAnnotations {
        MyAnnotation[] value();
    }
    
    @Repeatable(MyAnnotations.class)
    @Target({ElementType.TYPE, ElementType.PARAMETER})
    public @interface MyAnnotation {
        String value();
    }
    
    @MyAnnotation("a")
    @MyAnnotation("b")
    public class Show {
        public void show(@MyAnnotation("c") String c) {
    
        }
    }