终极CURD-4-java8新特性

1 概述

本篇博客主要介绍java8新特性,由于本身日常也使用到了一些java8的新特性,可是历来没有从头至尾,真真正正的把几乎全部经常使用的java8新特性研究一遍,此次借助端午节3三天,好好把经常使用java8新特性梳理一下,毕竟java12立刻就要出来了。java

本篇博客不会重点介绍java8新特性的基础内容,由于网上有不少优秀的文章已经介绍,好比像lambda表达式的用法,stream的特性,Optional的基础使用等等。在下就不重复造轮子了,我会给出几个我认为很是优秀的文章,对java8还不熟悉的读者,能够先看这些基础内容。本篇博客主要聚焦于java8新特性难点和易错点。express

2 lambda表达式

若是对lambda还不清楚的读者,能够先按顺序阅读下面两篇文章segmentfault

https://www.runoob.com/java/java8-lambda-expressions.htmlapi

http://blog.oneapm.com/apm-tech/226.html数组

2.1 lambda重要知识点总结

①lambda仅仅是一个语法糖,编译成class文件后,就能够发现,lambda变成了内部类网络

②一个lambda表达式,当作是一个匿名对象app

③lambda表达式 所表明的的接口必须是一个函数式接口框架

④ 函数式接口:接口中只有一个抽象方法,就称为函数式接口。可使用@FunctionalInterface确保该接口为函数式接口,若是不当心写错,java编译器会报错。dom

注意:函数式接口能够包含多个default方法和Object方法,如下程序编译器不会报错

@FunctionalInterface
public interface MyFunction {

    void hello();

    @Override
    boolean equals(Object o);

    default String sayHi() {
        System.out.println("hi ... everyone i am super ...");
        return "hi";
    }
}

⑤函数式接口的用处,某些方法的参数,可使用这些函数式接口,那么咱们在调用这些方法的时候,就可使用lambda表达式。函数式接口的惟一做用,就是为了lambda的使用

lambda重点在于参数和body,因此参数类型和body逻辑必定要正确,java编译期会自动将lambda转换成对象。

@Test // 使用匿名对象
    public void test1(){
        Comparable<Integer>  comparable = new Comparable<Integer>() {
            @Override
            public int compareTo(Integer o) {
                return 1;
            }
        };
    }

    @Test  // 使用lambda表达式
    public void test2(){
        Comparable<Integer> comparable = (o) -> 1;
    }

     /*
       想想 为何 下面4个会报错
                                            (o) -> "1"
                                            (o) -> System.out.print("abc")
                                            () -> 1;
                                            (String o) -> 1;
       缘由:java编译器如今已经很是强大,能够根据上下文推到lambda表达式参数和body,由于lambda表达式
       实际上就是一个匿名对象,所以lambda的参数类型和body代码逻辑必需要符合匿名对象的格式
     */

⑦lambda 表达式的局部变量能够不用声明为 final,可是必须不可被后面的代码修改(即隐性的具备 final 的语义,编辑器会自动帮咱们加上final)

2.2 java内置函数接口

java内置函数接口,不须要咱们本身写函数接口。最经常使用的四大函数接口是,Consumer、Supplier、Function、Predicate全部内置函数接口均在 java.util.function

为何java会内置如此多的函数式接口,咱们明明能够本身写函数式接口啊?

​ 缘由:由于函数式接口很是简单,一个接口声明,加上一个抽象方法。既然如此,java的内置函数式接口就是业界规定,是一种规范。好比我想写一个Myfunction函数式接口

public interface MyFunction<T> {
     void consume(T t);
}

而别人又写宁一个MyFunction二、MyFunction3,既然如此,那你们就都用java内置的函数式接口,约定很重要!!!

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

2.3 方法引用

核心:方法引用只是lambda表现的另外一种形式,它依旧仍是一个匿名内部对象

1 对象::实例方法 该方法的参数类型和返回类型和函数式接口中的抽象方法必须一致

// 1 匿名对象
        Consumer<String> consumer1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

        // 2 lambda表达式
        Consumer<String> consumer2 = s -> System.out.println(s);

        // 3 方法引用 1
        PrintStream printStream = System.out;
        Consumer<String> consumer3 = printStream::println;

        // 4 方法引用 2
        Consumer<String> consumer4 = System.out::println;

2 类::静态方法 该方法的参数类型和返回类型和函数式接口中的抽象方法必须一致

@Test
    public void test4(){
        // 1
        Comparator<Integer> comparator1 = (x,y) -> Integer.compare(x,y);
        // 2
        Comparator<Integer> comparator2 = Integer::compare;
    }

