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 表达式兼容的函数式接口的类型。

 import java.util.function.Function;

 public class TestLambda {

     public static void main(String[] args) {
String str = toUpperString("abcdefg", (e) -> e.toUpperCase());
System.out.println(str);
} public static String toUpperString(String string, Function<String, String> function) {
return function.apply(string);
} }

函数式接口

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

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

 @FunctionalInterface
public interface MyInterface {
public void getValue();
}

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 表达式。

 public class MyClass {
Integer i; public MyClass() {
} public MyClass(Integer i) {
this.i = i;
} @Override
public String toString() {
return "MyClass{" +
"i=" + i +
'}';
}
}
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修饰的方法,则可以在接口中进行具体实现,如下所示:

 public interface MyInterface {

     //接口中的常规方法是不能实现的。
int getValue(); //接口中的具体实现默认方法:getName
default String getName(){
return "Hello JDK8!";
} //接口中的具体实现默认方法:getAge
default int getAge(){
return 8;
} }
 public interface MyFunc {

     default String getName(){
return "Hello MyFunc!";
}
}
 public class MyClass {

     public String getName(){
return "Hello MyClass!";
}
}
public class SubClass extends MyClass implements MyFunc{
}

输出结果:

Hello MyClass!

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

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

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

 public class TestClass implements MyFunc, MyInterface {

     @Override
public int getValue() {
return 0;
} @Override
public String getName() {
//因为接口可以多实现,然MyFunc接口 和 MyInterface接口 都有getName默认方法。于是需要使用一下方法进行指定调用。
// return MyInterface.super.getName();
return MyFunc.super.getName();
} @Override
public int getAge() {
return 0;
}
}
TestClass testClass = new TestClass();
System.out.println(testClass.getName());

输出结果:

Hello MyFunc!

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

静态方法

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

 public interface MyFunction {

     //接口中使用静态方法
static void show(){
System.out.println("Hello static!");
}
}
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

重复注解

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

 import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; //使用Repeatable注解指定可以重复注解,注解容器为MyAnnotations
@Repeatable(MyAnnotations.class)
@Target({TYPE,FIELD,METHOD,PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public

JDK8 新特性的更多相关文章

  1. java进阶一之jdk8新特性

    1.官方发布的jdk8新特性 2.51CTO相关专题

  2. JDK8新特性一览

    转载自:http://blog.csdn.net/qiubabin/article/details/70256683 官方新特性说明地址 Jdk8新特性.png 下面对几个常用的特性做下重点说明. 一 ...

  3. 一次电话Java面试的问题总结(JDK8新特性、哈希冲突、HashMap原理、线程安全、Linux查询命令、Hadoop节点)

    面试涉及问题含有: Java JDK8新特性 集合(哈希冲突.HashMap的原理.自动排序的集合TreeSet) 多线程安全问题 String和StringBuffer JVM 原理.运行流程.内部 ...

  4. jdk8新特性:在用Repository实体查询是总是提示要java.util.Optional, 原 Inferred type 'S' for type parameter 'S' is not within its bound;

    jdk8新特性:在用Repository实体查询是总是提示要java.util.Optional 在使用springboot 方法报错: Inferred type 'S' for type para ...

  5. 深入理解java虚拟机---jdk8新特性(二)

    1.jdk8新特性 1.新特性 2.lambda函数表达式的作用 A: 替换内部类 B:对集合的操作并行化

  6. JDK8新特性:使用stream、Comparator和Method Reference实现集合的优雅排序

    大家对java接口Comparator和Comparable都不陌生,JDK8里面Comparable还和以前一样,没有什么改动:但是Comparator在之前基础上增加了很多static和defau ...

  7. jdk8新特性

    JDK8新特性(JDK8的新特性) * 接口中可以定义有方法体的方法,如果是非静态,必须用default修饰 * 如果是静态的就不用了 class Test { public void run() { ...

  8. JDK8新特性之一Lambda

    JDK8的新特性之一Lambda能将函数作为方法里面的参数使用. /** * JDK8新特性Lambda */ public class Test { public static void main( ...

  9. JDK8新特性关于Stream流

    在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: 1 2 3 ...

随机推荐

  1. .NET开源工作流RoadFlow-表单设计-数据字典选择

    添加数字字典选择框: 选择范围:指定可选择的字典范围. 是否多选:指定是否可以多选.

  2. 01_JMS概述

    [以前的通信技术的局限性] 在大规模和复杂的分布式系统中,传统的RMI.DCOM等中间件通信技术逐渐有了局限性,如下: 1.同步通信:客户发出调用后,必须等待服务对象完成处理并返回结果才能继续执行. ...

  3. Android上使用RecyclerView实现顶部悬浮标题效果的Sticky Title View

    目前很多的项目都在使用顶部悬浮标题的效果,很明显,这的确是一个比较人性化,用户体验效果比较好的UI交互效果,对于这个效果,有很多种实现方式,如果说要用RecyclerView来实现一个分类信息展示,并 ...

  4. Qt之QSS(Q_PROPERTY-原始属性)

    http://blog.csdn.net/liang19890820/article/details/51698536 版权声明:进步始于交流,收获源于分享!纯正开源之美,有趣.好玩.靠谱...作者: ...

  5. 爬虫入门之urllib库详解(二)

    爬虫入门之urllib库详解(二) 1 urllib模块 urllib模块是一个运用于URL的包 urllib.request用于访问和读取URLS urllib.error包括了所有urllib.r ...

  6. March 16 2017 Week 11 Thursday

    Adventure may hurt you, but monotony will kill you. 也许冒险会让你受伤,但一成不变会让你灭亡. The very theme of the univ ...

  7. java--内存管理的几点小技巧

    今天看一本书,书上提到了内存泄露,后面也提到了内存管理的小技巧,在这里记下来,以免以后忘记. 1.尽量使用直接量.比如:String str = "I can play!";而不是 ...

  8. Java传引用问题

            Java传引用问题  使用Java调用方法时,可以传值,也可以传引用.下面说说两者的区别: 1.传值 传值中的"值"类型是指java的8大基本类型(基础知识,不知道 ...

  9. Android(java)学习笔记55:LayoutInflater 和 findViewById

    1. 在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById(). 不同点是LayoutInflater是用来找res/layout/下的xml布局文件, ...

  10. python:部分内置函数与匿名函数

    一.内置函数 1,数据类型:int,bool .......... 2,数据结构:dict,list,tuple,set,str 3,reversed--保留原列表,返回一个反序的迭代器 revers ...