java8新特性

java8发布修改了很多的,比如HashMap、ConcurrentHashMap的实现都有了修改;ConcurrentHashMap在JDK8的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全,底层依然采用数组+链表+红黑树的存储结构。

说一下java8的有点有哪些:

1 速度更快

2 代码更少(增加了新的语法 Lambda 表达式)

3 强大的 Stream API

4 便于并行

5 最大化减少空指针异常 Optional

下面我们就来说有哪些变化:

1、lamada

2、stream

3、新时间日期 API

4、其他新特性

详情如下:

Lamada:

 

为什么使用 Lambda 表达式?

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

什么是函数式接口?

只包含一个抽象方法的接口,称为函数式接口。

你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。

我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

一、Lambda 表达式的基础语法:

Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符

箭头操作符将 Lambda 表达式拆分成两部分:

左侧:Lambda 表达式的参数列表

右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体

语法格式一:无参数,无返回值

() -> System.out.println("Hello Lambda!");

语法格式二:有一个参数,并且无返回值

(x) -> System.out.println(x)

语法格式三:若只有一个参数,小括号可以省略不写

x -> System.out.println(x)

语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

Comparator<Integer> com = (x, y) -> {

System.out.println("函数式接口");

return Integer.compare(x, y);

};

语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

(Integer x, Integer y) -> Integer.compare(x, y);

上联:左右遇一括号省

下联:左侧推断类型省

横批:能省则省

二、Lambda 表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰

可以检查是否是函数式接口

三、Java8 内置的四大核心函数式接口

Consumer<T> : 消费型接口

void accept(T t);

Supplier<T> : 供给型接口

T get();

Function<T, R> : 函数型接口

R apply(T t);

Predicate<T> : 断言型接口

boolean test(T t);

四、引用

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

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

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

如下三种主要使用情况:

一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用(可以将方法引用理解为 Lambda 表达式的另外一种表现形式)

1. 对象的引用 :: 实例方法名

2. 类名 :: 静态方法名

Comparator<Integer> com=(x,y)->Integer.compare(x, y);

int compare = com.compare(10, 20);

System.out.println(compare);

Comparator<Integer> com1=Integer::compare;

int i = com1.compare(20, 23);

System.out.println(i);

3. 类名 :: 实例方法名

Function<Employee, String> fun=Employee::getName;//这个地方需要注意,并不要()

String string = fun.apply(new Employee("wangbin"));

System.out.println(string);

Function<Employee,String> fun1=x->x.getName();

String str2 = fun1.apply(new Employee("wangbin"));

System.out.println(str2);

注意:

①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!

②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName

二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!

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

1. 类名 :: new

Function<String, Employee> fun=(x)->new Employee(x);

Employee employee = fun.apply("wangbin");

System.out.println(employee);

//这是构造器的引用

Function<String ,Employee> fun1=Employee::new;

Employee employee2 = fun1.apply("wangtianyan");

System.out.println(employee2);

三、数组引用

类型[] :: new;

Function<Integer,String[]> fun2=String[]::new;

String[] strings = fun2.apply(10);

System.out.println(strings.length);

Function<Integer,String[]> fun4=x->new String[x];

String[] str = fun4.apply(20);

System.out.println(str.length);

stream:

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

什么是 Stream

流(Stream) 到底是什么呢?

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

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

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream 的操作三个步骤

创建 Stream

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

中间操作

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

终止操作(终端操作)

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

创建 Stream

Java8 中的 Collection 接口被扩展,提供了

两个获取流的方法:

default Stream<E> stream() : 返回一个顺序流

default Stream<E> parallelStream() : 返回一个并行流

由数组创建流

Java8 中的 Arrays 的静态方法 stream() 可

以获取数组流:

static <T> Stream<T> stream(T[] array): 返回一个流

重载形式,能够处理对应基本类型的数组:

1、public static IntStream stream(int[] array)

2、public static LongStream stream(long[] array)

3、public static DoubleStream stream(double[] array)

由值创建流

可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。

1、public static<T> Stream<T> of(T... values) : 返回一个流

由函数创建流:创建无限流

可以使用静态方法 Stream.iterate() 和

Stream.generate(), 创建无限流。

迭代

public static<T> Stream<T> iterate(final T seed, final

UnaryOperator<T> f)

生成

public static<T> Stream<T> generate(Supplier<T> s) :

下面是所有创建流的方式:

//下面是通过Collection集合来创建流

List<String> list=new ArrayList<>();

//这是创建一个流

Stream<String> stream = list.stream();

//这是创建并行流

Stream<String> parallelStream = list.parallelStream();