3 类::实例方法 特殊 该方法的参数类型和返回类型和函数式接口中的抽象方法确定不一致,不一致,不一致

@Test
    public void test5(){
        // 1
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        // 2
        BiPredicate<String, String> bp2 = String::equals;
    }
    /*
    若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格     式: ClassName::MethodName  好比 x就是body中的调用者,而y正是该方法的参数
    */

好比下,下面的 类::静态方法 类::实例方法 是等价的

public class TestUtil {
    // 实例方法  没有参数
    private TestUtil println2() {
        return new TestUtil();
    }
    
    // 静态方法 有参数
    private static TestUtil println3(TestUtil testUtil) {
        return testUtil;
    }

    public static void main(String[] args) {
        TestUtil testUtil = new TestUtil();
        // 实例方法引用 函数式接口和实际方法参数确定不一致
        Function<TestUtil, TestUtil> function2222 = TestUtil::println2;
        // 静态方法引用 函数式接口和实际方法参数必须一致
        Function<TestUtil, TestUtil> function33333 = TestUtil::println3;
    }
}

实际上TestUtil::println2,TestUtil::println3均可以当作是一个匿名对象,java编译事后,class文件几乎是同样的。

日常咱们仍是写 () -> {} 这种形式的,若是存在方法引用或者构造器引用,idea会自动提示咱们,咱们再修改便可。可是最好仍是要给出详细注释,由于有的方法引用,一时半会看不明白。

2.4 构造器引用

构造器参数类型必须和函数式接口中的抽象方法一致,返回类型默认就是该类

@Test
    public void test6(){
        // 1
        Supplier<Employee> supplier1 = () -> new Employee();
        // 2 默认使用Employee无参构造器,由于Supplier的get方法没有任何参数
        Supplier<Employee> supplier2 = Employee::new;
        // 3 Employee必需要有一个 Integer构造器   public Employee(Integer age)
        Function<Integer,Employee> function = Employee::new;
    }

2.5 数组引用

构造器参数类型必须和函数式接口中的抽象方法一致,返回类型默认就是该数组

@Test
    public void test7(){
        // 1
        Function<Integer,String[]> function1 = x -> new String[x];
        // 2
        Function<Integer,String[]> function2 = String[]::new;
    }

2.6 lambda表达式的陷阱

有一篇博客很是棒,推荐给你们

http://www.javashuo.com/article/p-uocjsriy-kg.html

3 Stream

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

注意:

①Stream 本身不会存储元素。

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

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

3.1 stream 三个核心步骤

一:建立 Stream

二:中间操做

三:终止操做(终端操做)

3.2 stream 建立的5个方法

//1. Collection 提供了两个方法  stream() 与 parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream(); //获取一个顺序流
        Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
        
        //2. 经过 Arrays 中的 stream() 获取一个数组流
        Integer[] nums = new Integer[10];
        Stream<Integer> stream1 = Arrays.stream(nums);
        
        //3. 经过 Stream 类中静态方法 of()
        Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
        
        //4. 建立无限流
        //迭代
        Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
        stream3.forEach(System.out::println);
        
        //5. 建立无限流
        //生成
        Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
        stream4.forEach(TestUtil::println2);

3.3 stream 中间操做

多个中间操做能够链接起来造成一个流水线,除非流水 线上触发终止操做,不然中间操做不会执行任何的处理!

而在终止操做时一次性所有处理,称为“惰性求值”。

一:筛选与切片

方法 描述
filter(Predicate p) 接收 Lambda , 从流中排除某些元素。
distinct() 筛选,经过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量。
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补

二:映射

方法 描述
map(Function f) 接收一个函数做为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) 接收一个函数做为参数,该函数会被应用到每一个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一个函数做为参数,该函数会被应用到每一个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数做为参数,该函数会被应用到每一个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数做为参数,将流中的每一个值都换成另 一个流,而后把全部流链接成一个流

注意:map和flatMap的区别

区别就是 list方法中的 add 和 addAll

add(Object o) 若是添加一个集合,会直接把该集合添加进去。

addAll(Object o)若是添加一个集合,会把集合中的元素一个一个加入进去,而不会加入一整个集合。

三:排序

方法 描述
sorted() 产生一个新流,其中按天然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序

在stream中排序方法就只有sorted,若是不带参数,就是天然排序

List<String> list = Arrays.asList("ccc","aaa","ddd","bbb");
        list.stream().sorted().forEach(System.out::println);

由于集合中的元素 String 实现了 Comparable接口,因此会自动调用public int compareTo(T o)进行排序

