• JDK8 新特性


    JDK8 新特性目录导航:

    • Lambda 表达式
    • 函数式接口
    • 方法引用、构造器引用和数组引用
    • 接口支持默认方法和静态方法
    • Stream API
    • 增强类型推断
    • 新的日期时间 API
    • Optional 类
    • 重复注解和类型注解

    Lambda 表达式

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

    如下示例,将一个匿名类转换为Lambda表达式:

    //匿名内部类
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello world!");
        }
    };
    //Lambda 表达式
    Runnable runnable = () -> System.out.println("Hello world!");

    第一个匿名内部类的写法new一个Runnable接口并重写run方法打印Hello world!需要写一堆代码,但核心代码就一句System.out.println("Hello world!"),然而Lambda表达式仅需要一句代码() -> System.out.println("Hello world!")就可以替代上面的匿名内部类整个代码。从这个转换来看,Lambda表达式可以让代码更简洁,更灵活。

    Lambda 表达式语法

    Lambda 表达式在Java语言中引入了一个新的语法元素和操作符。这个操作符为 "->" ,该操作符被称为Lambda 操作符号或箭头操作符。它将Lambda分为两部分:

    • 左侧: 指定了Lambda 表达式需要的所有参数。
    • 右侧: 指定了Lambda 体,即Lambda 表达式要执行的功能。

    语法格式一: 无参,无返回值。Lambda 体只需要一条语句

    Runnable runnable = () -> System.out.println("Hello world!");

    语法格式二: 一个参数无返回值。注:一个参数时,扩符可以省略

    Consumer<String> consumer = (e) -> System.out.println(e);//一个参数时,参数的扩号可以省略。

    语法格式三: 两个参数并且有返回值。注:当Lambda 体只有一条语句时,可以省略 return 和 大括号。参数类型是可以省略的。通过编译器类型上下文推断出。同时也建议省略。如不省略则所有参数都必须加上类型。

    BinaryOperator<Integer> bo = (x,y) ->{
        System.out.println("实现函数式接口方法!");
        return x  + y;
    };
    //当Lambda 体只有一条语句时,可以省略 return大括号
    BinaryOperator<Integer> bo1 = (x,y) -> x  + y;
    //Lambda 的参数类型是可以省略的。通过编译器类型上下文推断出。同时也建议省略。如不省略则所有参数都必须加上类型。
    BinaryOperator<Integer> bo2 = (Integer x,Integer y) -> x  + y;

    语法格式四: 作为参数传递Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该Lambda 表达式兼容的函数式接口的类型。

     1 import java.util.function.Function;
     2 
     3 public class TestLambda {
     4 
     5     public static void main(String[] args) {
     6         String str = toUpperString("abcdefg", (e) -> e.toUpperCase());
     7         System.out.println(str);
     8     }
     9 
    10     public static String toUpperString(String string, Function<String, String> function) {
    11         return function.apply(string);
    12     }
    13 
    14 }

    函数式接口

    只包含一个抽象方法的接口,称为函数式接口。你可以通过Lambda 表达式来创建该接口的对象。我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

    如下所示,自定义函数式接口:

    1 @FunctionalInterface
    2 public interface MyInterface {
    3     public void getValue();
    4 }

    Java 内置四大核心函数式接口

    因为Lambda 表达式必须依赖函数式接口,然为了避免Lambda 表达式特意去书写函数式接口。Java 内置了如下四大核心函数式接口:

    • Consumer<T>: 消费型接口,表示一个接受单个输入参数并返回没有结果的操作。对类型为T的对象应用操作。接口方法: void accept(T t)
    • Supplier<T>: 供给型接口,类似一个供应商,返回一个类型为T的对象。接口方法: T get()
    • Function<T, R>: 函数型接口,表示一个接受一个参数并产生结果的函数。接口方法: R apply(T t)
    • Predicate<T>: 断言型接口,确定类型为T的对象是否满足某约束,并返回boolean 值。接口方法: boolean test(T t)

    除了以上四大内置接口外还有许许多多的函数式接口在 java.util.function 包下,比如:

    • BiFunction<T, U, R>: 与Function<T,R>类似,对类型为 T, U 参数应用操作,返回 R 类型的结果。接口方法:R apply(T t, U u);
    • UnaryOperator<T>: Function<T, T>的子接口。对类型为T的对象进行一元运算,并返回T类型的结果。接口方法:T apply(T t);
    • BinaryOperator<T>: BiFunction<T,T,T>的子接口,对类型为T的对象进行二元运算,并返回T类型的结果。接口方法:T apply(T t1, T t2);
    • ......
    • BiConsumer<T, U>: 对类型为T, U 参数应用操作。接口方法:void accept(T t, U u);

    方法引用、构造器引用和数组引用

    当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)

    方法引用

    使用操作符 “ ::” 将方法名和对象或类的名字分隔开来。有如下三种格式:

    • 引用静态方法: 类 :: 静态方法 
    • 引用特定对象的实例方法: 对象 :: 实例方法
    • 引用特定类型任意对象的实例方法: 特定类型 :: 实例方法

    如下示例,调用Math类的静态对象pow方法,可以直接使用Math::pow(格式:类::静态方法),引用静态方法代替Lambda 表达式。

    BinaryOperator<Double> bo = (x, y) -> Math.pow(x, y);
    System.out.println(bo.apply(2d, 3d));
    //Math::pow 可以替代 (x, y) -> Math.pow(x, y)
    BinaryOperator<Double> bo2 = Math::pow;
    System.out.println(bo2.apply(2d, 4d));

    输出结果:

    8.0
    16.0

    如下所示:调用System.out静态方法获取PrintStream对象再调用printf方法,可以直接使用System.out::printf(格式:对象::实例方法),引用特定对象的实例方法代替Lambda 表达式。

    Consumer<String> consumer = (x) -> System.out.println(x);
    consumer.accept("Hello");
    //System.out::printf 可以替代 (x) -> System.out.println(x)
    Consumer<String> consumer2 = System.out::printf;
    consumer2.accept("world");

    输出结果:

    Hello
    world

    如下所示:String特定类型的实例方法equals,可以直接使用String::equals(格式:特定类型::实例方法), 引用特定类型任意对象的实例方法代替Lambda 表达式。

    BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    System.out.println(bp.test("abcdef", "abcdef"));
    //String::equals 可以替代 (x, y) -> x.equals(y)
    BiPredicate<String, String> bp2 = String::equals;
    System.out.println(bp2.test("abcdef", "abcdef"));

    输出结果:

    true
    true

    构造器引用

    格式: 类::new  如下示例所示:new MyClass(n)构造器,可以直接使用MyClass::new。构造器引用可以直接代替Lambda 表达式。

     1 public class MyClass {
     2     Integer i;
     3 
     4     public MyClass() {
     5     }
     6 
     7     public MyClass(Integer i) {
     8         this.i = i;
     9     }
    10 
    11     @Override
    12     public String toString() {
    13         return "MyClass{" +
    14                 "i=" + i +
    15                 '}';
    16     }
    17 }
    Function<Integer, MyClass> myClass = (n) -> new MyClass(n);
    System.out.println(myClass.apply(15).toString());
    //MyClass::new 可以替代 (n) -> new MyClass(n)
    Function<Integer, MyClass> myClass2 = MyClass::new;
    System.out.println(myClass2.apply(10).toString());

    输出结果:

    MyClass{i=15}
    MyClass{i=10}

    数组引用

    格式:type[] :: new  如下示例所示:new Integer[n] 数组可以直接使用Integer[]::new代替。数组引用可以直接代替Lambda 表达式。

    Function<Integer, Integer[]> function = (n) -> new Integer[n];
    System.out.println(function.apply(15).length);
    //Integer[]::new 可以替代 (n) -> new Integer[n]
    Function<Integer, Integer[]> function2 = Integer[]::new;
    System.out.println(function2.apply(10).length);

    输出结果:

    15
    10

    接口支持默认方法和静态方法

    JDK8 中允许接口中包含具体的实现方法,该方法称为默认方法。同时接口中还支持静态方法。

    默认方法

    默认方法使用 default 关键字修饰。使用default修饰的方法,则可以在接口中进行具体实现,如下所示:

     1 public interface MyInterface {
     2 
     3     //接口中的常规方法是不能实现的。
     4     int getValue();
     5 
     6     //接口中的具体实现默认方法:getName
     7     default String getName(){
     8         return "Hello JDK8!";
     9     }
    10 
    11     //接口中的具体实现默认方法:getAge
    12     default int getAge(){
    13         return 8;
    14     }
    15 
    16 }
    1 public interface MyFunc {
    2 
    3     default String getName(){
    4         return "Hello MyFunc!";
    5     }
    6 }
    1 public class MyClass {
    2 
    3     public String getName(){
    4         return "Hello MyClass!";
    5     }
    6 }
    public class SubClass extends MyClass implements MyFunc{
    }

    输出结果:

    Hello MyClass!

    上面的示例可以看到,MyFunc接口中有默认方法getName、MyClass中也有getName()方法。然SubClass对象继承MyClass对象,同时实现MyFunc接口,调用SubClass对象的getName方法,实际执行的是MyClass对象的方法。接口的默认方法实现“类优先”原则。

    若一个接口中定义了一个默认方法,而另外一个父类又定义了一个同名的方法时,选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

    因接口可以多实现,则会出现如下示例:

     1 public class TestClass implements MyFunc, MyInterface {
     2 
     3     @Override
     4     public int getValue() {
     5         return 0;
     6     }
     7 
     8     @Override
     9     public String getName() {
    10         //因为接口可以多实现,然MyFunc接口 和 MyInterface接口 都有getName默认方法。于是需要使用一下方法进行指定调用。
    11 //        return MyInterface.super.getName();
    12         return MyFunc.super.getName();
    13     }
    14 
    15     @Override
    16     public int getAge() {
    17         return 0;
    18     }
    19 }
    TestClass testClass = new TestClass();
    System.out.println(testClass.getName());

    输出结果:

    Hello MyFunc!

    上面的示例可以看出。MyFunc接口和MyInterface接口中都有getName默认方法,然TestClass同时实现以上两个接口时,必须覆盖该方法来解决冲突。

    静态方法

    在JDK8 中,接口中允许使用静态方法。和类一样通过接口名称点静态方法去调用,如下示例所示:

    1 public interface MyFunction {
    2 
    3     //接口中使用静态方法
    4     static void show(){
    5         System.out.println("Hello static!");
    6     }
    7 }
    MyFunction.show();

    输出结果:

    Hello static!

    Stream API

    Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API( java.util.stream .*) 。

    Stream 是JDK8 中处理集合的关键抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种非常高效且易于使用的处理数据的方式。

    流(Stream)到底是什么?

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

    注意一下三点:

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

    Stream 的操作三个步骤:

    1. 创建 Stream: 一个数据源(如:集合、数组),获取一个流。
    2. 中间操作: 一个中间操作链,对数据源的数据进行一系列处理。
    3. 终止操作(终端操作): 一个终止操作,执行中间操作链,并产生结果。 

    创建 Stream

    JDK8 中的 Collection 接口被拓展,提供了两个获取流的方法:

    • default Stream<E> stream : Collection 接口的默认方法,返回一个顺序流。
    • default Stream<E> parallelStream: Collection 接口的的默认放,返回一个并行了。

    同时JDK8 在Arrays类中提供许多重载的Stream()静态方法 ,可以获取数组流。如下所示,可以处理很多类型的数组。

    • public static <T> Stream<T> stream(T[] array)
    • public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)
    • public static IntStream stream(int[] array)
    • public static IntStream stream(int[] array, int startInclusive, int endExclusive)
    • public static LongStream stream(long[] array)
    • public static LongStream stream(long[] array, int startInclusive, int endExclusive)
    • public static DoubleStream stream(double[] array)
    • public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) 

    Stream接口中提供了of静态方法,来创建一个流。

    • public static<T> Stream<T> of(T t)
    • public static<T> Stream<T> of(T... values)

    Stream接口还提供了iterate和generate方法创建无限流。

    • public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    • public static<T> Stream<T> generate(Supplier<T> s)

    Stream 的中间操作

    Stream 可以将多个中间操作连接起来形成一条流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作一次性全部处理,称为“惰性求值”

    Stream 的中间操作有以下几种:

    • 筛选与切片: 将Stream流进行筛选或截断处理。 
      • Stream<T> filter(Predicate<? super T> predicate): 接收Lambda 表达式,从流中排出某些元素;
      • Stream<T> distinct(): 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素;
      • Stream<T> limit(long maxSize): 截断流,使其元素不超过给定数量;
      • Stream<T> skip(long n): 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit方法互补。
    • 映射: 将Stream流映射到一个新的元素上。
      • <R> Stream<R> map(Function<? super T, ? extends R> mapper): 接受一个函数作为参数,该函数被应用到每一个元素上,并将其映射成一个新的元素。
      • IntStream mapToInt(ToIntFunction<? super T> mapper):接受一个函数作为参数,该函数被应用到每一个元素上,并将其映射成一个新的IntStream。
      • LongStream mapToLong(ToLongFunction<? super T> mapper):接受一个函数作为参数,该函数被应用到每一个元素上,并将其映射成一个新的LongStream。
      • DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper):接受一个函数作为参数,该函数被应用到每一个元素上,并将其映射成一个新的DoubleStream。
      • <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper): 接受一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流连接成一个流。flatMapToDouble、flatMapToInt和flatMapToLong以上差不多。只是获得具体的新流。
    • 排序: 将Stream流进行排序处理。 
      • Stream<T> sorted(): 返回一个新流,按自然顺序排序。
      • Stream<T> sorted(Comparator<? super T> comparator): 返回一个新流,按comparator比较器进行排序。

    Stream 的终止操作

    Stream 的终止操作会从流的流水线操作操作上获取一个新流。其结果可以是任何不是流的值。例如:List、Integer。甚至可以是 void。

    Stream 的终止操作有如下几种:

    • 查找与匹配: 查找流中的数据和进行匹配。
      • boolean allMatch(Predicate<? super T> predicate): 检查所有元素是否匹配该规则,返回一个布尔值。
      • boolean anyMatch(Predicate<? super T> predicate): 检查是否至少有一个匹配该规则,返回一个布尔值。
      • boolean noneMatch(Predicate<? super T> predicate): 检查该规则没有匹配所有元素,返回一个布尔值。
      • Optional<T> findFirst(): 返回流的第一个元素。
      • Optional<T> findAny(): 返回随机的一个元素。
      • long count(): 返回流的总数。
      • Optional<T> max(Comparator<? super T> comparator): 返回流中的最大值。
      • Optional<T> min(Comparator<? super T> comparator): 返回流中的最小值。
      • void forEach(Consumer<? super T> action): 内部迭代(Stream API 使用了内部迭达。相反,使用Collection 接口需要用户做的迭代是外部迭代)。
    • 归约:  将流中的元素反复结合返回一个新值。(备注:map 和 reduce 的连接通常称为 map-reduce 模式,因为Google 用它来进行网络搜索而出名)
      • Optional<T> reduce(BinaryOperator<T> accumulator): 将流中的元素反复结合,并返回一个新值 Optional<T>。
      • T reduce(T identity, BinaryOperator<T> accumulator): 将流中的元素反复结合,并返回一个新值T。
    • 收集: 将流转换为其他形式,用于将Stream中的元素做汇总。
      • <R, A> R collect(Collector<? super T, A, R> collector): 将流转换为其他形式。接收一个Collector 接口的实现,用于给Stream 中元素做汇总的方法。

    collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List 、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建收集器实例,具体方法与实例如下表:

    方法 返回类型 作用 示例
    toList List<T> 把流中的元素收集到List List<Employee> emps= list.stream().collect(Collectors.toList());
    toSet Set<T> 把流中的元素收集到Set Set<Employee> emps= list.stream().collect(Collectors.toSet());
    toCollection Collection<T> 把流中的元素收集到创建的集合 Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
    counting Long 计算流中元素的个数 long count = list.stream().collect(Collectors.counting());
    summingInt Integer 对流中元素的整数进行求和 inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
    averagingInt Double  计算流中元素Integer属性的平均值 doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
    summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。如:平均值 IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
    joining String 连接流中每个字符串 String str= list.stream().map(Employee::getName).collect(Collectors.joining());
    maxBy Optional<T> 根据比较器选择最大值 Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
    minBy Optional<T> 根据比较器选择最小值 Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
    reducing 归约产生的类型

    从一个作为累加器的初始值开始,利用BinaryOperator

    与流中元素逐个结合,从而归约成单个值

    inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
    collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果转换函数 inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
    groupingBy Map<K, List<T>>  根据某属性值对流分组,属性为K,结果为V Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
    partitioningBy Map<Boolean, List<T>> 根据true或false进行分区 Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

    并行流与串行流

    并行流就是把一个内容分为多个数据块,并使用不同的线程分别处理每个数据块的流。JDK8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel方法 与 sequential方法在并行流与串行流中进行切换。

    增强类型推断

    JDK8 中,编译器利用目标类型来推断泛型方法调用的类型参数。表达式的目标类型是编译器期望的数据类型,这取决于表达式出现的位置。例如:在JDK7 中使用赋值语句的目标类型进行类型推断。但是,在JDK8中,可以在更多上下文中使用目标类型进行类型推断。最显著的例子是使用方法调用的目标类型来推断其参数的数据类型。思考下面例子:

    //JDK7中,可以通过目标类型 stringList 的类型为String 推断出 ArrayList() 泛型类型为String。
    List<String> stringList = new ArrayList<>();
    stringList.add("A");
    //JDK8中,可以通过方法addALL的String类型,推断出 Arrays.asList()泛型类型为String。
    //在JDK7中,编译器是不能接受这段代码。因为它不支持目标方法调用来推断参数类型。
    //所以在JDK7 中必须这样写: stringList.addAll(Arrays.<String>asList());
    stringList.addAll(Arrays.asList());

    如上示例大概可以看出,增强的类型推断主要就是,可以通过调用泛型而通过调用者stringList的类型String。推断出Arrays.asList泛型的类型。

    新的日期时间 API

    JDK8中提供了一套全新的时间日期API(java.time.*)包下。使用了final修饰类,是起不可变,每次修改都是重新创建对象,类始于String对象,解决了线程安全问题。

    LocalDate、LocalTime 和 LocalDateTime类

    三个类的实例都是不可变的,每次修改操作都是新建一个实例对象。分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法。

    详细的方法如下表:

    方法 描述
    now() 静态方法,根据当前时间创建对象
    of() 静态方法,根据指定日期/时间创建对象

    plusDays

    plusWeeks

    plusMonths

    plusYears

    向当前 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 判断是否是闰年

    列举以下几个例子:

    LocalDate localDate = LocalDate.now();//获取当前日期
    LocalTime localTime = LocalTime.now();//获取当前时间
    LocalDateTime localDateTime = LocalDateTime.now();//获取当前日期时间
    LocalDateTime localDateTime1 = LocalDateTime.of(2018, 12, 19, 17, 00, 50);//通过指定数据去获取日期时间
    LocalDate localDate1 = localDate.plusDays(1);
    System.out.println("localDate: " + localDate);
    System.out.println("localTime: " + localTime);
    System.out.println("localDateTime: " + localDateTime);
    System.out.println("localDateTime1: " + localDateTime1);
    System.out.println("localDate1: " + localDate1);
    System.out.format("%s年%s月%s日 %s:%s:%s", localDateTime.getYear(),localDateTime.getMonthValue(),localDateTime.getDayOfMonth(),
            localDateTime.getHour(),localDateTime.getMinute(),localDateTime.getSecond());
    System.out.println("localDateTime1 isBefore localDateTime" + localDateTime1.isBefore(localDateTime));
    System.out.println("是否闰年:"+ localDate.isLeapYear());

    输出结果:

    localDate: 2018-06-19
    localTime: 17:12:37.701
    localDateTime: 2018-06-19T17:12:37.701
    localDateTime1: 2018-12-19T17:00:50
    localDate1: 2018-06-20
    2018年6月19日 17:12:37localDateTime1 isBefore localDateTimefalse
    是否闰年:false

    Instant 时间戳

    Instant 用于 “时间戳” 的运算。它是在Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始进行计算。常用方法如下:

    • public int getNano(): 获得纳秒值。
    • public long getEpochSecond(): 获得秒数。
    • public long toEpochMilli(): 获得分钟数。

    Duration 和 Period

    duration用来计算两个时间的间隔。period用于计算两个日期的间隔。

    Optional 类

    JDK8 中新增一个Optional<T>类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念。并且可以避免空指针异常。下图是Optional类的大致内容:

    常用的方法:

    • public static <T> Optional<T> of(T value): 创建一个Optional 实例。
    • public static<T> Optional<T> empty(): 创建一个空的Optional 实例。
    • public static <T> Optional<T> ofNullable(T value): 若T不为null,创建Optional实例,否则创建空实例。代码如下:return value == null ? empty() : of(value)。
    • public boolean isPresent(): 判断值是否为空。
    • public T orElse(T other): 如果值不为空返回该值,否则返回 other实例。
    • public T orElseGet(Supplier<? extends T> other): 如果调用该对象有值,返回该值,否则返回other的获取值。
    • public<U> Optional<U> map(Function<? super T, ? extends U> mapper): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。
    • public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper): 与 map 类似,要求返回值必须是Optional。
    • public T get(): 获取Optional对象的值。

    重复注解和类型注解

    重复注解

    在某些特定的情况下,您希望将相同的注解应用于声明或类型用途。思考如下示例:

     1 import java.lang.annotation.Repeatable;
     2 import java.lang.annotation.Retention;
     3 import java.lang.annotation.RetentionPolicy;
     4 import java.lang.annotation.Target;
     5 
     6 import static java.lang.annotation.ElementType.*;
     7 
     8 //使用Repeatable注解指定可以重复注解,注解容器为MyAnnotations
     9 @Repeatable(MyAnnotations.class)
    10 @Target({TYPE,FIELD,METHOD,PARAMETER})
    11 @Retention(RetentionPolicy.RUNTIME)
    12 public 

    重复注解

    在某些特定的情况下,您希望将相同的注解应用于声明或类型用途。思考如下示例:

     1 import java.lang.annotation.Repeatable;
     2 import java.lang.annotation.Retention;
     3 import java.lang.annotation.RetentionPolicy;
     4 import java.lang.annotation.Target;
     5 
     6 import static java.lang.annotation.ElementType.*;
     7 
     8 //使用Repeatable注解指定可以重复注解,注解容器为MyAnnotations
     9 @Repeatable(MyAnnotations.class)
    10 @Target({TYPE,FIELD,METHOD,PARAMETER})
    11 @Retention(RetentionPolicy.RUNTIME)
    12 public 
  • 相关阅读:
    简介.Net对象序列化.txt
    如何在Web页面退出前提示用户保存数据?
    如何将图片存储到数据库中
    页面回车键响应,onkeydown事件
    用C#创建Windows服务(Windows Services)
    解决“Visual Studio 要求设计器使用文件中的第一个类。移动类代码使之成为文件中的第一个类,然后尝试重新加载设计器。”方法
    动态创建htm元素并添加到document中
    如何在Asp.net的Header中添加/title/Meta tages/CSS
    无法打开项目文件,“d:\web\webapp.csproj”,此安装不支持该项目类型
    用Intelligencia.UrlRewriter实现URL重写
  • 原文地址:https://www.cnblogs.com/lufeiludaima/p/pz20190215.html
Copyright © 2020-2023  润新知