//用数组来创建流

Integer[] u=new Integer[10];

Stream<Integer> stream2 = Arrays.stream(u);

//使用Stream的of方法来创建流

Stream<String> of = Stream.of("12");

//创建无限流

Stream.iterate(0, (x)->x+2).limit(10).forEach(System.out::println);

Stream.generate(Math::random).limit(10).forEach(System.out::println);

Stream 的中间操作

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

筛选与切片:

filter——接收 Lambda , 从流中排除某些元素。

limit——截断流,使其元素不超过给定数量。

skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补

distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

映射:

map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

排序:

Stream的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer,甚至是 void 。

查找与匹配

allMatch——检查是否匹配所有元素

anyMatch——检查是否至少匹配一个元素

noneMatch——检查是否没有匹配的元素

findFirst——返回第一个元素

findAny——返回当前流中的任意元素

count——返回流中元素的总个数

max——返回流中最大值

min——返回流中最小值

 

归约

/**

* 归约

reduce(T identity, BinaryOperator)

/ reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。

*/

@Test

public void test6(){

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

Optional<Integer> reduce = list.stream().reduce((x,y)->{return x+y;});

System.out.println(reduce);

Optional<Double> reduce2 = emps.stream().map(Employee::getSalary).reduce((x,y)->{return x+y;});

System.out.println(reduce2);

Double reduce3 = emps.stream().map(Employee::getSalary)

.reduce(0.0, Double::sum);

System.out.println(reduce3);

}

收集

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

 

并行流与串行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。

下面是一些例子:

List<Employee> emps = Arrays.asList(

new Employee(101, "张三", 18, 9999.99),

new Employee(102, "李四", 59, 6666.66),

new Employee(103, "王五", 28, 3333.33),

new Employee(104, "赵六", 8, 7777.77),

new Employee(105, "田七", 38, 5555.55)

);

@Test

public void test3(){

emps.stream().filter((m)->m.getAge()>50).forEach(System.out::println);

emps.stream().map(Employee::getName).forEach(System.out::println);

emps.stream()

.filter((m)->m.getSalary()>5000)

.limit(2)

.forEach(System.out::println);

}

 

 

结果:

Employee [id=102, name=李四, age=59, salary=6666.66]

张三

李四

王五

赵六

田七

Employee [id=101, name=张三, age=18, salary=9999.99]

Employee [id=102, name=李四, age=59, salary=6666.66]

 
stream向list集合里面的每一个对象设置值:
            //查询批次中的所有提现申请
            List<SupplierWithdrawRequest> supplierWithdrawRequests
                    = accountDao.listSupplierWithdrawRequestByBatchId(batchId)
                    .stream()
                    .map(supplierWithdrawRequest -> {
                        supplierWithdrawRequest.
                                setData(accountDao.getSupplierWithdrawRequestCostDetailBySupplierWithdrawRequestId(supplierWithdrawRequest.getId()));
                        return supplierWithdrawRequest;
                    }).collect(Collectors.toList());

1. distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;