若是sorted方法带了参数,就是定制排序

emps.stream().sorted((e1,e2) -> e1.getAge()-e2.getAge()).forEach(System.out::println);

3.4 stream 终止操做

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

一:查找与匹配:

方法 描述
allMatch(Predicate p) 检查是否匹配全部元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配全部元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口须要用户去作迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代作了)

二:归约:

方法 描述
reduce(T iden, BinaryOperator b) 能够将流中元素反复结合起来,获得一个值。 返回 T
reduce(BinaryOperator b) 能够将流中元素反复结合起来,获得一个值。 返回 Optional

备注:map 和 reduce 的链接一般称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

由于reduce(T iden, BinaryOperator b)中,起始值就是T iden因此确定会有一个值,不会返回null值,因此返回值不是Optional

三:收集:

方法 描述
collect(Collector c) 将流转换为其余形式。接收一个 Collector接口的实现,用于给Stream中元素作汇总的方法

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

方法 返回类型 做用
toList List 把流中元素收集到List
toSet Set 把流中元素收集到Set
toCollection Collection 把流中元素收集到建立的集合
counting Long 计算流中元素的个数
summingInt Integer 对流中元素的整数属性求和
averagingInt Double 计算流中元素Integer属性的平均值
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。好比:数量、总和、最小值、平均值、最大值
maxBy Optional 根据比较器选择最大值
minBy Optional 根据比较器选择最小值
groupingBy Map<K,List > 根据某属性值对流分组,属性为K,值为List
partitioningBy Map<Boolean,List > 根据true或false进行分区
joining String 链接流中每一个字符串

注意:groupingBy能够多级分组

3.5 并行流和串行流

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

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

3.6 fork/join

Fork/Join 框架:就是在必要的状况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。

Fork/Join 框架与传统线程池的区别

采用 “工做窃取”模式(work-stealing):
当执行新的任务时它能够将其拆分分红更小的任务执行,并将小任务加到线程队列中,而后再从一个随机线程的队列中偷一个并把它放在本身的队列中。

相对于通常的线程池实现,fork/join框架的优点体如今对其中包含的任务的 处理方式上.在通常的线程池中,若是一个线程正在执行的任务因为某些缘由 没法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,若是某个子问题因为等待另一个子问题的完成而没法继续运行.那么处理该子 问题的线程会主动寻找其余还没有运行的子问题来执行.这种方式减小了线程 的等待时间,提升了性能.

在jdk1.8之前,fork/join比较复杂,代码编写比较难,而在jdk1.8中,利用stream中的parallel()能够很是轻松的实现fork/join的效果。(parallel底层就是利用fork/join)

4 java8中默认的接口和方法

由于要兼容java之前和接口特性,因此java1.8中才有接口默认方法。

不过有如下点注意点:

①接口默认方法 “类优先” 原则

若一个接口中定义了一个默认方法,而另一个父类或接口又定义了一个同名的方法时 ,子类若是直接调用,会选择父类的方法

②接口冲突

若是一个父接口提供一个默认方法,而另外一个接 口也提供了一个具备相同名称和参数列表的方法(无论方法是不是默认方法),那么必须覆盖该方法来解决冲突

③若是存在接口冲突,如何选择指定的接口方法呢?

public class SubClass implements Animal,Animal2{}
@Override
    public String hello() {
        return Animal.super.hello();
    }

    @Override
    public String hello() {
        return Animal2.super.hello();
    }

经过 Interface.super.method()

④接口中能够存在静态方法,和普通类的静态方法调用方式如出一辙。

5 Optional

有两篇很是不错的文章,你们能够参考一下,本人就不重复造轮子了。

http://www.javashuo.com/article/p-bslaslln-gg.html

http://www.importnew.com/22060.html

Optional的底层源码其实很是简单,建议你们多去看看源码。

5.1 Optional 精华所在

请仔细思考如下代码:

@Test
    public void test1() {
        Animal animal = getAnimal();
        if (animal != null) {
            Person person = animal.getPerson();
            if (person != null) {
                Student student = person.getStudent();
                if (student != null) {
                    System.out.println(student.getUsername());
                }
            }
        }
    }

  @Test
    public void test2() {
        Optional<Animal> animal = Optional.ofNullable(getAnimal());
        String name = animal.map(Animal::getPerson)
                .map(Person::getStudent)
                .map(Student::getUsername)
                .orElse("animal name");
        System.out.println(name);
    }

test1 是典型的非空判断,test2是使用Optional进行的非空判断,很明显后者的代码更加优雅,而且若是对象依赖的层级关系越多,那么if也就会越多,这时候使用Optional能省去不少麻烦。

