[TOC]html
本篇博客主要介紹java8新特性,由於本身日常也使用到了一些java8的新特性,可是歷來沒有從頭至尾,真真正正的把幾乎全部經常使用的java8新特性研究一遍,此次藉助端午節3三天,好好把經常使用java8新特性梳理一下,畢竟java12立刻就要出來了。java
本篇博客不會重點介紹java8新特性的基礎內容,由於網上有不少優秀的文章已經介紹,好比像lambda表達式的用法,stream的特性,Optional的基礎使用等等。在下就不重複造輪子了,我會給出幾個我認爲很是優秀的文章,對java8還不熟悉的讀者,能夠先看這些基礎內容。本篇博客主要聚焦於java8新特性難點和易錯點。express
若是對lambda還不清楚的讀者,能夠先按順序閱讀下面兩篇文章segmentfault
https://www.runoob.com/java/java8-lambda-expressions.htmlapi
http://blog.oneapm.com/apm-tech/226.html數組
①lambda僅僅是一個語法糖,編譯成class文件後,就能夠發現,lambda變成了內部類網絡
②一個lambda表達式,當作是一個匿名對象app
③lambda表達式 所表明的的接口必須是一個函數式接口框架
④ 函數式接口:接口中只有一個抽象方法,就稱爲函數式接口。可使用@FunctionalInterface
確保該接口爲函數式接口,若是不當心寫錯,java編譯器會報錯。dom
<span style="color:red">注意:函數式接口能夠包含多個default方法和Object方法,如下程序編譯器不會報錯</span>
@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表達式。<span style="color:red">函數式接口的惟一做用,就是爲了lambda的使用</span>
⑥<span style="color:red">lambda重點在於參數和body,因此參數類型和body邏輯必定要正確,java編譯期會自動將lambda轉換成對象。</span>
@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)
java內置函數接口,不須要咱們本身寫函數接口。最經常使用的四大函數接口是,Consumer、Supplier、Function、Predicate全部內置函數接口均在 java.util.function
爲何java會內置如此多的函數式接口,咱們明明能夠本身寫函數式接口啊?
緣由:由於函數式接口很是簡單,一個接口聲明,加上一個抽象方法。既然如此,java的內置函數式接口就是業界規定,是一種規範。好比我想寫一個Myfunction函數式接口
public interface MyFunction<T> { void consume(T t); }
而別人又寫寧一個MyFunction二、MyFunction3,既然如此,那你們就都用java內置的函數式接口,<span style="color:red">約定很重要!!!</span>
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
核心:方法引用只是lambda表現的另外一種形式,它依舊仍是一個匿名內部對象
1 對象::實例方法 <span style="color:red;font-size:24px">該方法的參數類型和返回類型和函數式接口中的抽象方法必須一致</span>
// 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 類::靜態方法 <span style="color:red;font-size:24px">該方法的參數類型和返回類型和函數式接口中的抽象方法必須一致</span>
@Test public void test4(){ // 1 Comparator<Integer> comparator1 = (x,y) -> Integer.compare(x,y); // 2 Comparator<Integer> comparator2 = Integer::compare; }
3 類::實例方法 特殊 <span style="color:red;font-size:24px">該方法的參數類型和返回類型和函數式接口中的抽象方法確定不一致,不一致,不一致</span>
@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; } }
<span style="color:red">實際上TestUtil::println2,TestUtil::println3均可以當作是一個匿名對象,java編譯事後,class文件幾乎是同樣的。</span>
日常咱們仍是寫 () -> {} 這種形式的,若是存在方法引用或者構造器引用,idea會自動提示咱們,咱們再修改便可。可是最好仍是要給出詳細註釋,由於有的方法引用,一時半會看不明白。
<span style="color:red;font-size:24px">構造器參數類型必須和函數式接口中的抽象方法一致,返回類型默認就是該類</span>
@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; }
<span style="color:red;font-size:24px">構造器參數類型必須和函數式接口中的抽象方法一致,返回類型默認就是該數組</span>
@Test public void test7(){ // 1 Function<Integer,String[]> function1 = x -> new String[x]; // 2 Function<Integer,String[]> function2 = String[]::new; }
有一篇博客很是棒,推薦給你們
http://www.javashuo.com/article/p-uocjsriy-kg.html
集合講的是數據,流講的是計算!
注意:
①Stream 本身不會存儲元素。
②Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
③Stream 操做是延遲執行的。這意味着他們會等到須要結果的時候才執行。
一:建立 Stream
二:中間操做
三:終止操做(終端操做)
//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);
多箇中間操做能夠鏈接起來造成一個<span style="color:red">流水線</span>,除非流水 線上觸發終止操做,不然中間操做不會執行任何的處理!
而在終止操做時一次性所有處理,稱爲「惰性求值」。
一:篩選與切片
方法 | 描述 |
---|---|
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) | 接收一個函數做爲參數,將流中的每一個值都換成另 一個流,而後把全部流鏈接成一個流 |
<span style="color:red;font-size:24px">注意:map和flatMap的區別</span>
區別就是 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);
終端操做會從流的流水線生成結果。其結果能夠是任何不是流的值,例如: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<T> | 把流中元素收集到List |
toSet | Set<T> | 把流中元素收集到Set |
toCollection | Collection<T> | 把流中元素收集到建立的集合 |
counting | Long | 計算流中元素的個數 |
summingInt | Integer | 對流中元素的整數屬性求和 |
averagingInt | Double | 計算流中元素Integer屬性的平均值 |
summarizingInt | IntSummaryStatistics | 收集流中Integer屬性的統計值。好比:數量、總和、最小值、平均值、最大值 |
maxBy | Optional<T> | 根據比較器選擇最大值 |
minBy | Optional<T> | 根據比較器選擇最小值 |
groupingBy | Map<K,List<T>> | 根據某屬性值對流分組,屬性爲K,值爲List<T> |
partitioningBy | Map<Boolean,List<T>> | 根據true或false進行分區 |
joining | String | 鏈接流中每一個字符串 |
<span style="color:red;font-size:24px">注意:groupingBy能夠多級分組</span>
並行流就是把一個內容分紅多個數據塊,並用不一樣的線程分 別處理每一個數據塊的流。
Java 8 中將並行進行了優化,咱們能夠很容易的對數據進行行操做。Stream API 能夠聲明性地經過 parallel() 與 sequential() 在並行流與順序流之間進行切換。
Fork/Join 框架:就是在必要的狀況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 彙總。
Fork/Join 框架與傳統線程池的區別
採用 「工做竊取」模式(work-stealing): 當執行新的任務時它能夠將其拆分分紅更小的任務執行,並將小任務加到線程隊列中,而後再從一個隨機線程的隊列中偷一個並把它放在本身的隊列中。
相對於通常的線程池實現,fork/join框架的優點體如今對其中包含的任務的 處理方式上.在通常的線程池中,若是一個線程正在執行的任務因爲某些緣由 沒法繼續運行,那麼該線程會處於等待狀態.而在fork/join框架實現中,若是某個子問題因爲等待另一個子問題的完成而沒法繼續運行.那麼處理該子 問題的線程會主動尋找其餘還沒有運行的子問題來執行.這種方式減小了線程 的等待時間,提升了性能.
<span style="color:red;font-size:24px">在jdk1.8之前,fork/join比較複雜,代碼編寫比較難,而在jdk1.8中,利用stream中的parallel()能夠很是輕鬆的實現fork/join的效果。(parallel底層就是利用fork/join)</span>
由於要兼容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()
④接口中能夠存在靜態方法,和普通類的靜態方法調用方式如出一轍。
有兩篇很是不錯的文章,你們能夠參考一下,本人就不重複造輪子了。
http://www.javashuo.com/article/p-bslaslln-gg.html
http://www.importnew.com/22060.html
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能省去不少麻煩。
一:能夠參考博客,文章內容很是不錯,值得一看!
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對象。
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 }
ZonedDateTime:
/** * ZonedDateTime : 帶時區的時間,內部包裝了一個LocalDateTime */ @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
時區相關講解,感受不錯。
若是對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
<br />
參考資料:
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 <br />
<div style="font-size:18px;background-color:#CCFFFF"> 做者:<font color="#551A8B" size="5px">一杯熱咖啡AAA</font> <br /> 出處:<a href="https://www.cnblogs.com/AdaiCoffee/">https://www.cnblogs.com/AdaiCoffee/</a> <br /> 本文以學習、研究和分享爲主,歡迎轉載。若是文中有不妥或者錯誤的地方還望指出,以避免誤人子弟。若是你有更好的想法和意見,能夠留言討論,謝謝! </div>
原文出處:https://www.cnblogs.com/AdaiCoffee/p/11016446.html