distinct方法示意图(**以下所有的示意图都要感谢[RxJava](https://github.com/Netflix/RxJava)项目的doc中的图片给予的灵感, 如果示意图表达的有错误和不准确的地方,请直接联系我。**):

2. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;

filter方法示意图:

3. map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;

map方法示意图:

4. flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;

flatMap方法示意图:

5. peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;

peek方法示意图:

6. limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

limit方法示意图:

7. skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

skip方法示意图:

9. 性能问题
有些细心的同学可能会有这样的疑问:在对于一个Stream进行多次转换操作,每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是一个for循环里把所有操作都做掉的N(转换的次数)倍啊。其实不是这样的,转换操作都是lazy的,多个转换操作只会在汇聚操作(见下节)的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在汇聚操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。
10、性能

λ表达式与集合类批处理操作(或者叫块操作)

上文提到了集合类的批处理操作。这是Java8的另一个重要特性,它与λ表达式的配合使用乃是Java8的最主要特性。集合类的批处理操作API的目的是实现集合类的“内部迭代”,并期望充分利用现代多核CPU进行并行计算。
Java8之前集合类的迭代(Iteration)都是外部的,即客户代码。而内部迭代意味着改由Java类库来进行迭代,而不是客户代码。例如:

    for(Object o: list) { // 外部迭代
        System.out.println(o);
    }

可以写成:

    list.forEach(o -> {System.out.println(o);}); //forEach函数实现内部迭代

集合类(包括List)现在都有一个forEach方法,对元素进行迭代(遍历),所以我们不需要再写for循环了。forEach方法接受一个函数接口Consumer做参数,所以可以使用λ表达式。

这种内部迭代方法广泛存在于各种语言,如C++的STL算法库、python、ruby、scala等。

Java8为集合类引入了另一个重要概念:流(stream)。一个流通常以一个集合类实例为其数据源,然后在其上定义各种操作。流的API设计使用了管道(pipelines)模式。对流的一次操作会返回另一个流。如同IO的API或者StringBuffer的append方法那样,从而多个不同的操作可以在一个语句里串起来。看下面的例子:

    List<Shape> shapes = ...
    shapes.stream()
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> s.setColor(RED));

首先调用stream方法,以集合类对象shapes里面的元素为数据源,生成一个流。然后在这个流上调用filter方法,挑出蓝色的,返回另一个流。最后调用forEach方法将这些蓝色的物体喷成红色。(forEach方法不再返回流,而是一个终端方法,类似于StringBuffer在调用若干append之后的那个toString)

filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式。

还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。

    shapes.parallelStream(); // 或shapes.stream().parallel()

来看更多的例子。下面是典型的大数据处理方法,Filter-Map-Reduce:

    //给出一个String类型的数组,找出其中所有不重复的素数
    public void distinctPrimary(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        List<Integer> r = l.stream()
                .map(e -> new Integer(e))
                .filter(e -> Primes.isPrime(e))
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinctPrimary result is: " + r);
    }

第一步:传入一系列String(假设都是合法的数字),转成一个List,然后调用stream()方法生成流。

第二步:调用流的map方法把每个元素由String转成Integer,得到一个新的流。map方法接受一个Function类型的参数,上面介绍了,Function是个函数接口,所以这里用λ表达式。

第三步:调用流的filter方法,过滤那些不是素数的数字,并得到一个新流。filter方法接受一个Predicate类型的参数,上面介绍了,Predicate是个函数接口,所以这里用λ表达式。

第四步:调用流的distinct方法,去掉重复,并得到一个新流。这本质上是另一个filter操作。

第五步:用collect方法将最终结果收集到一个List里面去。collect方法接受一个Collector类型的参数,这个参数指明如何收集最终结果。在这个例子中,结果简单地收集到一个List中。我们也可以用Collectors.toMap(e->e, e->e)把结果收集到一个Map中,它的意思是:把结果收到一个Map,用这些素数自身既作为键又作为值。toMap方法接受两个Function类型的参数,分别用以生成键和值,Function是个函数接口,所以这里都用λ表达式。

你可能会觉得在这个例子里,List l被迭代了好多次,map,filter,distinct都分别是一次循环,效率会不好。实际并非如此。这些返回另一个Stream的方法都是“懒(lazy)”的,而最后返回最终结果的collect方法则是“急(eager)”的。在遇到eager方法之前,lazy的方法不会执行

当遇到eager方法时,前面的lazy方法才会被依次执行。而且是管道贯通式执行。这意味着每一个元素依次通过这些管道。例如有个元素“3”,首先它被map成整数型3;然后通过filter,发现是素数,被保留下来;又通过distinct,如果已经有一个3了,那么就直接丢弃,如果还没有则保留。这样,3个操作其实只经过了一次循环。

除collect外其它的eager操作还有forEach,toArray,reduce等。

下面来看一下也许是最常用的收集器方法,groupingBy:

    //给出一个String类型的数组,找出其中各个素数,并统计其出现次数
    public void primaryOccurrence(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        Map<Integer, Integer> r = l.stream()
            .map(e -> new Integer(e))
            .filter(e -> Primes.isPrime(e))
            .collect( Collectors.groupingBy(p->p, Collectors.summingInt(p->1)) );
        System.out.println("primaryOccurrence result is: " + r);
    }

注意这一行:

    Collectors.groupingBy(p->p, Collectors.summingInt(p->1))

它的意思是:把结果收集到一个Map中,用统计到的各个素数自身作为键,其出现次数作为值。

下面是一个reduce的例子:

    //给出一个String类型的数组,求其中所有不重复素数的和
    public void distinctPrimarySum(String... numbers) {
        List<String> l = Arrays.asList(numbers);
        int sum = l.stream()
            .map(e -> new Integer(e))
            .filter(e -> Primes.isPrime(e))
            .distinct()
            .reduce(0, (x,y) -> x+y); // equivalent to .sum()
        System.out.println("distinctPrimarySum result is: " + sum);
    }

reduce方法用来产生单一的一个最终结果。
流有很多预定义的reduce操作,如sum(),max(),min()等。

再举个现实世界里的栗子比如:

    // 统计年龄在25-35岁的男女人数、比例
    public void boysAndGirls(List<Person> persons) {
        Map<Integer, Integer> result = persons.parallelStream().filter(p -> p.getAge()>=25 && p.getAge()<=35).
            collect(
                Collectors.groupingBy(p->p.getSex(), Collectors.summingInt(p->1))
        );
        System.out.print("boysAndGirls result is " + result);
        System.out.println(", ratio (male : female) is " + (float)result.get(Person.MALE)/result.get(Person.FEMALE));
    }

11、其他特性可以参考下面的博客:

https://www.cnblogs.com/NiceCui/p/java8.html

http://www.cnblogs.com/figure9/archive/2014/10/24/4048421.html

新时间日期 API :

5-新时间日期 API

使用 LocalDate、 LocalTime、 LocalDateTime

LocalDateTime是LocalDate和LocalTime的集合体,

2018-07-13T17:03:55.663(LocalDateTime now = LocalDateTime.now())

LocalTime只有时间:17:01:55.959(LocalTime now = LocalTime.now()

LocalDate只有日期:2018-07-13(LocalDate now = LocalDate.now()

 

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

Instant 时间戳

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

2018-07-13T09:09:00.180Z(Instant now = Instant.now())

 

Instant now = Instant.now();

//下面是带偏移量8小时

OffsetDateTime atOffset = now.atOffset(ZoneOffset.ofHours(8));

System.out.println(now);

System.out.println(atOffset);

//now.getNano()这个是转化为秒

System.out.println(now.toEpochMilli());

Instant milli = Instant.ofEpochMilli(10);

System.out.println(milli);

打印的结果:

2018-07-13T09:39:01.064Z

2018-07-13T17:39:01.064+08:00

1531474741064

1970-01-01T00:00:00.010Z

Duration 和 Period

Duration:用于计算两个“时间”间隔

LocalTime t1=LocalTime.now();

LocalTime of = LocalTime.of(23, 0, 0);

System.out.println(Duration.between(t1, of).getSeconds());

LocalDateTime start = LocalDateTime.now();

LocalDateTime of2 = LocalDateTime.of(2018, 7, 13, 23, 23,0);

System.out.println(Duration.between(start, of2).getSeconds());

Period:用于计算两个“日期”间隔

LocalDate start=LocalDate.now();

Thread.sleep(100);

LocalDate end=LocalDate.of(2018, 7, 30);

System.out.println(Period.between(start, end).getDays());

日期的操纵

TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。

TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。

例如获取下个周日:

解析与格式化

java.time.format.DateTimeFormatter 类:该类提供了三种

格式化方法:

预定义的标准格式

语言环境相关的格式

自定义的格式

LocalDateTime now = LocalDateTime.now();

DateTimeFormatter date=DateTimeFormatter.ofPattern("yyyy年MM月dd HH-mm-ss");

、、这是日期格式化:2018-07-13T17:23:48.638-》2018年07月13 17-23-48

String format = now.format(date);

System.out.println(format);

System.out.println(now);

、、这是日期转化回来:2018年07月13 17-23-48-》2018-07-13T17:23:48

LocalDateTime parse = LocalDateTime.parse(format, date);

System.out.println(parse);

下面是打印的结果:

2018年07月13 17-23-48

2018-07-13T17:23:48.638

2018-07-13T17:23:48

时区的处理

Java8 中加入了对时区的支持,带时区的时间为分别为:

ZonedDate、 ZonedTime、 ZonedDateTime、ZoneId

获得所有的时区:

Set<String> set = ZoneId.getAvailableZoneIds();

set.forEach(System.out::println);

其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式例如 : Asia/Shanghai 等

ZoneId:该类中包含了所有的时区信息

getAvailableZoneIds() : 可以获取所有时区时区信息

of(id) : 用指定的时区信息获取 ZoneId 对象

与传统日期处理的转换

其他新特性:

1、接口的改变:

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

接口中的默认方法

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

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

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

2、注解

重复注解与类型注解

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

 

下面是定义重复注解的实例:

@Repeatable(MyAnnocations.class)

@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE })

@Retention(RetentionPolicy.SOURCE)

public @interface MyAnnocation {

String value() default "jrj";

}

 

@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE })

@Retention(RetentionPolicy.SOURCE)

public @interface MyAnnocations {

 

MyAnnocation[] value();

}

@Repeatable这个注解一定要添加的

 

3、Optional 容器类

 

Optional<T> 类(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

 

也可以参考:https://segmentfault.com/a/1190000006985405

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值