5.2 Optional 误区

一:能够参考博客,文章内容很是不错,值得一看!

https://segmentfault.com/a/1190000018936877?utm_source=tag-newest

二:optional中使用map和flatMap

map源码:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

flatMap源码:

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

咱们能够看到,在flatMap中,若是mapper.apply(value)返回null,那么将会直接抛出异常。

而在map中mapper.apply(value)为null,不会抛出异常,仅仅返回一个empty对象。

6 java8 新的时间api

LocalDate、LocalTime、LocalDateTime:

/**
     * LocalDate、LocalTime、LocalDateTime API用法几乎同样
     * LocalDate  只有 年-月-日
     * LocalTime  只有 时-分-秒-纳秒
     * LocalDateTime 年-月-日-时-分-秒-纳秒
     */
    @Test
    public void test1() {
        LocalDateTime ldt = LocalDateTime.now(); //2019-06-12T11:40:55.132
        LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);//2016-11-                                                                               21T10:10:10
        LocalDateTime ldt3 = ld2.plusYears(20);//2036-11-21T10:10:10
        LocalDateTime ldt4 = ld2.minusMonths(2);//2016-09-21T10:10:10
        System.out.println(ldt.getYear());//2019
        System.out.println(ldt.getMonthValue());//6
        System.out.println(ldt.getDayOfMonth());//12
        System.out.println(ldt.getHour());//11
        System.out.println(ldt.getMinute());//40
        System.out.println(ldt.getSecond());//55
    }

Instant:

/**
     * Instant : 时间戳。 (使用 Unix 元年  1970年1月1日 00:00:00 所经历的毫秒值)
     */
    @Test
    public void test2() {
        Instant ins = Instant.now();  //默认使用 UTC 时区
        System.out.println(ins); //2019-06-12T03:45:38.923Z
        OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt); //2019-06-12T11:46:08.147+08:00
        System.out.println(odt.toEpochSecond()); //1560311168  不论那个时区,输出的毫秒和秒                                                                           都是同样的
        System.out.println(ins.getEpochSecond()); //1560311168
        System.out.println(ins.toEpochMilli()); // 和System.currentTimeMillis() 同样
    }

Duration、Period:

/**
     * Duration : 用于计算两个“时间”间隔
     * Period : 用于计算两个“日期”间隔
     */ 
    @Test
    public void test3() throws InterruptedException {
        Instant ins1 = Instant.now();
        Thread.sleep(1000);
        Instant ins2 = Instant.now();
        System.out.println("所耗费时间为:" + Duration.between(ins1, ins2)); //所耗费时间                                                                           为:PT1.001S
        System.out.println("----------------------------------");
        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2011, 1, 1);
        Period pe = Period.between(ld2, ld1);
        System.out.println(pe.getYears()); //8
        System.out.println(pe.getMonths()); //5
        System.out.println(pe.getDays()); //11
    }

TemporalAdjuster:

/**
     * TemporalAdjuster : 时间校订器。有时咱们可能须要获取例如:将日期调整到“下个周日”、“这个月的第一天”、"这一年的最后一天"等操做。
     * TemporalAdjusters : 该类经过静态方法提供了大量的经常使用 TemporalAdjuster 的实现。
     * 主要涉及到LocalDateTime、LocalDate、LocalTime的with方法
     * public LocalDateTime with(TemporalAdjuster adjuster) { ... }
     */
    @Test
    public void test4() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt); // 2019-06-12T13:53:06.814

        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2); // 2019-06-10T13:53:06.814

        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3); // 2019-06-16T13:53:06.814

        //自定义:下一个工做日
        LocalDateTime ldt5 = ldt.with((l) -> {
            LocalDateTime ldt4 = (LocalDateTime) l;

            DayOfWeek dow = ldt4.getDayOfWeek();

            if (dow.equals(DayOfWeek.FRIDAY)) {
                return ldt4.plusDays(3);
            } else if (dow.equals(DayOfWeek.SATURDAY)) {
                return ldt4.plusDays(2);
            } else {
                return ldt4.plusDays(1);
            }
        });
        System.out.println(ldt5); // 2019-06-13T13:53:06.814
    }

DateTimeFormatter

/**
     * DateTimeFormatter : 解析和格式化日期或时间
     */
    @Test
    public void test5() {
        // DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE; DateTimeFormatter内部定义了不少时间格式                
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss:SSS E");  // 自定义格式,E表示周几
        LocalDateTime ldt = LocalDateTime.now();
        String strDate = ldt.format(dtf); //LocalDateTime转成String
        System.out.println(strDate); // 2019年06月12日 14:14:22:611 星期三
        LocalDateTime newLdt = LocalDateTime.parse(strDate, dtf); // 将String转换成LocalDateTime
        System.out.println(newLdt); // 2019-06-12T14:14:22.611
    }

ZonedDate、ZonedTime、ZonedDateTime

/**
 * ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
 */
@Test
public void test7() {
    Set<String> set = ZoneId.getAvailableZoneIds();
    set.forEach(System.out::println); // 获取全部的时区
    /*
        America/Los_Angeles
        SystemV/EST5EDT
        Pacific/Majuro
        America/Argentina/Buenos_Aires
        Europe/Nicosia
        Pacific/Guadalcanal
        Europe/Athens
        US/Pacific
        Europe/Monaco
        ... ...
        ... ...
    */
    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt); //2019-06-12T14:33:16.543+08:00[Asia/Shanghai]
}

下面是一篇很是不错的博客,建议读者仔细阅读

http://www.javashuo.com/article/p-cbjionju-be.html

这篇博客总共有20个知识点:

1 如何在java8中获取当天的日期

2 如何在java8中获取当前的年月日

3 在java8中如何获取某个特定的日期

4 在java8中检查两个日期是否相等

5 在java8中如何检查重复事件,好比生日

6 如何在java8中获取当前时间

7 如何增长时间里面的小时数

8 如何获取1周后的日期

9 一年先后的日期

10 在java8中使用时钟

11 在java中如何判断某个日期在另外一个日期的前面仍是后面

12 在java8中处理不一样的时区

13 如何表示固定的日期,好比信用卡过时时间

14 如何在java8中检查闰年

15 两个日期之间包含多少天,多少月

16 带时区的日期与时间

17 在java8中获取当前时间戳

18 如何在java8中使用预约义的格式器来对日期进行解析/格式化

19 如何在java中使用自定义的格式器来解析日期

20 如何在java8中对日期进行格式化,转换成字符串

这是一篇英语博客,内容很是不错,感兴趣的朋友能够阅读。

https://www.baeldung.com/java-8-date-time-intro

这篇博客的 ZonedDateTime时区相关讲解,感受不错。

7 重复注解和类型注解

若是对java的注解彻底不熟悉的朋友,能够参考下面的博客。

http://www.javashuo.com/article/p-hydssdxv-hg.html

这篇博客主要讲解的是java1.8注解新特性

http://www.javashuo.com/article/p-cxfiqzhm-ew.html

注解有几个容易被遗忘的地方:

①若是注解上面没有写@Target,那么默认@Target包含全部的属性,下面两个MyAnnotation是彻底同样的

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello world";
}

@Target( { ElementType.METHOD,ElementType.TYPE,ElementType.FIELD,ElementType.TYPE_USE
,ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE,ElementType.LOCAL_VARIABLE,
ElementType.PACKAGE,ElementType.PARAMETER,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello world";
}

②使用重复注解的时候,用getAnnotations只能获取 容器注解的类型(也是就@Repeatable中的类)

可重复注解:

@Target( { ElementType.METHOD,ElementType.TYPE,ElementType.FIELD,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
    String value() default "hello world";
}

容器注解类

@Target( { ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

使用注解的类

@MyAnnotation("value ... ...")
@MyAnnotation2
@MyAnnotation("personality design ... ...")
class AnnotationUse { }

主程序:

@Test
    public void test1() {
        Annotation[] annotations = AnnotationUse.class.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);
    }

输出:

@com.atguigu.anonotation.MyAnnotations(value=[@com.atguigu.anonotation.MyAnnotation(value=hello world), @com.atguigu.anonotation.MyAnnotation(value=personality design ... ...)])
@com.atguigu.anonotation.MyAnnotation2(value=MyAnnotation2 ... ...)

咱们能够看到,用getAnnotations并无拿到@MyAnnotation而是@MyAnnotations

参考资料

https://www.runoob.com/java/java8-lambda-expressions.html

http://blog.oneapm.com/apm-tech/226.html

http://www.javashuo.com/article/p-uocjsriy-kg.html

http://www.javashuo.com/article/p-bslaslln-gg.html

http://www.importnew.com/22060.html

https://segmentfault.com/a/1190000018936877?utm_source=tag-newest

http://www.javashuo.com/article/p-cbjionju-be.html

https://www.baeldung.com/java-8-date-time-intro

请尊重做者劳动成果,转载请注明出处。以上内容如有侵权,请联系做者。

相关文章
相关标签/搜索